add benchmark framework



git-svn-id: http://skia.googlecode.com/svn/trunk@52 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/bench/BenchTool/BenchTool.xcodeproj/project.pbxproj b/bench/BenchTool/BenchTool.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..7e8ad17
--- /dev/null
+++ b/bench/BenchTool/BenchTool.xcodeproj/project.pbxproj
@@ -0,0 +1,324 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 45;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		272FB43A0F11A19C00CA935D /* RectBench.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 272FB4390F11A19C00CA935D /* RectBench.cpp */; };
+		272FB4F30F11B40300CA935D /* SkBenchmark.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 272FB4F20F11B40300CA935D /* SkBenchmark.cpp */; };
+		27739F4D0F11439200F233EA /* libmaccore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27739F240F11404A00F233EA /* libmaccore.a */; };
+		27739F4E0F11439300F233EA /* libcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27739F1A0F11403B00F233EA /* libcore.a */; };
+		27739F520F1143C000F233EA /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27739F510F1143C000F233EA /* Carbon.framework */; };
+		8DD76F650486A84900D96B5E /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* main.cpp */; settings = {ATTRIBUTES = (); }; };
+		8DD76F6A0486A84900D96B5E /* BenchTool.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859E8B029090EE04C91782 /* BenchTool.1 */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		27739F190F11403B00F233EA /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 27739F120F11403B00F233EA /* core.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = D2AAC046055464E500DB518D;
+			remoteInfo = core;
+		};
+		27739F230F11404A00F233EA /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 27739F1C0F11404A00F233EA /* maccore.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = D2AAC046055464E500DB518D;
+			remoteInfo = maccore;
+		};
+		27739F3C0F11424800F233EA /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 27739F1C0F11404A00F233EA /* maccore.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = D2AAC045055464E500DB518D;
+			remoteInfo = maccore;
+		};
+		27739F3E0F11424C00F233EA /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 27739F120F11403B00F233EA /* core.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = D2AAC045055464E500DB518D;
+			remoteInfo = core;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		8DD76F690486A84900D96B5E /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 8;
+			dstPath = /usr/share/man/man1/;
+			dstSubfolderSpec = 0;
+			files = (
+				8DD76F6A0486A84900D96B5E /* BenchTool.1 in CopyFiles */,
+			);
+			runOnlyForDeploymentPostprocessing = 1;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		08FB7796FE84155DC02AAC07 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
+		272FB4390F11A19C00CA935D /* RectBench.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RectBench.cpp; path = ../RectBench.cpp; sourceTree = SOURCE_ROOT; };
+		272FB4F20F11B40300CA935D /* SkBenchmark.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBenchmark.cpp; path = ../SkBenchmark.cpp; sourceTree = SOURCE_ROOT; };
+		27739F120F11403B00F233EA /* core.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = core.xcodeproj; path = ../../xcode/core/core.xcodeproj; sourceTree = SOURCE_ROOT; };
+		27739F1C0F11404A00F233EA /* maccore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = maccore.xcodeproj; path = ../../xcode/maccore/maccore.xcodeproj; sourceTree = SOURCE_ROOT; };
+		27739F510F1143C000F233EA /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; };
+		8DD76F6C0486A84900D96B5E /* BenchTool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BenchTool; sourceTree = BUILT_PRODUCTS_DIR; };
+		C6859E8B029090EE04C91782 /* BenchTool.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = BenchTool.1; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		8DD76F660486A84900D96B5E /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				27739F4D0F11439200F233EA /* libmaccore.a in Frameworks */,
+				27739F4E0F11439300F233EA /* libcore.a in Frameworks */,
+				27739F520F1143C000F233EA /* Carbon.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		08FB7794FE84155DC02AAC07 /* BenchTool */ = {
+			isa = PBXGroup;
+			children = (
+				27739F510F1143C000F233EA /* Carbon.framework */,
+				27739F1C0F11404A00F233EA /* maccore.xcodeproj */,
+				27739F120F11403B00F233EA /* core.xcodeproj */,
+				08FB7795FE84155DC02AAC07 /* Source */,
+				C6859E8C029090F304C91782 /* Documentation */,
+				1AB674ADFE9D54B511CA2CBB /* Products */,
+			);
+			name = BenchTool;
+			sourceTree = "<group>";
+		};
+		08FB7795FE84155DC02AAC07 /* Source */ = {
+			isa = PBXGroup;
+			children = (
+				08FB7796FE84155DC02AAC07 /* main.cpp */,
+				272FB4F20F11B40300CA935D /* SkBenchmark.cpp */,
+				272FB4390F11A19C00CA935D /* RectBench.cpp */,
+			);
+			name = Source;
+			sourceTree = "<group>";
+		};
+		1AB674ADFE9D54B511CA2CBB /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				8DD76F6C0486A84900D96B5E /* BenchTool */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		27739F130F11403B00F233EA /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				27739F1A0F11403B00F233EA /* libcore.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		27739F1D0F11404A00F233EA /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				27739F240F11404A00F233EA /* libmaccore.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		C6859E8C029090F304C91782 /* Documentation */ = {
+			isa = PBXGroup;
+			children = (
+				C6859E8B029090EE04C91782 /* BenchTool.1 */,
+			);
+			name = Documentation;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		8DD76F620486A84900D96B5E /* BenchTool */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "BenchTool" */;
+			buildPhases = (
+				8DD76F640486A84900D96B5E /* Sources */,
+				8DD76F660486A84900D96B5E /* Frameworks */,
+				8DD76F690486A84900D96B5E /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				27739F3D0F11424800F233EA /* PBXTargetDependency */,
+				27739F3F0F11424C00F233EA /* PBXTargetDependency */,
+			);
+			name = BenchTool;
+			productInstallPath = "$(HOME)/bin";
+			productName = BenchTool;
+			productReference = 8DD76F6C0486A84900D96B5E /* BenchTool */;
+			productType = "com.apple.product-type.tool";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		08FB7793FE84155DC02AAC07 /* Project object */ = {
+			isa = PBXProject;
+			buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "BenchTool" */;
+			compatibilityVersion = "Xcode 3.1";
+			hasScannedForEncodings = 1;
+			mainGroup = 08FB7794FE84155DC02AAC07 /* BenchTool */;
+			projectDirPath = "";
+			projectReferences = (
+				{
+					ProductGroup = 27739F130F11403B00F233EA /* Products */;
+					ProjectRef = 27739F120F11403B00F233EA /* core.xcodeproj */;
+				},
+				{
+					ProductGroup = 27739F1D0F11404A00F233EA /* Products */;
+					ProjectRef = 27739F1C0F11404A00F233EA /* maccore.xcodeproj */;
+				},
+			);
+			projectRoot = "";
+			targets = (
+				8DD76F620486A84900D96B5E /* BenchTool */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXReferenceProxy section */
+		27739F1A0F11403B00F233EA /* libcore.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = libcore.a;
+			remoteRef = 27739F190F11403B00F233EA /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		27739F240F11404A00F233EA /* libmaccore.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = libmaccore.a;
+			remoteRef = 27739F230F11404A00F233EA /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+/* End PBXReferenceProxy section */
+
+/* Begin PBXSourcesBuildPhase section */
+		8DD76F640486A84900D96B5E /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				8DD76F650486A84900D96B5E /* main.cpp in Sources */,
+				272FB43A0F11A19C00CA935D /* RectBench.cpp in Sources */,
+				272FB4F30F11B40300CA935D /* SkBenchmark.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		27739F3D0F11424800F233EA /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = maccore;
+			targetProxy = 27739F3C0F11424800F233EA /* PBXContainerItemProxy */;
+		};
+		27739F3F0F11424C00F233EA /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = core;
+			targetProxy = 27739F3E0F11424C00F233EA /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+		1DEB923208733DC60010E9CD /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				COPY_PHASE_STRIP = NO;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_ENABLE_FIX_AND_CONTINUE = YES;
+				GCC_MODEL_TUNING = G5;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"_GLIBCXX_DEBUG=1",
+					"_GLIBCXX_DEBUG_PEDANTIC=1",
+				);
+				INSTALL_PATH = /usr/local/bin;
+				PRODUCT_NAME = BenchTool;
+			};
+			name = Debug;
+		};
+		1DEB923308733DC60010E9CD /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				GCC_MODEL_TUNING = G5;
+				INSTALL_PATH = /usr/local/bin;
+				PRODUCT_NAME = BenchTool;
+			};
+			name = Release;
+		};
+		1DEB923608733DC60010E9CD /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_ENABLE_CPP_EXCEPTIONS = NO;
+				GCC_ENABLE_CPP_RTTI = NO;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_THREADSAFE_STATICS = NO;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				PREBINDING = NO;
+				SDKROOT = macosx10.5;
+				USER_HEADER_SEARCH_PATHS = ".. ../../include/**";
+			};
+			name = Debug;
+		};
+		1DEB923708733DC60010E9CD /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_ENABLE_CPP_EXCEPTIONS = NO;
+				GCC_ENABLE_CPP_RTTI = NO;
+				GCC_THREADSAFE_STATICS = NO;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				PREBINDING = NO;
+				SDKROOT = macosx10.5;
+				USER_HEADER_SEARCH_PATHS = ".. ../../include/**";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "BenchTool" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1DEB923208733DC60010E9CD /* Debug */,
+				1DEB923308733DC60010E9CD /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "BenchTool" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1DEB923608733DC60010E9CD /* Debug */,
+				1DEB923708733DC60010E9CD /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
+}
diff --git a/bench/BenchTool/main.cpp b/bench/BenchTool/main.cpp
new file mode 100644
index 0000000..ae0dc58
--- /dev/null
+++ b/bench/BenchTool/main.cpp
@@ -0,0 +1,70 @@
+//#include <iostream>
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkString.h"
+
+#include "SkBenchmark.h"
+
+typedef SkTRegistry<SkBenchmark> BenchRegistry;
+
+class Iter {
+public:
+    Iter() {
+        fBench = BenchRegistry::Head();
+    }
+    
+    SkBenchmark* next() {
+        if (fBench) {
+            BenchRegistry::Factory f = fBench->factory();
+            fBench = fBench->next();
+            return f();
+        }
+        return NULL;
+    }
+    
+private:
+    const BenchRegistry* fBench;
+};
+
+static void make_filename(const char name[], SkString* path) {
+    path->set(name);
+    for (int i = 0; name[i]; i++) {
+        switch (name[i]) {
+            case '/':
+            case '\\':
+            case ' ':
+            case ':':
+                path->writable_str()[i] = '-';
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+int main (int argc, char * const argv[]) {
+    const int w = 640;
+    const int h = 480;
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    bm.allocPixels();
+    
+    SkCanvas canvas(bm);
+    
+    Iter iter;
+    SkBenchmark* bench;
+    while ((bench = iter.next()) != NULL) {
+        canvas.drawColor(SK_ColorWHITE);
+        bench->draw(&canvas);
+        
+        SkString str;
+        make_filename(bench->getName(), &str);
+        str.prepend("/skimages/");
+        str.append(".png");
+        ::remove(str.c_str());
+        SkImageEncoder::EncodeFile(str.c_str(), bm, SkImageEncoder::kPNG_Type);
+    }
+    
+    return 0;
+}
diff --git a/bench/RectBench.cpp b/bench/RectBench.cpp
new file mode 100644
index 0000000..081e242
--- /dev/null
+++ b/bench/RectBench.cpp
@@ -0,0 +1,113 @@
+#include "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+
+class RectBench : public SkBenchmark {
+public:
+    enum {
+        W = 640,
+        H = 480,
+        N = 100
+    };
+    SkRect  fRects[N];
+    SkColor fColors[N];
+
+    RectBench() {
+        SkRandom rand;
+        for (int i = 0; i < N; i++) {
+            int x = rand.nextU() % W;
+            int y = rand.nextU() % H;
+            int w = rand.nextU() % W;
+            int h = rand.nextU() % H;
+            w >>= 1;
+            h >>= 1;
+            x -= w/2;
+            y -= h/2;
+            fRects[i].set(SkIntToScalar(x), SkIntToScalar(y),
+                          SkIntToScalar(x+w), SkIntToScalar(y+h));
+            fColors[i] = rand.nextU() | 0xFF808080;
+        }
+    }
+        
+protected:
+    virtual void drawThisRect(SkCanvas* c, const SkRect& r, const SkPaint& p) {
+        c->drawRect(r, p);
+    }
+
+    virtual const char* onGetName() { return "rectangles"; }
+    virtual SkIPoint onGetSize() { return SkMakeIPoint(640, 480); }
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        for (int i = 0; i < N; i++) {
+            paint.setColor(fColors[i]);
+            this->drawThisRect(canvas, fRects[i], paint);
+        }
+    }
+};
+
+class OvalBench : public RectBench {
+protected:
+    virtual void drawThisRect(SkCanvas* c, const SkRect& r, const SkPaint& p) {
+        c->drawOval(r, p);
+    }
+    virtual const char* onGetName() { return "ovals"; }
+};
+
+class RRectBench : public RectBench {
+protected:
+    virtual void drawThisRect(SkCanvas* c, const SkRect& r, const SkPaint& p) {
+        c->drawRoundRect(r, r.width() / 4, r.height() / 4, p);
+    }
+    virtual const char* onGetName() { return "roundrects"; }
+};
+
+class PointsBench : public RectBench {
+public:
+    SkCanvas::PointMode fMode;
+    const char* fName;
+
+    PointsBench(SkCanvas::PointMode mode, const char* name) : fMode(mode) {
+        fName = name;
+    }
+
+protected:
+    virtual void onDraw(SkCanvas* canvas) {
+        static const SkScalar gSizes[] = {
+            SkIntToScalar(7), 0
+        };
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStrokeCap(SkPaint::kRound_Cap);
+        
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gSizes); i++) {
+            paint.setStrokeWidth(gSizes[i]);
+            canvas->drawPoints(fMode, N * 2,
+                               reinterpret_cast<const SkPoint*>(fRects), paint);
+            paint.setColor(fColors[i]);
+        }
+    }
+    virtual const char* onGetName() { return fName; }
+};
+
+static SkBenchmark* RectFactory() { return SkNEW(RectBench); }
+static SkBenchmark* OvalFactory() { return SkNEW(OvalBench); }
+static SkBenchmark* RRectFactory() { return SkNEW(RRectBench); }
+static SkBenchmark* PointsFactory() {
+    return SkNEW_ARGS(PointsBench, (SkCanvas::kPoints_PointMode, "points"));
+}
+static SkBenchmark* LinesFactory() {
+    return SkNEW_ARGS(PointsBench, (SkCanvas::kLines_PointMode, "lines"));
+}
+static SkBenchmark* PolygonFactory() {
+    return SkNEW_ARGS(PointsBench, (SkCanvas::kPolygon_PointMode, "polygon"));
+}
+
+static SkTRegistry<SkBenchmark> gRectReg(RectFactory);
+static SkTRegistry<SkBenchmark> gOvalReg(OvalFactory);
+static SkTRegistry<SkBenchmark> gRRectReg(RRectFactory);
+static SkTRegistry<SkBenchmark> gPointsReg(PointsFactory);
+static SkTRegistry<SkBenchmark> gLinesReg(LinesFactory);
+static SkTRegistry<SkBenchmark> gPolygonReg(PolygonFactory);
+
diff --git a/bench/SkBenchmark.cpp b/bench/SkBenchmark.cpp
new file mode 100644
index 0000000..ebeea9d
--- /dev/null
+++ b/bench/SkBenchmark.cpp
@@ -0,0 +1,14 @@
+#include "SkBenchmark.h"
+
+const char* SkBenchmark::getName() {
+    return this->onGetName();
+}
+
+SkIPoint SkBenchmark::getSize() {
+    return this->onGetSize();
+}
+
+void SkBenchmark::draw(SkCanvas* canvas) {
+    this->onDraw(canvas);
+}
+
diff --git a/bench/SkBenchmark.h b/bench/SkBenchmark.h
new file mode 100644
index 0000000..6256029
--- /dev/null
+++ b/bench/SkBenchmark.h
@@ -0,0 +1,54 @@
+#ifndef SkBenchmark_DEFINED
+#define SkBenchmark_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkPoint.h"
+
+class SkCanvas;
+
+class SkBenchmark : public SkRefCnt {
+public:
+    const char* getName();
+    SkIPoint getSize();
+    void draw(SkCanvas*);
+
+protected:
+    virtual const char* onGetName() = 0;
+    virtual SkIPoint onGetSize() = 0;
+    virtual void onDraw(SkCanvas*) = 0;
+};
+
+static inline SkIPoint SkMakeIPoint(int x, int y) {
+    SkIPoint p;
+    p.set(x, y);
+    return p;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> class SkTRegistry : SkNoncopyable {
+public:
+    typedef T* (*Factory)();
+    
+    SkTRegistry(Factory fact) {
+        fFact = fact;
+        fChain = gHead;
+        gHead = this;
+    }
+    
+    static const SkTRegistry* Head() { return gHead; }
+    
+    SkTRegistry* next() const { return fChain; }
+    Factory factory() const { return fFact; }
+    
+private:
+    Factory   fFact;
+    SkTRegistry* fChain;
+    
+    static SkTRegistry* gHead;
+};
+
+template <typename T> SkTRegistry<T>* SkTRegistry<T>::gHead;
+
+#endif
+
diff --git a/xcode/maccore/maccore.xcodeproj/project.pbxproj b/xcode/maccore/maccore.xcodeproj/project.pbxproj
index 6bb123b..30bf768 100644
--- a/xcode/maccore/maccore.xcodeproj/project.pbxproj
+++ b/xcode/maccore/maccore.xcodeproj/project.pbxproj
@@ -12,6 +12,9 @@
 		002884A70EFAB5DE0083E387 /* SkTime_Unix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 002884A40EFAB5DE0083E387 /* SkTime_Unix.cpp */; };
 		002884E10EFABFFC0083E387 /* SkGlobals_global.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 002884E00EFABFFC0083E387 /* SkGlobals_global.cpp */; };
 		007A7BEF0F01427100A2D6EE /* SkFontHost_mac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7BEE0F01427100A2D6EE /* SkFontHost_mac.cpp */; };
