add SkSize.h
add golden-master (gm) test app
diff --git a/Android.mk b/Android.mk
index 70fd81b..de09fd4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,3 +1,4 @@
+BASE_PATH := $(call my-dir)
 LOCAL_PATH:= $(call my-dir)
 
 #############################################################
@@ -253,7 +254,12 @@
 include $(BUILD_SHARED_LIBRARY)
 
 #############################################################
-# Build the skia benchmark too
+# Build the skia tools
 #
 
-include $(LOCAL_PATH)/bench/Android.mk
+# benchmark (timings)
+include $(BASE_PATH)/bench/Android.mk
+
+# golden-master (fidelity / regression test)
+include $(BASE_PATH)/gm/Android.mk
+
diff --git a/gm/Android.mk b/gm/Android.mk
new file mode 100644
index 0000000..6ce1dae
--- /dev/null
+++ b/gm/Android.mk
@@ -0,0 +1,31 @@
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	filltypes.cpp \
+    gradients.cpp \
+	tilemodes.cpp \
+	bitmapfilters.cpp \
+	xfermodes.cpp \
+	gmmain.cpp
+
+# additional optional class for this tool
+LOCAL_SRC_FILES += \
+	../src/utils/SkUnitMappers.cpp
+
+LOCAL_SHARED_LIBRARIES := libcutils libskia
+LOCAL_C_INCLUDES := \
+    external/skia/include/config \
+    external/skia/include/core \
+    external/skia/include/images \
+    external/skia/include/utils \
+    external/skia/include/effects
+
+#LOCAL_CFLAGS := 
+
+LOCAL_MODULE := skia_gm
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/gm/bitmapfilters.cpp b/gm/bitmapfilters.cpp
new file mode 100644
index 0000000..0487fe6
--- /dev/null
+++ b/gm/bitmapfilters.cpp
@@ -0,0 +1,116 @@
+#include "gm.h"
+
+namespace skiagm {
+
+static void make_bm(SkBitmap* bm) {
+    const SkColor colors[] = {
+        SK_ColorRED, SK_ColorGREEN,
+        SK_ColorBLUE, SK_ColorWHITE
+    };
+    SkColorTable* ctable = new SkColorTable(colors, 4);
+    bm->setConfig(SkBitmap::kIndex8_Config, 2, 2);
+    bm->allocPixels(ctable);
+    ctable->unref();
+    
+    *bm->getAddr8(0, 0) = 0;
+    *bm->getAddr8(1, 0) = 1;
+    *bm->getAddr8(0, 1) = 2;
+    *bm->getAddr8(1, 1) = 3;
+}
+
+static SkScalar draw_bm(SkCanvas* canvas, const SkBitmap& bm,
+                        SkScalar x, SkScalar y, SkPaint* paint) {
+    canvas->drawBitmap(bm, x, y, paint);
+    return SkIntToScalar(bm.width()) * 5/4;
+}
+
+static SkScalar draw_set(SkCanvas* c, const SkBitmap& bm, SkScalar x,
+                         SkPaint* p) {
+    x += draw_bm(c, bm, x, 0, p);
+    p->setFilterBitmap(true);
+    x += draw_bm(c, bm, x, 0, p);
+    p->setDither(true);
+    return x + draw_bm(c, bm, x, 0, p);
+}
+
+static const char* gConfigNames[] = {
+    "unknown config",
+    "A1",
+    "A8",
+    "Index8",
+    "565",
+    "4444",
+    "8888"
+};
+
+static SkScalar draw_row(SkCanvas* canvas, const SkBitmap& bm) {
+    SkAutoCanvasRestore acr(canvas, true);
+
+    SkPaint paint;
+    SkScalar x = 0;
+    const int scale = 32;
+
+    paint.setAntiAlias(true);
+    const char* name = gConfigNames[bm.config()];
+    canvas->drawText(name, strlen(name), x, SkIntToScalar(bm.height())*scale*5/8,
+                     paint);
+    canvas->translate(SkIntToScalar(48), 0);
+
+    canvas->scale(SkIntToScalar(scale), SkIntToScalar(scale));
+    
+    x += draw_set(canvas, bm, 0, &paint);
+    paint.reset();
+    paint.setAlpha(0x80);
+    draw_set(canvas, bm, x, &paint);
+    return x * scale / 3;
+}
+
+class FilterGM : public GM {
+public:
+    SkBitmap    fBM8, fBM4444, fBM16, fBM32;
+
+	FilterGM() {
+        make_bm(&fBM8);
+        fBM8.copyTo(&fBM4444, SkBitmap::kARGB_4444_Config);
+        fBM8.copyTo(&fBM16, SkBitmap::kRGB_565_Config);
+        fBM8.copyTo(&fBM32, SkBitmap::kARGB_8888_Config);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("bitmapfilters");
+    }
+
+	virtual SkISize onISize() {
+        return make_isize(540, 330);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+
+        SkScalar x = SkIntToScalar(10);
+        SkScalar y = SkIntToScalar(10);
+        
+        canvas->translate(x, y);
+        y = draw_row(canvas, fBM8);
+        canvas->translate(0, y);
+        y = draw_row(canvas, fBM4444);
+        canvas->translate(0, y);
+        y = draw_row(canvas, fBM16);
+        canvas->translate(0, y);
+        draw_row(canvas, fBM32);
+    }
+    
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new FilterGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
+
+
diff --git a/gm/filltypes.cpp b/gm/filltypes.cpp
new file mode 100644
index 0000000..e723f16
--- /dev/null
+++ b/gm/filltypes.cpp
@@ -0,0 +1,83 @@
+#include "gm.h"
+
+namespace skiagm {
+
+class FillTypeGM : public GM {
+    SkPath fPath;
+public:
+	FillTypeGM() {
+        const SkScalar radius = SkIntToScalar(45);
+        fPath.addCircle(SkIntToScalar(50), SkIntToScalar(50), radius);
+        fPath.addCircle(SkIntToScalar(100), SkIntToScalar(100), radius);
+    }
+    
+protected:
+    virtual SkString onShortName() {
+        return SkString("filltypes");
+    }
+
+	virtual SkISize onISize() {
+        return make_isize(835, 840);
+    }
+
+    void showPath(SkCanvas* canvas, int x, int y, SkPath::FillType ft,
+                  SkScalar scale, const SkPaint& paint) {
+
+        const SkRect r = { 0, 0, SkIntToScalar(150), SkIntToScalar(150) };
+
+        canvas->save();
+        canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
+        canvas->clipRect(r);
+        canvas->drawColor(SK_ColorWHITE);
+        fPath.setFillType(ft);
+        canvas->translate(r.centerX(), r.centerY());
+        canvas->scale(scale, scale);
+        canvas->translate(-r.centerX(), -r.centerY());
+        canvas->drawPath(fPath, paint);
+        canvas->restore();
+    }
+
+    void showFour(SkCanvas* canvas, SkScalar scale, const SkPaint& paint) {
+        showPath(canvas,   0,   0, SkPath::kWinding_FillType,
+                 scale, paint);
+        showPath(canvas, 200,   0, SkPath::kEvenOdd_FillType,
+                 scale, paint);
+        showPath(canvas,  00, 200, SkPath::kInverseWinding_FillType,
+                 scale, paint);
+        showPath(canvas, 200, 200, SkPath::kInverseEvenOdd_FillType,
+                 scale, paint);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+        
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+        
+        SkPaint paint;
+        const SkScalar scale = SkIntToScalar(5)/4;
+
+        paint.setAntiAlias(false);
+
+        showFour(canvas, SK_Scalar1, paint);
+        canvas->translate(SkIntToScalar(450), 0);
+        showFour(canvas, scale, paint);
+
+        paint.setAntiAlias(true);
+
+        canvas->translate(SkIntToScalar(-450), SkIntToScalar(450));
+        showFour(canvas, SK_Scalar1, paint);
+        canvas->translate(SkIntToScalar(450), 0);
+        showFour(canvas, scale, paint);
+    }
+    
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new FillTypeGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gm/gm.h b/gm/gm.h
new file mode 100644
index 0000000..ab92ff6
--- /dev/null
+++ b/gm/gm.h
@@ -0,0 +1,45 @@
+#ifndef skiagm_DEFINED
+#define skiagm_DEFINED
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRefCnt.h"
+#include "SkSize.h"
+#include "SkString.h"
+#include "SkTRegistry.h"
+
+namespace skiagm {
+	
+	static inline SkISize make_isize(int w, int h) {
+		SkISize sz;
+		sz.set(w, h);
+		return sz;
+	}
+
+    class GM {
+    public:
+        GM();
+        virtual ~GM();
+		
+		void draw(SkCanvas*);
+		SkISize getISize() { return this->onISize(); }
+        const char* shortName() {
+            if (fShortName.size() == 0) {
+                fShortName = this->onShortName();
+            }
+            return fShortName.c_str();
+        }
+
+	protected:
+		virtual void onDraw(SkCanvas*) = 0;
+		virtual SkISize onISize() = 0;
+        virtual SkString onShortName() = 0;
+        
+    private:
+        SkString fShortName;
+    };
+
+    typedef SkTRegistry<GM*, void*> GMRegistry;
+}
+
+#endif
diff --git a/gm/gm_files.mk b/gm/gm_files.mk
new file mode 100644
index 0000000..50eb311
--- /dev/null
+++ b/gm/gm_files.mk
@@ -0,0 +1,8 @@
+SOURCE := \
+	bitmapfilters.cpp \
+	filltypes.cpp \
+	gradients.cpp \
+	shapes.cpp \
+	tilemodes.cpp \
+	xfermodes.cpp \
+	gmmain.cpp
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
new file mode 100644
index 0000000..59f6c64
--- /dev/null
+++ b/gm/gmmain.cpp
@@ -0,0 +1,197 @@
+#include "gm.h"
+#include "SkColorPriv.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+
+using namespace skiagm;
+
+// need to explicitly declare this, or we get some weird infinite loop llist
+template GMRegistry* GMRegistry::gHead;
+
+class Iter {
+public:
+    Iter() {
+        fReg = GMRegistry::Head();
+    }
+	
+    GM* next() {
+        if (fReg) {
+            GMRegistry::Factory fact = fReg->factory();
+            fReg = fReg->next();
+            return fact(0);
+        }
+        return NULL;
+    }
+	
+    static int Count() {
+        const GMRegistry* reg = GMRegistry::Head();
+        int count = 0;
+        while (reg) {
+            count += 1;
+            reg = reg->next();
+        }
+        return count;
+    }
+	
+private:
+    const GMRegistry* fReg;
+};
+
+static SkString make_name(const char shortName[], const char configName[]) {
+    SkString name(shortName);
+    name.appendf("_%s", configName);
+    return name;
+}
+
+static SkString make_filename(const char path[], const SkString& name) {
+    SkString filename(path);
+    if (filename.size() && filename[filename.size() - 1] != '/') {
+        filename.append("/");
+    }
+    filename.appendf("%s.png", name.c_str());
+    return filename;
+}
+
+/* since PNG insists on unpremultiplying our alpha, we take no precision chances
+    and force all pixels to be 100% opaque, otherwise on compare we may not get
+    a perfect match.
+ */
+static void force_all_opaque(const SkBitmap& bitmap) {
+    SkAutoLockPixels lock(bitmap);
+    for (int y = 0; y < bitmap.height(); y++) {
+        for (int x = 0; x < bitmap.width(); x++) {
+            *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
+        }
+    }
+}
+
+static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) {
+    SkBitmap copy;
+    bitmap.copyTo(&copy, SkBitmap::kARGB_8888_Config);
+    force_all_opaque(copy);
+    return SkImageEncoder::EncodeFile(path.c_str(), copy,
+                                      SkImageEncoder::kPNG_Type, 100);
+}
+
+static void compare(const SkBitmap& target, const SkBitmap& base,
+                    const SkString& name) {
+    SkBitmap copy;
+    const SkBitmap* bm = &target;
+    if (target.config() != SkBitmap::kARGB_8888_Config) {
+        target.copyTo(&copy, SkBitmap::kARGB_8888_Config);
+        bm = &copy;
+    }
+
+    force_all_opaque(*bm);
+
+    const int w = bm->width();
+    const int h = bm->height();
+    if (w != base.width() || h != base.height()) {
+        SkDebugf("---- dimensions mismatch for %s base [%d %d] current [%d %d]\n",
+                 name.c_str(), base.width(), base.height(), w, h);
+        return;
+    }
+
+    SkAutoLockPixels bmLock(*bm);
+    SkAutoLockPixels baseLock(base);
+
+    for (int y = 0; y < h; y++) {
+        for (int x = 0; x < w; x++) {
+            SkPMColor c0 = *base.getAddr32(x, y);
+            SkPMColor c1 = *bm->getAddr32(x, y);
+            if (c0 != c1) {
+                SkDebugf("----- pixel mismatch for %s at [%d %d] base 0x%08X current 0x%08X\n",
+                         name.c_str(), x, y, c0, c1);
+                return;
+            }
+        }
+    }
+}
+
+static const struct {
+	SkBitmap::Config	fConfig;
+	bool				fUsePicture;
+	const char*			fName;
+} gRec[] = {
+	{ SkBitmap::kARGB_8888_Config,	false,	"8888" },
+	{ SkBitmap::kARGB_4444_Config,	false,	"4444" },
+	{ SkBitmap::kRGB_565_Config,	false,	"565" },
+};
+
+int main (int argc, char * const argv[]) {
+    SkAutoGraphics ag;
+    
+    const char* writePath = NULL;   // if non-null, where we write the originals
+    const char* readPath = NULL;    // if non-null, were we read from to compare
+
+    char* const* stop = argv + argc;
+    for (++argv; argv < stop; ++argv) {
+        if (strcmp(*argv, "-w") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                writePath = *argv;
+            }
+        } else if (strcmp(*argv, "-r") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                readPath = *argv;
+            }
+        }
+    }
+    
+    Iter iter;
+    GM* gm;
+	
+    while ((gm = iter.next()) != NULL) {
+		SkISize size = gm->getISize();
+        SkDebugf("creating... %s [%d %d]\n", gm->shortName(),
+                 size.width(), size.height());
+
+		SkBitmap bitmap;
+		for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+			bitmap.setConfig(gRec[i].fConfig, size.width(), size.height());
+			bitmap.allocPixels();
+			bitmap.eraseColor(0);
+			SkCanvas canvas(bitmap);
+
+			gm->draw(&canvas);
+            
+            SkString name = make_name(gm->shortName(), gRec[i].fName);
+
+            if (writePath) {
+                SkString path = make_filename(writePath, name);
+                bool success = write_bitmap(path, bitmap);
+                if (!success) {
+                    fprintf(stderr, "FAILED to write %s\n", path.c_str());
+                }
+            } else if (readPath) {
+                SkString path = make_filename(readPath, name);
+                SkBitmap orig;
+                bool success = SkImageDecoder::DecodeFile(path.c_str(), &orig,
+                                    SkBitmap::kARGB_8888_Config,
+                                    SkImageDecoder::kDecodePixels_Mode, NULL);
+                if (success) {
+                    compare(bitmap, orig, name);
+                } else {
+                    fprintf(stderr, "FAILED to read %s\n", path.c_str());
+                }
+            }
+		}
+        SkDELETE(gm);
+    }
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+using namespace skiagm;
+
+GM::GM() {}
+GM::~GM() {}
+
+void GM::draw(SkCanvas* canvas) {
+	this->onDraw(canvas);
+}
+
+
diff --git a/gm/gradients.cpp b/gm/gradients.cpp
new file mode 100644
index 0000000..aed4e0b
--- /dev/null
+++ b/gm/gradients.cpp
@@ -0,0 +1,113 @@
+#include "gm.h"
+#include "SkGradientShader.h"
+
+namespace skiagm {
+
+struct GradData {
+    int             fCount;
+    const SkColor*  fColors;
+    const SkScalar* fPos;
+};
+
+static const SkColor gColors[] = {
+    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
+};
+static const SkScalar gPos0[] = { 0, SK_Scalar1 };
+static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
+static const SkScalar gPos2[] = {
+    0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
+};
+
+static const GradData gGradData[] = {
+    { 2, gColors, NULL },
+    { 2, gColors, gPos0 },
+    { 2, gColors, gPos1 },
+    { 5, gColors, NULL },
+    { 5, gColors, gPos2 }
+};
+
+static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
+                            SkShader::TileMode tm, SkUnitMapper* mapper) {
+    return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
+                                          data.fCount, tm, mapper);
+}
+                                          
+static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
+                            SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center;
+    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+               SkScalarAve(pts[0].fY, pts[1].fY));
+    return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
+                                          data.fPos, data.fCount, tm, mapper);
+}
+
+static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
+                           SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center;
+    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+               SkScalarAve(pts[0].fY, pts[1].fY));
+    return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
+                                         data.fPos, data.fCount, mapper);
+}
+
+typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
+                     SkShader::TileMode tm, SkUnitMapper* mapper);
+static const GradMaker gGradMakers[] = {
+    MakeLinear, MakeRadial, MakeSweep
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GradientsGM : public GM {
+public:
+	GradientsGM() {}
+    
+protected:
+    SkString onShortName() {
+        return SkString("gradients");
+    }
+    
+	SkISize onISize() { return make_isize(640, 380); }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        SkPoint pts[2] = {
+            { 0, 0 },
+            { SkIntToScalar(100), SkIntToScalar(100) }
+        };
+        SkShader::TileMode tm = SkShader::kClamp_TileMode;
+        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
+            canvas->save();
+            for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
+                SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, NULL);
+                paint.setShader(shader);
+                canvas->drawRect(r, paint);
+                shader->unref();
+                canvas->translate(0, SkIntToScalar(120));
+            }
+            canvas->restore();
+            canvas->translate(SkIntToScalar(120), 0);
+        }
+    }
+    
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new GradientsGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gm/shapes.cpp b/gm/shapes.cpp
new file mode 100644
index 0000000..b3d4863
--- /dev/null
+++ b/gm/shapes.cpp
@@ -0,0 +1,126 @@
+#include "gm.h"
+#include "SkPicture.h"
+#include "SkRectShape.h"
+#include "SkGroupShape.h"
+
+namespace skiagm {
+
+static SkRect make_rect(int l, int t, int r, int b) {
+    SkRect rect;
+    rect.set(SkIntToScalar(l), SkIntToScalar(t),
+             SkIntToScalar(r), SkIntToScalar(b));
+    return rect;
+}
+
+static SkShape* make_shape0(bool red) {
+    SkRectShape* s = new SkRectShape;
+    s->setRect(make_rect(10, 10, 90, 90));
+    if (red) {
+        s->paint().setColor(SK_ColorRED);
+    }
+    return s;
+}
+
+static SkShape* make_shape1() {
+    SkRectShape* s = new SkRectShape;
+    s->setOval(make_rect(10, 10, 90, 90));
+    s->paint().setColor(SK_ColorBLUE);
+    return s;
+}
+
+static SkShape* make_shape2() {
+    SkRectShape* s = new SkRectShape;
+    s->setRRect(make_rect(10, 10, 90, 90),
+                SkIntToScalar(20), SkIntToScalar(20));
+    s->paint().setColor(SK_ColorGREEN);
+    return s;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ShapesGM : public GM {
+    SkGroupShape fGroup;
+    SkMatrixRef*    fMatrixRefs[4];
+public:
+	ShapesGM() {
+        SkMatrix m;
+        fGroup.appendShape(make_shape0(false))->unref();
+        m.setRotate(SkIntToScalar(30), SkIntToScalar(50), SkIntToScalar(50));
+        m.postTranslate(0, SkIntToScalar(120));
+        fGroup.appendShape(make_shape0(true), m)->unref();
+
+        m.setTranslate(SkIntToScalar(120), 0);
+        fGroup.appendShape(make_shape1(), m)->unref();
+        m.postTranslate(0, SkIntToScalar(120));
+        fGroup.appendShape(make_shape2(), m)->unref();
+        
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) {
+            SkSafeRef(fMatrixRefs[i] = fGroup.getShapeMatrixRef(i));
+        }
+    }
+    
+    virtual ~ShapesGM() {
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) {
+            SkSafeUnref(fMatrixRefs[i]);
+        }
+    }
+    
+protected:
+    virtual SkString onShortName() {
+        return SkString("shapes");
+    }
+    
+	virtual SkISize onISize() {
+        return make_isize(380, 480);
+    }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        SkMatrix saveM = *fMatrixRefs[3];
+        SkScalar c = SkIntToScalar(50);
+        fMatrixRefs[3]->preRotate(SkIntToScalar(30), c, c);
+        
+        SkMatrix matrix;
+     
+        SkGroupShape* gs = new SkGroupShape;
+        SkAutoUnref aur(gs);
+        gs->appendShape(&fGroup);
+        matrix.setScale(-SK_Scalar1, SK_Scalar1);
+        matrix.postTranslate(SkIntToScalar(220), SkIntToScalar(240));
+        gs->appendShape(&fGroup, matrix);
+        matrix.setTranslate(SkIntToScalar(240), 0);
+        matrix.preScale(SK_Scalar1*2, SK_Scalar1*2);
+        gs->appendShape(&fGroup, matrix);
+        
+#if 0        
+        canvas->drawShape(gs);
+#else
+        SkPicture pict;
+        SkCanvas* cv = pict.beginRecording(1000, 1000);
+        cv->scale(SK_ScalarHalf, SK_ScalarHalf);
+        cv->drawShape(gs);
+        cv->translate(SkIntToScalar(680), SkIntToScalar(480));
+        cv->scale(-SK_Scalar1, SK_Scalar1);
+        cv->drawShape(gs);
+        pict.endRecording();
+        canvas->drawPicture(pict);
+#endif
+
+        *fMatrixRefs[3] = saveM;
+}
+    
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ShapesGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/tilemodes.cpp b/gm/tilemodes.cpp
new file mode 100644
index 0000000..d836ea5
--- /dev/null
+++ b/gm/tilemodes.cpp
@@ -0,0 +1,155 @@
+#include "gm.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTypeface.h"
+
+// effects
+#include "SkGradientShader.h"
+#include "SkUnitMappers.h"
+#include "SkBlurDrawLooper.h"
+
+namespace skiagm {
+
+static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
+    bm->setConfig(config, w, h);
+    bm->allocPixels();
+    bm->eraseColor(0);
+    
+    SkCanvas    canvas(*bm);
+    SkPoint     pts[] = { { 0, 0 }, { SkIntToScalar(w), SkIntToScalar(h)} };
+    SkColor     colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
+    SkScalar    pos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
+    SkPaint     paint;
+    
+    SkUnitMapper*   um = NULL;    
+
+    um = new SkCosineMapper;
+//    um = new SkDiscreteMapper(12);
+
+    SkAutoUnref au(um);
+
+    paint.setDither(true);
+    paint.setShader(SkGradientShader::CreateLinear(pts, colors, pos,
+                SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode, um))->unref();
+    canvas.drawPaint(paint);
+}
+
+static void setup(SkPaint* paint, const SkBitmap& bm, bool filter,
+                  SkShader::TileMode tmx, SkShader::TileMode tmy) {
+    SkShader* shader = SkShader::CreateBitmapShader(bm, tmx, tmy);
+    paint->setShader(shader)->unref();
+    paint->setFilterBitmap(filter);
+}
+
+static const SkBitmap::Config gConfigs[] = {
+    SkBitmap::kARGB_8888_Config,
+    SkBitmap::kRGB_565_Config,
+    SkBitmap::kARGB_4444_Config
+};
+static const int gWidth = 32;
+static const int gHeight = 32;
+
+class TilingGM : public GM {
+    SkBlurDrawLooper    fLooper;
+public:
+	TilingGM()
+            : fLooper(SkIntToScalar(1), SkIntToScalar(2), SkIntToScalar(2),
+                      0x88000000) {
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
+            makebm(&fTexture[i], gConfigs[i], gWidth, gHeight);
+        }
+    }
+
+    SkBitmap    fTexture[SK_ARRAY_COUNT(gConfigs)];
+	
+protected:
+    SkString onShortName() {
+        return SkString("tilemodes");
+    }
+    
+	SkISize onISize() { return make_isize(880, 560); }
+    
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        SkRect r = { 0, 0, SkIntToScalar(gWidth*2), SkIntToScalar(gHeight*2) };
+
+        static const char* gConfigNames[] = { "8888", "565", "4444" };
+    
+        static const bool           gFilters[] = { false, true };
+        static const char*          gFilterNames[] = {     "point",                     "bilinear" };
+    
+        static const SkShader::TileMode gModes[] = { SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode };
+        static const char*          gModeNames[] = {    "C",                    "R",                   "M" };
+
+        SkScalar y = SkIntToScalar(24);
+        SkScalar x = SkIntToScalar(10);
+
+        for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
+            for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
+                SkPaint p;
+                SkString str;
+                p.setAntiAlias(true);
+                p.setDither(true);
+                p.setLooper(&fLooper);
+                str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]);
+
+                p.setTextAlign(SkPaint::kCenter_Align);
+                canvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p);
+                
+                x += r.width() * 4 / 3;
+            }
+        }
+        
+        y += SkIntToScalar(16);
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
+            for (size_t j = 0; j < SK_ARRAY_COUNT(gFilters); j++) {
+                x = SkIntToScalar(10);
+                for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
+                    for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
+                        SkPaint paint;
+                        setup(&paint, fTexture[i], gFilters[j], gModes[kx], gModes[ky]);
+                        paint.setDither(true);
+                        
+                        canvas->save();
+                        canvas->translate(x, y);
+                        canvas->drawRect(r, paint);
+                        canvas->restore();
+                        
+                        x += r.width() * 4 / 3;
+                    }
+                }
+                {
+                    SkPaint p;
+                    SkString str;
+                    p.setAntiAlias(true);
+                    p.setLooper(&fLooper);
+                    str.printf("%s, %s", gConfigNames[i], gFilterNames[j]);
+                    canvas->drawText(str.c_str(), str.size(), x, y + r.height() * 2 / 3, p);
+                }
+
+                y += r.height() * 4 / 3;
+            }
+        }
+    }
+    
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new TilingGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gm/xfermodes.cpp b/gm/xfermodes.cpp
new file mode 100644
index 0000000..5a43f4a
--- /dev/null
+++ b/gm/xfermodes.cpp
@@ -0,0 +1,188 @@
+#include "gm.h"
+#include "SkBitmap.h"
+#include "SkShader.h"
+#include "SkXfermode.h"
+
+namespace skiagm {
+
+static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) {    
+    src->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    src->allocPixels();
+    src->eraseColor(0);
+
+    SkCanvas c(*src);
+    SkPaint p;
+    SkRect r;
+    SkScalar ww = SkIntToScalar(w);
+    SkScalar hh = SkIntToScalar(h);
+
+    p.setAntiAlias(true);
+    p.setColor(0xFFFFCC44);    
+    r.set(0, 0, ww*3/4, hh*3/4);
+    c.drawOval(r, p);
+    
+    dst->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    dst->allocPixels();
+    dst->eraseColor(0);
+    c.setBitmapDevice(*dst);
+
+    p.setColor(0xFF66AAFF);
+    r.set(ww/3, hh/3, ww*19/20, hh*19/20);
+    c.drawRect(r, p);
+}
+
+static uint16_t gBG[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
+
+class XfermodesGM : public GM {
+    SkBitmap    fBitmap;
+    SkBitmap    fBG;
+    SkBitmap    fSrcB, fDstB;
+
+    void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha) {
+        SkPaint p;
+        
+        canvas->drawBitmap(fSrcB, 0, 0, &p);        
+        p.setAlpha(alpha);
+        p.setXfermode(mode);
+        canvas->drawBitmap(fDstB, 0, 0, &p);
+    }
+    
+public:
+	XfermodesGM() {
+        const int W = 64;
+        const int H = 64;
+        
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, W, H);
+        fBitmap.allocPixels();
+        
+        fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
+        fBG.setPixels(gBG);
+        fBG.setIsOpaque(true);
+        
+        make_bitmaps(W, H, &fSrcB, &fDstB);
+    }
+    
+protected:
+    virtual SkString onShortName() {
+        return SkString("xfermodes");
+    }
+
+	virtual SkISize onISize() {
+        return make_isize(790, 480);
+    }
+
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+        return;
+        SkShader* s = SkShader::CreateBitmapShader(fBG,
+                                                   SkShader::kRepeat_TileMode,
+                                                   SkShader::kRepeat_TileMode);
+        SkPaint p;
+        SkMatrix m;
+        
+        p.setShader(s)->unref();
+        m.setScale(SkIntToScalar(8), SkIntToScalar(8));
+        s->setLocalMatrix(m);
+        canvas->drawPaint(p);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+        
+        const struct {
+            SkXfermode::Mode    fMode;
+            const char*         fLabel;
+        } gModes[] = {
+            { SkXfermode::kClear_Mode,    "Clear"     },
+            { SkXfermode::kSrc_Mode,      "Src"       },
+            { SkXfermode::kDst_Mode,      "Dst"       },
+            { SkXfermode::kSrcOver_Mode,  "SrcOver"   },
+            { SkXfermode::kDstOver_Mode,  "DstOver"   },
+            { SkXfermode::kSrcIn_Mode,    "SrcIn"     },
+            { SkXfermode::kDstIn_Mode,    "DstIn"     },
+            { SkXfermode::kSrcOut_Mode,   "SrcOut"    },
+            { SkXfermode::kDstOut_Mode,   "DstOut"    },
+            { SkXfermode::kSrcATop_Mode,  "SrcATop"   },
+            { SkXfermode::kDstATop_Mode,  "DstATop"   },
+            { SkXfermode::kXor_Mode,      "Xor"       },
+            
+            { SkXfermode::kPlus_Mode,         "Plus"          },
+            { SkXfermode::kMultiply_Mode,     "Multiply"      },
+            { SkXfermode::kScreen_Mode,       "Screen"        },
+            { SkXfermode::kOverlay_Mode,      "Overlay"       },
+            { SkXfermode::kDarken_Mode,       "Darken"        },
+            { SkXfermode::kLighten_Mode,      "Lighten"       },
+            { SkXfermode::kColorDodge_Mode,   "ColorDodge"    },
+            { SkXfermode::kColorBurn_Mode,    "ColorBurn"     },
+            { SkXfermode::kHardLight_Mode,    "HardLight"     },
+            { SkXfermode::kSoftLight_Mode,    "SoftLight"     },
+            { SkXfermode::kDifference_Mode,   "Difference"    },
+            { SkXfermode::kExclusion_Mode,    "Exclusion"     },
+        };
+        
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
+        
+        SkCanvas c(fBitmap);
+        const SkScalar w = SkIntToScalar(fBitmap.width());
+        const SkScalar h = SkIntToScalar(fBitmap.height());
+        SkShader* s = SkShader::CreateBitmapShader(fBG,
+                                                   SkShader::kRepeat_TileMode,
+                                                   SkShader::kRepeat_TileMode);
+        SkMatrix m;
+        m.setScale(SkIntToScalar(6), SkIntToScalar(6));
+        s->setLocalMatrix(m);
+        
+        SkPaint labelP;
+        labelP.setAntiAlias(true);
+        labelP.setTextAlign(SkPaint::kCenter_Align);
+
+        const int W = 5;
+        
+        SkScalar x0 = 0;
+        for (int twice = 0; twice < 2; twice++) {
+            SkScalar x = x0, y = 0;
+            for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
+                SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
+                
+                fBitmap.eraseColor(0);
+                draw_mode(&c, mode, twice ? 0x88 : 0xFF);
+                SkSafeUnref(mode);
+                
+                SkPaint p;
+                SkRect r;
+                r.set(x, y, x+w, y+h);
+                r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+                p.setStyle(SkPaint::kStroke_Style);
+                canvas->drawRect(r, p);
+                p.setStyle(SkPaint::kFill_Style);
+                p.setShader(s);
+                r.inset(SK_ScalarHalf, SK_ScalarHalf);
+                canvas->drawRect(r, p);
+                
+                canvas->drawBitmap(fBitmap, x, y, NULL);
+                
+                canvas->drawText(gModes[i].fLabel, strlen(gModes[i].fLabel),
+                                 x + w/2, y - labelP.getTextSize()/2, labelP);
+                
+                x += w + SkIntToScalar(10);
+                if ((i % W) == W - 1) {
+                    x = x0;
+                    y += h + SkIntToScalar(30);
+                }
+            }
+            x0 += SkIntToScalar(400);
+        }
+        s->unref();
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new XfermodesGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/include/core/SkSize.h b/include/core/SkSize.h
new file mode 100644
index 0000000..d432102
--- /dev/null
+++ b/include/core/SkSize.h
@@ -0,0 +1,90 @@
+
+
+#ifndef SkSize_DEFINED
+#define SkSize_DEFINED
+
+template <typename T> struct SkTSize {
+    T fWidth;
+    T fHeight;
+
+    void set(T w, T h) {
+        fWidth = w;
+        fHeight = h;
+    }
+
+    /** Returns true iff fWidth == 0 && fHeight == 0
+     */
+    bool isZero() const {
+        return 0 == fWidth && 0 == fHeight;
+    }
+
+    /** Returns true if either widht or height are <= 0 */
+    bool isEmpty() const {
+        return fWidth <= 0 || fHeight <= 0;
+    }
+    
+    /** Set the width and height to 0 */
+    void setEmpty() {
+        fWidth = fHeight = 0;
+    }
+	
+	T width() const { return fWidth; }
+	T height() const { return fHeight; }
+    
+    /** If width or height is < 0, it is set to 0 */
+    void clampNegToZero() {
+        if (fWidth < 0) {
+            fWidth = 0;
+        }
+        if (fHeight < 0) {
+            fHeight = 0;
+        }
+    }
+    
+    bool equals(T w, T h) const {
+        return fWidth == w && fHeight == h;
+    }
+};
+
+template <typename T>
+static inline bool operator==(const SkTSize<T>& a, const SkTSize<T>& b) {
+    return a.fWidth == b.fWidth && a.fHeight == b.fHeight;
+}
+
+template <typename T>
+static inline bool operator!=(const SkTSize<T>& a, const SkTSize<T>& b) {
+    return !(a == b);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SkISize : public SkTSize<int32_t> {};
+
+#include "SkScalar.h"
+
+struct SkSize : public SkTSize<SkScalar> {
+    SkSize& operator=(const SkISize& src) {
+        this->set(SkIntToScalar(src.fWidth), SkIntToScalar(src.fHeight));
+        return *this;
+    }
+
+    SkISize round() const {
+        SkISize s;
+        s.set(SkScalarRound(fWidth), SkScalarRound(fHeight));
+        return s;
+    }
+    
+    SkISize ceil() const {
+        SkISize s;
+        s.set(SkScalarCeil(fWidth), SkScalarCeil(fHeight));
+        return s;
+    }
+
+    SkISize floor() const {
+        SkISize s;
+        s.set(SkScalarFloor(fWidth), SkScalarFloor(fHeight));
+        return s;
+    }
+};
+
+#endif