+		27739F2B0F11407000F233EA /* SkCreateCGImageRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27739F2A0F11407000F233EA /* SkCreateCGImageRef.cpp */; };
+		27739F2D0F11408100F233EA /* SkImageDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27739F2C0F11408100F233EA /* SkImageDecoder.cpp */; };
+		27739F2F0F11409100F233EA /* SkImageDecoder_CG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27739F2E0F11409100F233EA /* SkImageDecoder_CG.cpp */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
@@ -20,6 +23,9 @@
 		002884A40EFAB5DE0083E387 /* SkTime_Unix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkTime_Unix.cpp; path = ../../src/ports/SkTime_Unix.cpp; sourceTree = SOURCE_ROOT; };
 		002884E00EFABFFC0083E387 /* SkGlobals_global.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkGlobals_global.cpp; path = ../../src/ports/SkGlobals_global.cpp; sourceTree = SOURCE_ROOT; };
 		007A7BEE0F01427100A2D6EE /* SkFontHost_mac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkFontHost_mac.cpp; path = ../../src/ports/SkFontHost_mac.cpp; sourceTree = SOURCE_ROOT; };
+		27739F2A0F11407000F233EA /* SkCreateCGImageRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkCreateCGImageRef.cpp; path = ../../src/utils/mac/SkCreateCGImageRef.cpp; sourceTree = SOURCE_ROOT; };
+		27739F2C0F11408100F233EA /* SkImageDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkImageDecoder.cpp; path = ../../src/images/SkImageDecoder.cpp; sourceTree = SOURCE_ROOT; };
+		27739F2E0F11409100F233EA /* SkImageDecoder_CG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkImageDecoder_CG.cpp; path = ../../src/ports/SkImageDecoder_CG.cpp; sourceTree = SOURCE_ROOT; };
 		D2AAC046055464E500DB518D /* libmaccore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libmaccore.a; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
 
@@ -37,6 +43,7 @@
 		08FB7794FE84155DC02AAC07 /* maccore */ = {
 			isa = PBXGroup;
 			children = (
+				27739F280F11405800F233EA /* Images */,
 				08FB7795FE84155DC02AAC07 /* Source */,
 				C6A0FF2B0290797F04C91782 /* Documentation */,
 				1AB674ADFE9D54B511CA2CBB /* Products */,
@@ -64,6 +71,16 @@
 			name = Products;
 			sourceTree = "<group>";
 		};
+		27739F280F11405800F233EA /* Images */ = {
+			isa = PBXGroup;
+			children = (
+				27739F2E0F11409100F233EA /* SkImageDecoder_CG.cpp */,
+				27739F2C0F11408100F233EA /* SkImageDecoder.cpp */,
+				27739F2A0F11407000F233EA /* SkCreateCGImageRef.cpp */,
+			);
+			name = Images;
+			sourceTree = "<group>";
+		};
 		C6A0FF2B0290797F04C91782 /* Documentation */ = {
 			isa = PBXGroup;
 			children = (
@@ -128,6 +145,9 @@
 				002884A70EFAB5DE0083E387 /* SkTime_Unix.cpp in Sources */,
 				002884E10EFABFFC0083E387 /* SkGlobals_global.cpp in Sources */,
 				007A7BEF0F01427100A2D6EE /* SkFontHost_mac.cpp in Sources */,
+				27739F2B0F11407000F233EA /* SkCreateCGImageRef.cpp in Sources */,
+				27739F2D0F11408100F233EA /* SkImageDecoder.cpp in Sources */,
+				27739F2F0F11409100F233EA /* SkImageDecoder_CG.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -172,7 +192,7 @@
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				PREBINDING = NO;
 				SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
-				USER_HEADER_SEARCH_PATHS = "../../include/utils/mac ../../include/ports ../../include/core";
+				USER_HEADER_SEARCH_PATHS = "../../include/images ../../include/utils/mac ../../include/ports ../../include/core";
 			};
 			name = Debug;
 		};
@@ -194,7 +214,7 @@
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				PREBINDING = NO;
 				SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
-				USER_HEADER_SEARCH_PATHS = "../../include/utils/mac ../../include/ports ../../include/core";
+				USER_HEADER_SEARCH_PATHS = "../../include/images ../../include/utils/mac ../../include/ports ../../include/core";
 			};
 			name = Release;
 		};
diff --git a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
index f37421b..f288c87 100644
--- a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
+++ b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
@@ -18,7 +18,6 @@
 		00003C7A0EFC22CE000FF73A /* SkView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C710EFC22CE000FF73A /* SkView.cpp */; };
 		00003C7B0EFC22CE000FF73A /* SkViewPriv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C720EFC22CE000FF73A /* SkViewPriv.cpp */; };
 		00003C7C0EFC22CE000FF73A /* SkWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C730EFC22CE000FF73A /* SkWindow.cpp */; };
-		00003C800EFC22E1000FF73A /* SkCreateCGImageRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C7D0EFC22E1000FF73A /* SkCreateCGImageRef.cpp */; };
 		00003C810EFC22E1000FF73A /* skia_mac.cp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C7E0EFC22E1000FF73A /* skia_mac.cp */; };
 		00003C820EFC22E1000FF73A /* SkOSWindow_Mac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C7F0EFC22E1000FF73A /* SkOSWindow_Mac.cpp */; };
 		00003C950EFC2316000FF73A /* libeffects.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00003C940EFC230E000FF73A /* libeffects.a */; };
@@ -30,7 +29,6 @@
 		0028847B0EFAB46A0083E387 /* libcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 002884510EFAA35C0083E387 /* libcore.a */; };
 		002884BD0EFAB6A30083E387 /* libmaccore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 002884BC0EFAB69F0083E387 /* libmaccore.a */; };
 		0041CDDB0F00975E00695E8C /* SampleImageDir.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CDDA0F00975E00695E8C /* SampleImageDir.cpp */; };
-		0041CDF00F009EB000695E8C /* SkImageDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CDEF0F009EB000695E8C /* SkImageDecoder.cpp */; };
 		0041CDF30F009ED100695E8C /* SkImageRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CDF20F009ED100695E8C /* SkImageRef.cpp */; };
 		0041CDF60F009EED00695E8C /* SkImageRef_GlobalPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CDF50F009EED00695E8C /* SkImageRef_GlobalPool.cpp */; };
 		0041CDFA0F009F0700695E8C /* SkImageRefPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CDF90F009F0700695E8C /* SkImageRefPool.cpp */; };
@@ -53,7 +51,6 @@
 		0041CE470F00A12400695E8C /* SampleNinePatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE310F00A12400695E8C /* SampleNinePatch.cpp */; };
 		0041CE480F00A12400695E8C /* SampleOverflow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE320F00A12400695E8C /* SampleOverflow.cpp */; };
 		0041CE4A0F00A12400695E8C /* SamplePatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE340F00A12400695E8C /* SamplePatch.cpp */; };
-		007A7BE40F01424500A2D6EE /* SkImageDecoder_CG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7BE30F01424500A2D6EE /* SkImageDecoder_CG.cpp */; };
 		007A7CB30F01658C00A2D6EE /* SamplePicture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CA40F01658C00A2D6EE /* SamplePicture.cpp */; };
 		007A7CB40F01658C00A2D6EE /* SamplePoints.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CA50F01658C00A2D6EE /* SamplePoints.cpp */; };
 		007A7CB60F01658C00A2D6EE /* SampleRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CA70F01658C00A2D6EE /* SampleRegion.cpp */; };
@@ -132,7 +129,6 @@
 		00003C710EFC22CE000FF73A /* SkView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkView.cpp; path = ../../src/views/SkView.cpp; sourceTree = SOURCE_ROOT; };
 		00003C720EFC22CE000FF73A /* SkViewPriv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkViewPriv.cpp; path = ../../src/views/SkViewPriv.cpp; sourceTree = SOURCE_ROOT; };
 		00003C730EFC22CE000FF73A /* SkWindow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkWindow.cpp; path = ../../src/views/SkWindow.cpp; sourceTree = SOURCE_ROOT; };
-		00003C7D0EFC22E1000FF73A /* SkCreateCGImageRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkCreateCGImageRef.cpp; path = ../../src/utils/mac/SkCreateCGImageRef.cpp; sourceTree = SOURCE_ROOT; };
 		00003C7E0EFC22E1000FF73A /* skia_mac.cp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = skia_mac.cp; path = ../../src/utils/mac/skia_mac.cp; sourceTree = SOURCE_ROOT; };
 		00003C7F0EFC22E1000FF73A /* SkOSWindow_Mac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkOSWindow_Mac.cpp; path = ../../src/utils/mac/SkOSWindow_Mac.cpp; sourceTree = SOURCE_ROOT; };
 		00003C8C0EFC230E000FF73A /* effects.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = effects.xcodeproj; path = ../effects/effects.xcodeproj; sourceTree = SOURCE_ROOT; };
@@ -144,7 +140,6 @@
 		002884490EFAA35C0083E387 /* core.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = core.xcodeproj; path = ../core/core.xcodeproj; sourceTree = SOURCE_ROOT; };
 		002884B40EFAB69F0083E387 /* maccore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = maccore.xcodeproj; path = ../maccore/maccore.xcodeproj; sourceTree = SOURCE_ROOT; };
 		0041CDDA0F00975E00695E8C /* SampleImageDir.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleImageDir.cpp; path = ../../samplecode/SampleImageDir.cpp; sourceTree = SOURCE_ROOT; };
-		0041CDEF0F009EB000695E8C /* SkImageDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkImageDecoder.cpp; path = ../../src/images/SkImageDecoder.cpp; sourceTree = SOURCE_ROOT; };
 		0041CDF20F009ED100695E8C /* SkImageRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkImageRef.cpp; path = ../../src/images/SkImageRef.cpp; sourceTree = SOURCE_ROOT; };
 		0041CDF50F009EED00695E8C /* SkImageRef_GlobalPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkImageRef_GlobalPool.cpp; path = ../../src/images/SkImageRef_GlobalPool.cpp; sourceTree = SOURCE_ROOT; };
 		0041CDF90F009F0700695E8C /* SkImageRefPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkImageRefPool.cpp; path = ../../src/images/SkImageRefPool.cpp; sourceTree = SOURCE_ROOT; };
@@ -168,7 +163,6 @@
 		0041CE310F00A12400695E8C /* SampleNinePatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleNinePatch.cpp; path = ../../samplecode/SampleNinePatch.cpp; sourceTree = SOURCE_ROOT; };
 		0041CE320F00A12400695E8C /* SampleOverflow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleOverflow.cpp; path = ../../samplecode/SampleOverflow.cpp; sourceTree = SOURCE_ROOT; };
 		0041CE340F00A12400695E8C /* SamplePatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SamplePatch.cpp; path = ../../samplecode/SamplePatch.cpp; sourceTree = SOURCE_ROOT; };
-		007A7BE30F01424500A2D6EE /* SkImageDecoder_CG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkImageDecoder_CG.cpp; path = ../../src/ports/SkImageDecoder_CG.cpp; sourceTree = SOURCE_ROOT; };
 		007A7CA40F01658C00A2D6EE /* SamplePicture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SamplePicture.cpp; path = ../../samplecode/SamplePicture.cpp; sourceTree = SOURCE_ROOT; };
 		007A7CA50F01658C00A2D6EE /* SamplePoints.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SamplePoints.cpp; path = ../../samplecode/SamplePoints.cpp; sourceTree = SOURCE_ROOT; };
 		007A7CA70F01658C00A2D6EE /* SampleRegion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleRegion.cpp; path = ../../samplecode/SampleRegion.cpp; sourceTree = SOURCE_ROOT; };
@@ -264,7 +258,6 @@
 				00003C9B0EFC233F000FF73A /* SkParse.cpp */,
 				00003C9C0EFC233F000FF73A /* SkParseColor.cpp */,
 				00003C9D0EFC233F000FF73A /* SkXMLParser.cpp */,
-				00003C7D0EFC22E1000FF73A /* SkCreateCGImageRef.cpp */,
 				00003C7E0EFC22E1000FF73A /* skia_mac.cp */,
 				00003C7F0EFC22E1000FF73A /* SkOSWindow_Mac.cpp */,
 				00003C6B0EFC22CE000FF73A /* SkEvent.cpp */,
@@ -317,8 +310,6 @@
 				0041CDF90F009F0700695E8C /* SkImageRefPool.cpp */,
 				0041CDF50F009EED00695E8C /* SkImageRef_GlobalPool.cpp */,
 				0041CDF20F009ED100695E8C /* SkImageRef.cpp */,
-				0041CDEF0F009EB000695E8C /* SkImageDecoder.cpp */,
-				007A7BE30F01424500A2D6EE /* SkImageDecoder_CG.cpp */,
 				00003C6A0EFC22AD000FF73A /* views */,
 				00003C610EFC2287000FF73A /* samples */,
 				00003CA30EFC235F000FF73A /* SkXMLParser_empty.cpp */,
@@ -470,7 +461,6 @@
 				00003C7A0EFC22CE000FF73A /* SkView.cpp in Sources */,
 				00003C7B0EFC22CE000FF73A /* SkViewPriv.cpp in Sources */,
 				00003C7C0EFC22CE000FF73A /* SkWindow.cpp in Sources */,
-				00003C800EFC22E1000FF73A /* SkCreateCGImageRef.cpp in Sources */,
 				00003C810EFC22E1000FF73A /* skia_mac.cp in Sources */,
 				00003C820EFC22E1000FF73A /* SkOSWindow_Mac.cpp in Sources */,
 				00003C9E0EFC233F000FF73A /* SkDOM.cpp in Sources */,
@@ -480,7 +470,6 @@
 				00003CA40EFC235F000FF73A /* SkXMLParser_empty.cpp in Sources */,
 				00A41E4B0EFC312F00C9CBEB /* SampleArc.cpp in Sources */,
 				0041CDDB0F00975E00695E8C /* SampleImageDir.cpp in Sources */,
-				0041CDF00F009EB000695E8C /* SkImageDecoder.cpp in Sources */,
 				0041CDF30F009ED100695E8C /* SkImageRef.cpp in Sources */,
 				0041CDF60F009EED00695E8C /* SkImageRef_GlobalPool.cpp in Sources */,
 				0041CDFA0F009F0700695E8C /* SkImageRefPool.cpp in Sources */,
@@ -502,10 +491,8 @@
 				0041CE480F00A12400695E8C /* SampleOverflow.cpp in Sources */,
 				0041CE4A0F00A12400695E8C /* SamplePatch.cpp in Sources */,
 				0041CE470F00A12400695E8C /* SampleNinePatch.cpp in Sources */,
-				007A7BE40F01424500A2D6EE /* SkImageDecoder_CG.cpp in Sources */,
 				007A7CB30F01658C00A2D6EE /* SamplePicture.cpp in Sources */,
 				007A7CB40F01658C00A2D6EE /* SamplePoints.cpp in Sources */,
-				007A7CB60F01658C00A2D6EE /* SampleRegion.cpp in Sources */,
 				007A7CB70F01658C00A2D6EE /* SampleShaders.cpp in Sources */,
 				007A7CB80F01658C00A2D6EE /* SampleStrokeText.cpp in Sources */,
 				007A7CBA0F01658C00A2D6EE /* SampleText.cpp in Sources */,
@@ -517,6 +504,7 @@
 				007A7CC00F01658C00A2D6EE /* SampleVertices.cpp in Sources */,
 				007A7CC10F01658C00A2D6EE /* SampleXfermodes.cpp in Sources */,
 				0041CE3C0F00A12400695E8C /* SampleEncode.cpp in Sources */,
+				007A7CB60F01658C00A2D6EE /* SampleRegion.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};