add BlitRow procs for 32->32, to allow for neon and other optimizations.
call these new procs in (nearly) all the places we had inlined loops before.
In once instance (blitter_argb32::blitAntiH) we get different results by a
  tiny bit. The new code is more accurate, and exactly inline with all of the
  other like-minded blits, so I think the change is good going forward.



git-svn-id: http://skia.googlecode.com/svn/trunk@366 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkBlitRow.h b/include/core/SkBlitRow.h
new file mode 100644
index 0000000..dbbd84d
--- /dev/null
+++ b/include/core/SkBlitRow.h
@@ -0,0 +1,82 @@
+#ifndef SkBlitRow_DEFINED
+#define SkBlitRow_DEFINED
+
+#include "SkBitmap.h"
+#include "SkColor.h"
+
+class SkBlitRow {
+public:
+    enum Flags16 {
+        //! If set, the alpha parameter will be != 255
+        kGlobalAlpha_Flag   = 0x01,
+        //! If set, the src colors may have alpha != 255
+        kSrcPixelAlpha_Flag = 0x02,
+        //! If set, the resulting 16bit colors should be dithered
+        kDither_Flag        = 0x04
+    };
+
+    /** Function pointer that reads a scanline of src SkPMColors, and writes
+        a corresponding scanline of 16bit colors (specific format based on the
+        config passed to the Factory.
+     
+        The x,y params are useful just for dithering
+     
+        @param alpha A global alpha to be applied to all of the src colors
+        @param x The x coordinate of the beginning of the scanline
+        @param y THe y coordinate of the scanline
+     */
+    typedef void (*Proc)(uint16_t* SK_RESTRICT dst,
+                         const SkPMColor* SK_RESTRICT src,
+                         int count, U8CPU alpha, int x, int y);
+
+    //! Public entry-point to return a blit function ptr
+    static Proc Factory(unsigned flags, SkBitmap::Config);
+
+    ///////////// D32 version
+
+    enum Flags32 {
+        kGlobalAlpha_Flag32     = 1 << 0,
+        kSrcPixelAlpha_Flag32   = 1 << 1,
+    };
+
+    /** Function pointer that blends 32bit colors onto a 32bit destination.
+        @param dst  array of dst 32bit colors
+        @param src  array of src 32bit colors (w/ or w/o alpha)
+        @param count number of colors to blend
+        @param alpha global alpha to be applied to all src colors
+     */
+    typedef void (*Proc32)(uint32_t* SK_RESTRICT dst,
+                         const SkPMColor* SK_RESTRICT src,
+                         int count, U8CPU alpha);
+
+    static Proc32 Factory32(unsigned flags32);
+    
+    /** Blend a single color onto a row of S32 pixels, writing the result
+        into a row of D32 pixels. src and dst may be the same memory, but
+        if they are not, they may not overlap.
+     */
+    static void Color32(SkPMColor dst[], const SkPMColor src[], int count,
+                         SkPMColor color);
+
+    /** Blend a single color onto a row of 32bit pixels, writing the result
+        into the same row.
+     */
+    static void Color32(SkPMColor row[], int count, SkPMColor color) {
+        Color32(row, row, count, color);
+    }
+
+private:
+    enum {
+        kFlags16_Mask = 7,
+        kFlags32_Mask = 3
+    };
+    /** These global arrays are indexed using the flags parameter to Factory,
+        and contain either NULL, or a platform-specific function-ptr to be used
+        in place of the system default.
+     */
+    static const Proc gPlatform_565_Procs[];
+    static const Proc gPlatform_4444_Procs[];
+    static const Proc32 gPlatform_Procs32[];
+};
+
+#endif
diff --git a/samplecode/SampleGM.cpp b/samplecode/SampleGM.cpp
new file mode 100644
index 0000000..095dfe6
--- /dev/null
+++ b/samplecode/SampleGM.cpp
@@ -0,0 +1,84 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+
+#include "gm.h"
+
+using namespace skiagm;
+
+GM::GM() {}
+GM::~GM() {}
+
+void GM::draw(SkCanvas* canvas) {
+	this->onDraw(canvas);
+}
+
+// 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;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GMView : public SkView {
+    Iter fIter;
+    GM*  fGM;
+public:
+	GMView() {
+        fGM = fIter.next();
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "GM");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        fGM->draw(canvas);
+    }
+    
+private:
+    typedef SkView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new GMView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleNinePatch.cpp b/samplecode/SampleNinePatch.cpp
index efb108f..941b3ff 100644
--- a/samplecode/SampleNinePatch.cpp
+++ b/samplecode/SampleNinePatch.cpp
@@ -19,6 +19,9 @@
         r.set(1, 1, fBM.width() - 1, fBM.height() - 1);
         fBM.extractSubset(&tmp, r);
         fBM.swap(tmp);
+        
+        fX = SkIntToScalar(fBM.width());
+        fY = 0;
     }
 
 protected:
@@ -33,7 +36,11 @@
     
     void drawBG(SkCanvas* canvas) {
         canvas->drawColor(SK_ColorWHITE);
-        
+    }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+
         canvas->scale(1.5f, 1.5f);
         
         canvas->drawBitmap(fBM, 0, 0);
@@ -44,17 +51,19 @@
         
         margins.set(d, d, d, d);
         dst.set(0, 0, SkIntToScalar(200), SkIntToScalar(200));
-        dst.offset(SkIntToScalar(fBM.width()), 0);
-        dst.offset(SkIntToScalar(2), SkIntToScalar(2));
+        dst.offset(fX, fY);
         
         SkNinePatch::DrawNine(canvas, dst, fBM, margins);
     }
     
-    virtual void onDraw(SkCanvas* canvas) {
-        this->drawBG(canvas);
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        fX = x / 1.5f;
+        fY = y / 1.5f;
+        this->inval(NULL);
+        return this->INHERITED::onFindClickHandler(x, y);
     }
-    
 private:
+    SkScalar fX, fY;
     typedef SkView INHERITED;
 };
 
diff --git a/src/core/SkBlitRow.h b/src/core/SkBlitRow.h
deleted file mode 100644
index ec31ff5..0000000
--- a/src/core/SkBlitRow.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef SkBlitRow_DEFINED
-#define SkBlitRow_DEFINED
-
-#include "SkBitmap.h"
-#include "SkColor.h"
-
-class SkBlitRow {
-public:
-    enum {
-        //! If set, the alpha parameter will be != 255
-        kGlobalAlpha_Flag   = 0x01,
-        //! If set, the src colors may have alpha != 255
-        kSrcPixelAlpha_Flag = 0x02,
-        //! If set, the resulting 16bit colors should be dithered
-        kDither_Flag        = 0x04
-    };
-
-    /** Function pointer that reads a scanline of src SkPMColors, and writes
-        a corresponding scanline of 16bit colors (specific format based on the
-        config passed to the Factory.
-     
-        The x,y params are useful just for dithering
-     
-        @param alpha A global alpha to be applied to all of the src colors
-        @param x The x coordinate of the beginning of the scanline
-        @param y THe y coordinate of the scanline
-     */
-    typedef void (*Proc)(uint16_t* SK_RESTRICT dst,
-                         const SkPMColor* SK_RESTRICT src,
-                         int count, U8CPU alpha, int x, int y);
-
-    //! Public entry-point to return a blit function ptr
-    static Proc Factory(unsigned flags, SkBitmap::Config);
-
-private:
-    /** These global arrays are indexed using the flags parameter to Factory,
-        and contain either NULL, or a platform-specific function-ptr to be used
-        in place of the system default.
-     */
-    static const Proc gPlatform_565_Procs[];
-    static const Proc gPlatform_4444_Procs[];
-};
-
-#endif
diff --git a/src/core/SkBlitRow_D16.cpp b/src/core/SkBlitRow_D16.cpp
index 1860d3b..66ac90e 100644
--- a/src/core/SkBlitRow_D16.cpp
+++ b/src/core/SkBlitRow_D16.cpp
@@ -235,6 +235,8 @@
     
 SkBlitRow::Proc SkBlitRow::Factory(unsigned flags, SkBitmap::Config config) {
     SkASSERT(flags < SK_ARRAY_COUNT(gDefault_565_Procs));
+    // just so we don't crash
+    flags &= kFlags16_Mask;
 
     SkBlitRow::Proc proc = NULL;
 
diff --git a/src/core/SkBlitRow_D32.cpp b/src/core/SkBlitRow_D32.cpp
new file mode 100644
index 0000000..f67bb9a
--- /dev/null
+++ b/src/core/SkBlitRow_D32.cpp
@@ -0,0 +1,112 @@
+#include "SkBlitRow.h"
+#include "SkColorPriv.h"
+#include "SkUtils.h"
+
+static void S32_Opaque_BlitRow32(SkPMColor* SK_RESTRICT dst,
+                                 const SkPMColor* SK_RESTRICT src,
+                                 int count, U8CPU alpha) {
+    SkASSERT(255 == alpha);
+    memcpy(dst, src, count * sizeof(SkPMColor));
+}
+
+static void S32_Blend_BlitRow32(SkPMColor* SK_RESTRICT dst,
+                                const SkPMColor* SK_RESTRICT src,
+                                int count, U8CPU alpha) {
+    SkASSERT(alpha <= 255);
+    if (count > 0) {
+        unsigned src_scale = SkAlpha255To256(alpha);
+        unsigned dst_scale = 256 - src_scale;
+        do {
+            *dst = SkAlphaMulQ(*src, src_scale) + SkAlphaMulQ(*dst, dst_scale);
+            src += 1;
+            dst += 1;
+        } while (--count > 0);
+    }
+}
+
+//#define TEST_SRC_ALPHA
+
+static void S32A_Opaque_BlitRow32(SkPMColor* SK_RESTRICT dst,
+                                  const SkPMColor* SK_RESTRICT src,
+                                  int count, U8CPU alpha) {
+    SkASSERT(255 == alpha);
+    if (count > 0) {
+        do {
+#ifdef TEST_SRC_ALPHA
+            SkPMColor sc = *src;
+            if (sc) {
+                unsigned srcA = SkGetPackedA32(sc);
+                SkPMColor result = sc;
+                if (srcA != 255) {
+                    result = SkPMSrcOver(sc, *dst);
+                }
+                *dst = result;
+            }
+#else
+            *dst = SkPMSrcOver(*src, *dst);
+#endif
+            src += 1;
+            dst += 1;
+        } while (--count > 0);
+    }
+}
+
+static void S32A_Blend_BlitRow32(SkPMColor* SK_RESTRICT dst,
+                                 const SkPMColor* SK_RESTRICT src,
+                                 int count, U8CPU alpha) {
+    SkASSERT(alpha <= 255);
+    if (count > 0) {
+        do {
+            *dst = SkBlendARGB32(*src, *dst, alpha);
+            src += 1;
+            dst += 1;
+        } while (--count > 0);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkBlitRow::Proc32 gDefault_Procs32[] = {
+    S32_Opaque_BlitRow32,
+    S32_Blend_BlitRow32,
+    S32A_Opaque_BlitRow32,
+    S32A_Blend_BlitRow32
+};
+
+SkBlitRow::Proc32 SkBlitRow::Factory32(unsigned flags) {
+    SkASSERT(flags < SK_ARRAY_COUNT(gDefault_Procs32));
+    // just so we don't crash
+    flags &= kFlags32_Mask;
+    
+    SkBlitRow::Proc32 proc = gPlatform_Procs32[flags];
+    if (NULL == proc) {
+        proc = gDefault_Procs32[flags];
+    }
+    SkASSERT(proc);
+    return proc;
+}
+
+void SkBlitRow::Color32(SkPMColor dst[], const SkPMColor src[], int count,
+                        SkPMColor color) {
+    if (count > 0) {
+        if (0 == color) {
+            if (src != dst) {
+                memcpy(dst, src, count * sizeof(SkPMColor));
+            }
+        }
+        unsigned colorA = SkGetPackedA32(color);
+        if (255 == colorA) {
+            sk_memset32(dst, color, count);
+        } else {
+            unsigned scale = 256 - SkAlpha255To256(colorA);
+            do {
+                *dst = color + SkAlphaMulQ(*src, scale);
+                src += 1;
+                dst += 1;
+            } while (--count);
+        }
+    }
+}
+
+
+
diff --git a/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp
index d3e5714..37bd0da 100644
--- a/src/core/SkBlitter_ARGB32.cpp
+++ b/src/core/SkBlitter_ARGB32.cpp
@@ -69,29 +69,7 @@
 void SkARGB32_Blitter::blitH(int x, int y, int width) {
     SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
 
-    if (fSrcA == 0) {
-        return;
-    }
-
-    uint32_t* device = fDevice.getAddr32(x, y);
-
-    if (fSrcA == 255) {
-        sk_memset32(device, fPMColor, width);
-    } else {
-        uint32_t color = fPMColor;
-        unsigned dst_scale = SkAlpha255To256(255 - fSrcA);
-        uint32_t prevDst = ~device[0];  // so we always fail the test the first time
-        uint32_t result SK_INIT_TO_AVOID_WARNING;
-
-        for (int i = 0; i < width; i++) {
-            uint32_t currDst = device[i];
-            if (currDst != prevDst) {
-                result = color + SkAlphaMulQ(currDst, dst_scale);
-                prevDst = currDst;
-            }
-            device[i] = result;
-        }
-    }
+    SkBlitRow::Color32(fDevice.getAddr32(x, y), width, fPMColor);
 }
 
 void SkARGB32_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
@@ -115,13 +93,8 @@
             if ((opaqueMask & aa) == 255) {
                 sk_memset32(device, color, count);
             } else {
-                uint32_t sc = SkAlphaMulQ(color, aa);
-                unsigned dst_scale = 255 - SkGetPackedA32(sc);
-                int n = count;
-                do {
-                    --n;
-                    device[n] = sc + SkAlphaMulQ(device[n], dst_scale);
-                } while (n > 0);
+                uint32_t sc = SkAlphaMulQ(color, SkAlpha255To256(aa));
+                SkBlitRow::Color32(device, count, sc);
             }
         }
         runs += count;
@@ -310,29 +283,11 @@
 
     uint32_t*   device = fDevice.getAddr32(x, y);
     uint32_t    color = fPMColor;
+    size_t      rowBytes = fDevice.rowBytes();
 
-    if (fSrcA == 255) {
-        while (--height >= 0) {
-            sk_memset32(device, color, width);
-            device = (uint32_t*)((char*)device + fDevice.rowBytes());
-        }
-    } else {
-        unsigned dst_scale = SkAlpha255To256(255 - fSrcA);
-
-        while (--height >= 0) {
-            uint32_t prevDst = ~device[0];
-            uint32_t result SK_INIT_TO_AVOID_WARNING;
-
-            for (int i = 0; i < width; i++) {
-                uint32_t dst = device[i];
-                if (dst != prevDst) {
-                    result = color + SkAlphaMulQ(dst, dst_scale);
-                    prevDst = dst;
-                }
-                device[i] = result;
-            }
-            device = (uint32_t*)((char*)device + fDevice.rowBytes());
-        }
+    while (--height >= 0) {
+        SkBlitRow::Color32(device, width, color);
+        device = (uint32_t*)((char*)device + rowBytes);
     }
 }
 
@@ -440,11 +395,20 @@
 //////////////////////////////////////////////////////////////////////////////////////////
 
 SkARGB32_Shader_Blitter::SkARGB32_Shader_Blitter(const SkBitmap& device,
-                                                 const SkPaint& paint)
-        : INHERITED(device, paint) {
+                            const SkPaint& paint) : INHERITED(device, paint) {
     fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor)));
 
-    (fXfermode = paint.getXfermode())->safeRef();
+    fXfermode = paint.getXfermode();
+    SkSafeRef(fXfermode);
+
+    int flags = 0;
+    if (!(fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+        flags |= SkBlitRow::kSrcPixelAlpha_Flag32;
+    }
+    // we call this on the output from the shader
+    fProc32 = SkBlitRow::Factory32(flags);
+    // we call this on the output from the shader + alpha from the aa buffer
+    fProc32Blend = SkBlitRow::Factory32(flags | SkBlitRow::kGlobalAlpha_Flag32);
 }
 
 SkARGB32_Shader_Blitter::~SkARGB32_Shader_Blitter() {
@@ -465,16 +429,7 @@
         if (fXfermode) {
             fXfermode->xfer32(device, span, width, NULL);
         } else {
-            for (int i = 0; i < width; i++) {
-                uint32_t src = span[i];
-                if (src) {
-                    unsigned srcA = SkGetPackedA32(src);
-                    if (srcA != 0xFF) {
-                        src += SkAlphaMulQ(device[i], SkAlpha255To256(255 - srcA));
-                    }
-                    device[i] = src;
-                }
-            }
+            fProc32(device, span, width, 255);
         }
     }
 }
@@ -519,15 +474,12 @@
             }
             int aa = *antialias;
             if (aa) {
-                if (aa == 255) {  // cool, have the shader draw right into the device
+                if (aa == 255) {
+                    // cool, have the shader draw right into the device
                     shader->shadeSpan(x, y, device, count);
                 } else {
                     shader->shadeSpan(x, y, span, count);
-                    for (int i = count - 1; i >= 0; --i) {
-                        if (span[i]) {
-                            device[i] = SkBlendARGB32(span[i], device[i], aa);
-                        }
-                    }
+                    fProc32Blend(device, span, count, aa);
                 }
             }
             device += count;
@@ -535,7 +487,7 @@
             antialias += count;
             x += count;
         } 
-    } else {    // no xfermode but we are not opaque
+    } else {    // no xfermode but the shader not opaque
         for (;;) {
             int count = *runs;
             if (count <= 0) {
@@ -545,17 +497,9 @@
             if (aa) {
                 fShader->shadeSpan(x, y, span, count);
                 if (aa == 255) {
-                    for (int i = count - 1; i >= 0; --i) {
-                        if (span[i]) {
-                            device[i] = SkPMSrcOver(span[i], device[i]);
-                        }
-                    }
+                    fProc32(device, span, count, 255);
                 } else {
-                    for (int i = count - 1; i >= 0; --i) {
-                        if (span[i]) {
-                            device[i] = SkBlendARGB32(span[i], device[i], aa);
-                        }
-                    }
+                    fProc32Blend(device, span, count, aa);
                 }
             }
             device += count;
diff --git a/src/core/SkComposeShader.cpp b/src/core/SkComposeShader.cpp
index 1e5e202..7e566a3 100644
--- a/src/core/SkComposeShader.cpp
+++ b/src/core/SkComposeShader.cpp
@@ -124,6 +124,8 @@
 
     if (NULL == mode)   // implied SRC_OVER
     {
+        // TODO: when we have a good test-case, should use SkBlitRow::Proc32
+        // for these loops
         do {
             int n = count;
             if (n > TMP_COLOR_COUNT)
diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h
index 5e34685..84f8fa6 100644
--- a/src/core/SkCoreBlitters.h
+++ b/src/core/SkCoreBlitters.h
@@ -141,8 +141,10 @@
     virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
 
 private:
-    SkXfermode* fXfermode;
-    SkPMColor*  fBuffer;
+    SkXfermode*         fXfermode;
+    SkPMColor*          fBuffer;
+    SkBlitRow::Proc32   fProc32;
+    SkBlitRow::Proc32   fProc32Blend;
 
     // illegal
     SkARGB32_Shader_Blitter& operator=(const SkARGB32_Shader_Blitter&);
diff --git a/src/core/SkSpriteBlitter_ARGB32.cpp b/src/core/SkSpriteBlitter_ARGB32.cpp
index 6addde7..01ee9f1 100644
--- a/src/core/SkSpriteBlitter_ARGB32.cpp
+++ b/src/core/SkSpriteBlitter_ARGB32.cpp
@@ -16,64 +16,58 @@
 */
 
 #include "SkSpriteBlitter.h"
+#include "SkBlitRow.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
 #include "SkTemplates.h"
 #include "SkUtils.h"
-#include "SkColorPriv.h"
-
-#define D32_S32A_Opaque_Pixel(dst, sc) \
-do {                                                                  \
-    if (sc) {                                                         \
-        unsigned srcA = SkGetPackedA32(sc);                           \
-        uint32_t result = sc;                                         \
-        if (srcA != 0xFF) {                                           \
-            result += SkAlphaMulQ(*dst, SkAlpha255To256(255 - srcA)); \
-        }                                                             \
-        *dst = result;                                                \
-    }                                                                 \
-} while (0)
-
-#define SkSPRITE_CLASSNAME                  Sprite_D32_S32A_Opaque
-#define SkSPRITE_ARGS
-#define SkSPRITE_FIELDS
-#define SkSPRITE_INIT
-#define SkSPRITE_DST_TYPE                   uint32_t
-#define SkSPRITE_SRC_TYPE                   uint32_t
-#define SkSPRITE_DST_GETADDR                getAddr32
-#define SkSPRITE_SRC_GETADDR                getAddr32
-#define SkSPRITE_PREAMBLE(srcBM, x, y)
-#define SkSPRITE_BLIT_PIXEL(dst, src)       D32_S32A_Opaque_Pixel(dst, src)
-#define SkSPRITE_NEXT_ROW
-#define SkSPRITE_POSTAMBLE(srcBM)
-#include "SkSpriteBlitterTemplate.h"
+#include "SkXfermode.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class Sprite_D32_S32_Opaque : public SkSpriteBlitter {
+class Sprite_D32_S32 : public SkSpriteBlitter {
 public:
-    Sprite_D32_S32_Opaque(const SkBitmap& source) : SkSpriteBlitter(source) {}
+    Sprite_D32_S32(const SkBitmap& src, U8CPU alpha)  : INHERITED(src) {
+        SkASSERT(src.config() == SkBitmap::kARGB_8888_Config);
 
+        unsigned flags32 = 0;
+        if (255 != alpha) {
+            flags32 |= SkBlitRow::kGlobalAlpha_Flag32;
+        }
+        if (!src.isOpaque()) {
+            flags32 |= SkBlitRow::kSrcPixelAlpha_Flag32;
+        }
+
+        fProc32 = SkBlitRow::Factory32(flags32);
+        fAlpha = alpha;
+    }
+    
     virtual void blitRect(int x, int y, int width, int height) {
         SkASSERT(width > 0 && height > 0);
         SK_RESTRICT uint32_t* dst = fDevice->getAddr32(x, y);
         const SK_RESTRICT uint32_t* src = fSource->getAddr32(x - fLeft,
                                                              y - fTop);
-        unsigned dstRB = fDevice->rowBytes();
-        unsigned srcRB = fSource->rowBytes();
-        size_t size = width * sizeof(uint32_t);
+        size_t dstRB = fDevice->rowBytes();
+        size_t srcRB = fSource->rowBytes();
+        SkBlitRow::Proc32 proc = fProc32;
+        U8CPU             alpha = fAlpha;
 
         do {
-            memcpy(dst, src, size);
+            proc(dst, src, width, alpha);
             dst = (SK_RESTRICT uint32_t*)((char*)dst + dstRB);
             src = (const SK_RESTRICT uint32_t*)((const char*)src + srcRB);
         } while (--height != 0);
     }
+
+private:
+    SkBlitRow::Proc32   fProc32;
+    U8CPU               fAlpha;
+    
+    typedef SkSpriteBlitter INHERITED;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#include "SkColorFilter.h"
-#include "SkXfermode.h"
-
 class Sprite_D32_XferFilter : public SkSpriteBlitter {
 public:
     Sprite_D32_XferFilter(const SkBitmap& source, const SkPaint& paint)
@@ -86,6 +80,17 @@
         
         fBufferSize = 0;
         fBuffer = NULL;
+
+        unsigned flags32 = 0;
+        if (255 != paint.getAlpha()) {
+            flags32 |= SkBlitRow::kGlobalAlpha_Flag32;
+        }
+        if (!source.isOpaque()) {
+            flags32 |= SkBlitRow::kSrcPixelAlpha_Flag32;
+        }
+        
+        fProc32 = SkBlitRow::Factory32(flags32);
+        fAlpha = paint.getAlpha();
     }
     
     virtual ~Sprite_D32_XferFilter() {
@@ -107,11 +112,13 @@
     }
 
 protected:
-    SkColorFilter*  fColorFilter;
-    SkXfermode*     fXfermode;
-    int             fBufferSize;
-    SkPMColor*      fBuffer;
-    
+    SkColorFilter*      fColorFilter;
+    SkXfermode*         fXfermode;
+    int                 fBufferSize;
+    SkPMColor*          fBuffer;
+    SkBlitRow::Proc32   fProc32;
+    U8CPU               fAlpha;
+
 private:
     typedef SkSpriteBlitter INHERITED;
 };
@@ -144,9 +151,7 @@
             if (NULL != xfermode) {
                 xfermode->xfer32(dst, tmp, width, NULL);
             } else {
-                for (int i = 0; i < width; i++) {
-                    dst[i] = SkPMSrcOver(tmp[i], dst[i]);
-                }
+                fProc32(dst, tmp, width, fAlpha);
             }
 
             dst = (SK_RESTRICT uint32_t*)((char*)dst + dstRB);
@@ -192,9 +197,7 @@
             if (NULL != xfermode) {
                 xfermode->xfer32(dst, buffer, width, NULL);
             } else {
-                for (int i = 0; i < width; i++) {
-                    dst[i] = SkPMSrcOver(buffer[i], dst[i]);
-                }
+                fProc32(dst, buffer, width, fAlpha);
             }
             
             dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB);
@@ -273,16 +276,20 @@
 SkSpriteBlitter* SkSpriteBlitter::ChooseD32(const SkBitmap& source,
                                             const SkPaint& paint,
                                             void* storage, size_t storageSize) {
-    if (paint.getMaskFilter() != NULL || paint.getAlpha() != 0xFF) {
+    if (paint.getMaskFilter() != NULL) {
         return NULL;
     }
 
+    U8CPU       alpha = paint.getAlpha();
     SkXfermode* xfermode = paint.getXfermode();
     SkColorFilter* filter = paint.getColorFilter();
     SkSpriteBlitter* blitter = NULL;
 
     switch (source.getConfig()) {
         case SkBitmap::kARGB_4444_Config:
+            if (alpha != 0xFF) {
+                return NULL;    // we only have opaque sprites
+            }
             if (xfermode || filter) {
                 SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S4444_XferFilter,
                                       storage, storageSize, (source, paint));
@@ -296,14 +303,15 @@
             break;
         case SkBitmap::kARGB_8888_Config:
             if (xfermode || filter) {
-                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32A_XferFilter,
+                if (255 == alpha) {
+                    // this can handle xfermode or filter, but not alpha
+                    SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32A_XferFilter,
                                       storage, storageSize, (source, paint));
-            } else if (source.isOpaque()) {
-                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32_Opaque,
-                                      storage, storageSize, (source));
+                }
             } else {
-                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32A_Opaque,
-                                      storage, storageSize, (source));
+                // this can handle alpha, but not xfermode or filter
+                SK_PLACEMENT_NEW_ARGS(blitter, Sprite_D32_S32,
+                              storage, storageSize, (source, alpha));
             }
             break;
         default:
diff --git a/src/core/core_files.mk b/src/core/core_files.mk
index 736f7fd..553706d 100644
--- a/src/core/core_files.mk
+++ b/src/core/core_files.mk
@@ -8,6 +8,7 @@
     SkBitmapSampler.cpp \
     SkBitmap_scroll.cpp \
     SkBlitRow_D16.cpp \
+    SkBlitRow_D32.cpp \
     SkBlitRow_D4444.cpp \
     SkBlitter.cpp \
     SkBlitter_4444.cpp \
diff --git a/src/effects/SkColorFilters.cpp b/src/effects/SkColorFilters.cpp
index 0bb3ae5..733e1ae 100644
--- a/src/effects/SkColorFilters.cpp
+++ b/src/effects/SkColorFilters.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "SkBlitRow.h"
 #include "SkColorFilter.h"
 #include "SkColorPriv.h"
 #include "SkUtils.h"
@@ -86,12 +87,7 @@
     
     virtual void filterSpan(const SkPMColor shader[], int count,
                             SkPMColor result[]) {
-        SkPMColor   src = fPMColor;
-        unsigned    scale = SkAlpha255To256(255 - SkGetPackedA32(src));
-
-        for (int i = 0; i < count; i++) {
-            result[i] = src + SkAlphaMulQ(shader[i], scale);
-        }
+        SkBlitRow::Color32(result, shader, count, fPMColor);
     }
 
     virtual void filterSpan16(const uint16_t shader[], int count,
diff --git a/src/opts/SkBlitRow_opts_none.cpp b/src/opts/SkBlitRow_opts_none.cpp
index 88ab7c4..7a77759 100644
--- a/src/opts/SkBlitRow_opts_none.cpp
+++ b/src/opts/SkBlitRow_opts_none.cpp
@@ -30,3 +30,10 @@
     NULL,   // S32A_D4444_Blend_Dither
 };
 
+const SkBlitRow::Proc32 SkBlitRow::gPlatform_Procs32[] = {
+    NULL,   // S32_Opaque,
+    NULL,   // S32_Blend,
+    NULL,   // S32A_Opaque,
+    NULL,   // S32A_Blend,
+};
+
diff --git a/xcode/core/core.xcodeproj/project.pbxproj b/xcode/core/core.xcodeproj/project.pbxproj
index 8fba832..673d640 100644
--- a/xcode/core/core.xcodeproj/project.pbxproj
+++ b/xcode/core/core.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		00244E10106A6DEA00B8F4D8 /* SkBlitRow_D32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00244E0F106A6DEA00B8F4D8 /* SkBlitRow_D32.cpp */; };
 		002884C80EFAB8B90083E387 /* SkMMapStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 002884C70EFAB8B90083E387 /* SkMMapStream.cpp */; };
 		002884D50EFAB8F80083E387 /* SkStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 002884D40EFAB8F80083E387 /* SkStream.cpp */; };
 		002B342810213340000F04C6 /* SkBlitRow_opts_none.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 002B342710213340000F04C6 /* SkBlitRow_opts_none.cpp */; };
@@ -132,6 +133,7 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
+		00244E0F106A6DEA00B8F4D8 /* SkBlitRow_D32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBlitRow_D32.cpp; path = ../../src/core/SkBlitRow_D32.cpp; sourceTree = SOURCE_ROOT; };
 		002884C70EFAB8B90083E387 /* SkMMapStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkMMapStream.cpp; path = ../../src/core/SkMMapStream.cpp; sourceTree = SOURCE_ROOT; };
 		002884D40EFAB8F80083E387 /* SkStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkStream.cpp; path = ../../src/core/SkStream.cpp; sourceTree = SOURCE_ROOT; };
 		002B342710213340000F04C6 /* SkBlitRow_opts_none.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBlitRow_opts_none.cpp; path = ../../src/opts/SkBlitRow_opts_none.cpp; sourceTree = SOURCE_ROOT; };
@@ -298,6 +300,7 @@
 		08FB7795FE84155DC02AAC07 /* src */ = {
 			isa = PBXGroup;
 			children = (
+				00244E0F106A6DEA00B8F4D8 /* SkBlitRow_D32.cpp */,
 				00554E0C102733D300C9C8ED /* SkBitmapProcState_opts_none.cpp */,
 				002B342710213340000F04C6 /* SkBlitRow_opts_none.cpp */,
 				005DC79A10179AE000F00DFB /* SkFontHost.cpp */,
@@ -612,6 +615,7 @@
 				005DC79B10179AE000F00DFB /* SkFontHost.cpp in Sources */,
 				002B342810213340000F04C6 /* SkBlitRow_opts_none.cpp in Sources */,
 				00554E0D102733D300C9C8ED /* SkBitmapProcState_opts_none.cpp in Sources */,
+				00244E10106A6DEA00B8F4D8 /* SkBlitRow_D32.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
index 2f89460..3f07318 100644
--- a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
+++ b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
@@ -23,9 +23,14 @@
 		00003C9E0EFC233F000FF73A /* SkDOM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C9A0EFC233F000FF73A /* SkDOM.cpp */; };
 		00003CA10EFC233F000FF73A /* SkXMLParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C9D0EFC233F000FF73A /* SkXMLParser.cpp */; };
 		00003CA40EFC235F000FF73A /* SkXMLParser_empty.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003CA30EFC235F000FF73A /* SkXMLParser_empty.cpp */; };
-		0009E2201057E96800B0DE6F /* SampleStrokePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0009E21F1057E96800B0DE6F /* SampleStrokePath.cpp */; };
 		000A99820FD97526007E45BD /* SampleArc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A41E4A0EFC312F00C9CBEB /* SampleArc.cpp */; };
 		001B871E1042184D00C84ED4 /* Forth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 001B871D1042184D00C84ED4 /* Forth.cpp */; };
+		00244D1B10642BBA00B8F4D8 /* SampleStrokePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0009E21F1057E96800B0DE6F /* SampleStrokePath.cpp */; };
+		00244D4A1069646100B8F4D8 /* SampleGradients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00C55DA00F8552DC000CAC09 /* SampleGradients.cpp */; };
+		00244D97106A539500B8F4D8 /* SampleXfermodes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CB20F01658C00A2D6EE /* SampleXfermodes.cpp */; };
+		00244DBB106A61B700B8F4D8 /* SampleGM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00244DBA106A61B700B8F4D8 /* SampleGM.cpp */; };
+		00244DCD106A630100B8F4D8 /* bitmapfilters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00244DC7106A630100B8F4D8 /* bitmapfilters.cpp */; };
+		00244DE2106A681600B8F4D8 /* SampleShaders.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CA80F01658C00A2D6EE /* SampleShaders.cpp */; };
 		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 */; };
@@ -43,7 +48,6 @@
 		0041CE420F00A12400695E8C /* SampleImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE2C0F00A12400695E8C /* SampleImage.cpp */; };
 		0041CE430F00A12400695E8C /* SampleLayers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE2D0F00A12400695E8C /* SampleLayers.cpp */; };
 		0041CE450F00A12400695E8C /* SampleMeasure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE2F0F00A12400695E8C /* SampleMeasure.cpp */; };
-		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 */; };
 		0057785F0FF17CCC00582CD9 /* SampleMipMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2794C04E0FE72903009AD112 /* SampleMipMap.cpp */; };
@@ -53,7 +57,6 @@
 		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 */; };
-		007A7CB70F01658C00A2D6EE /* SampleShaders.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CA80F01658C00A2D6EE /* SampleShaders.cpp */; };
 		007A7CB80F01658C00A2D6EE /* SampleStrokeText.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CA90F01658C00A2D6EE /* SampleStrokeText.cpp */; };
 		007A7CBA0F01658C00A2D6EE /* SampleText.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CAB0F01658C00A2D6EE /* SampleText.cpp */; };
 		007A7CBB0F01658C00A2D6EE /* SampleTextAlpha.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CAC0F01658C00A2D6EE /* SampleTextAlpha.cpp */; };
@@ -64,9 +67,9 @@
 		007A7CC00F01658C00A2D6EE /* SampleVertices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CB10F01658C00A2D6EE /* SampleVertices.cpp */; };
 		007C785E0F3B4C230004B142 /* SamplePathClip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007C785D0F3B4C230004B142 /* SamplePathClip.cpp */; };
 		00840B75104C69F5005B3EDC /* SampleExtractAlpha.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00840B74104C69F5005B3EDC /* SampleExtractAlpha.cpp */; };
-		0088C1160FEC311C00CE52F5 /* SampleXfermodes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CB20F01658C00A2D6EE /* SampleXfermodes.cpp */; };
 		008C4D980F77DAEE0056981C /* SampleHairline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 008C4D970F77DAEE0056981C /* SampleHairline.cpp */; };
 		009490320FB0A5B90063C792 /* SampleLayerMask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 009490310FB0A5B90063C792 /* SampleLayerMask.cpp */; };
+		009887F1106142FC0020D19B /* SampleNinePatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE310F00A12400695E8C /* SampleNinePatch.cpp */; };
 		009CC9190F65918A002185BE /* SampleFontScalerTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 009CC9180F65918A002185BE /* SampleFontScalerTest.cpp */; };
 		00A728270FD43D0400D5051F /* SampleMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6760FCCCB01002BD8B4 /* SampleMovie.cpp */; };
 		00A7282F0FD43D3700D5051F /* SkMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A7282D0FD43D3700D5051F /* SkMovie.cpp */; };
@@ -78,7 +81,6 @@
 		00C1B809103857A400FA5948 /* SampleFillType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE270F00A12400695E8C /* SampleFillType.cpp */; };
 		00EB4593104DBB18002B413E /* ForthTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00EB4592104DBB18002B413E /* ForthTests.cpp */; };
 		00ED55F3104A10EB00F51FF8 /* StdWords.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00ED55F2104A10EB00F51FF8 /* StdWords.cpp */; };
-		00F53F480FFCFC4D003FA70A /* SampleGradients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00C55DA00F8552DC000CAC09 /* SampleGradients.cpp */; };
 		00FF39140FC6ED2C00915187 /* SampleEffects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00FF39130FC6ED2C00915187 /* SampleEffects.cpp */; };
 		0156F80407C56A3000C6122B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0156F80307C56A3000C6122B /* Foundation.framework */; };
 		01FC44D507BD3BB800D228F4 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 01FC44D407BD3BB800D228F4 /* Quartz.framework */; };
@@ -157,6 +159,13 @@
 		00003CA30EFC235F000FF73A /* SkXMLParser_empty.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkXMLParser_empty.cpp; path = ../../src/ports/SkXMLParser_empty.cpp; sourceTree = SOURCE_ROOT; };
 		0009E21F1057E96800B0DE6F /* SampleStrokePath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleStrokePath.cpp; path = ../../samplecode/SampleStrokePath.cpp; sourceTree = SOURCE_ROOT; };
 		001B871D1042184D00C84ED4 /* Forth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Forth.cpp; path = ../../forth/Forth.cpp; sourceTree = SOURCE_ROOT; };
+		00244DBA106A61B700B8F4D8 /* SampleGM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleGM.cpp; path = ../../samplecode/SampleGM.cpp; sourceTree = SOURCE_ROOT; };
+		00244DC7106A630100B8F4D8 /* bitmapfilters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bitmapfilters.cpp; path = ../../gm/bitmapfilters.cpp; sourceTree = SOURCE_ROOT; };
+		00244DC8106A630100B8F4D8 /* filltypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = filltypes.cpp; path = ../../gm/filltypes.cpp; sourceTree = SOURCE_ROOT; };
+		00244DC9106A630100B8F4D8 /* gradients.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gradients.cpp; path = ../../gm/gradients.cpp; sourceTree = SOURCE_ROOT; };
+		00244DCA106A630100B8F4D8 /* shapes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = shapes.cpp; path = ../../gm/shapes.cpp; sourceTree = SOURCE_ROOT; };
+		00244DCB106A630100B8F4D8 /* tilemodes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tilemodes.cpp; path = ../../gm/tilemodes.cpp; sourceTree = SOURCE_ROOT; };
+		00244DCC106A630100B8F4D8 /* xfermodes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = xfermodes.cpp; path = ../../gm/xfermodes.cpp; sourceTree = SOURCE_ROOT; };
 		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; };
 		003145310FB9B48F00B10956 /* SampleShapes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleShapes.cpp; path = ../../samplecode/SampleShapes.cpp; sourceTree = SOURCE_ROOT; };
@@ -264,6 +273,7 @@
 				007A7CA40F01658C00A2D6EE /* SamplePicture.cpp */,
 				00D6B5CB0F72DC4300C466B9 /* SampleFuzz.cpp */,
 				00C55DA00F8552DC000CAC09 /* SampleGradients.cpp */,
+				00244DBA106A61B700B8F4D8 /* SampleGM.cpp */,
 				009490310FB0A5B90063C792 /* SampleLayerMask.cpp */,
 				003145310FB9B48F00B10956 /* SampleShapes.cpp */,
 				009CC9180F65918A002185BE /* SampleFontScalerTest.cpp */,
@@ -342,6 +352,19 @@
 			name = Products;
 			sourceTree = "<group>";
 		};
+		00244DC5106A62E600B8F4D8 /* gm */ = {
+			isa = PBXGroup;
+			children = (
+				00244DC7106A630100B8F4D8 /* bitmapfilters.cpp */,
+				00244DC8106A630100B8F4D8 /* filltypes.cpp */,
+				00244DC9106A630100B8F4D8 /* gradients.cpp */,
+				00244DCA106A630100B8F4D8 /* shapes.cpp */,
+				00244DCB106A630100B8F4D8 /* tilemodes.cpp */,
+				00244DCC106A630100B8F4D8 /* xfermodes.cpp */,
+			);
+			name = gm;
+			sourceTree = "<group>";
+		};
 		0028844A0EFAA35C0083E387 /* Products */ = {
 			isa = PBXGroup;
 			children = (
@@ -369,6 +392,7 @@
 		20286C29FDCF999611CA2CEA /* CICarbonSample */ = {
 			isa = PBXGroup;
 			children = (
+				00244DC5106A62E600B8F4D8 /* gm */,
 				001B871D1042184D00C84ED4 /* Forth.cpp */,
 				00ED55F2104A10EB00F51FF8 /* StdWords.cpp */,
 				00EB4592104DBB18002B413E /* ForthTests.cpp */,
@@ -559,9 +583,7 @@
 				0041CE450F00A12400695E8C /* SampleMeasure.cpp in Sources */,
 				0041CE480F00A12400695E8C /* SampleOverflow.cpp in Sources */,
 				0041CE4A0F00A12400695E8C /* SamplePatch.cpp in Sources */,
-				0041CE470F00A12400695E8C /* SampleNinePatch.cpp in Sources */,
 				007A7CB40F01658C00A2D6EE /* SamplePoints.cpp in Sources */,
-				007A7CB70F01658C00A2D6EE /* SampleShaders.cpp in Sources */,
 				007A7CB80F01658C00A2D6EE /* SampleStrokeText.cpp in Sources */,
 				007A7CBA0F01658C00A2D6EE /* SampleText.cpp in Sources */,
 				007A7CBB0F01658C00A2D6EE /* SampleTextAlpha.cpp in Sources */,
@@ -587,12 +609,10 @@
 				000A99820FD97526007E45BD /* SampleArc.cpp in Sources */,
 				00AF77B00FE2EA2D007F9650 /* SampleTestGL.cpp in Sources */,
 				00AF787E0FE94433007F9650 /* SamplePath.cpp in Sources */,
-				0088C1160FEC311C00CE52F5 /* SampleXfermodes.cpp in Sources */,
 				005E92DC0FF08507008965B9 /* SampleFilter2.cpp in Sources */,
 				005E92E00FF08512008965B9 /* SampleFilter.cpp in Sources */,
 				0057785F0FF17CCC00582CD9 /* SampleMipMap.cpp in Sources */,
 				005778B40FF5616F00582CD9 /* SampleShapes.cpp in Sources */,
-				00F53F480FFCFC4D003FA70A /* SampleGradients.cpp in Sources */,
 				27005D16100903C100E275B6 /* SampleLines.cpp in Sources */,
 				27005D5F10095B2B00E275B6 /* SampleCircle.cpp in Sources */,
 				00C1B809103857A400FA5948 /* SampleFillType.cpp in Sources */,
@@ -602,7 +622,13 @@
 				00ED55F3104A10EB00F51FF8 /* StdWords.cpp in Sources */,
 				00840B75104C69F5005B3EDC /* SampleExtractAlpha.cpp in Sources */,
 				00EB4593104DBB18002B413E /* ForthTests.cpp in Sources */,
-				0009E2201057E96800B0DE6F /* SampleStrokePath.cpp in Sources */,
+				009887F1106142FC0020D19B /* SampleNinePatch.cpp in Sources */,
+				00244D1B10642BBA00B8F4D8 /* SampleStrokePath.cpp in Sources */,
+				00244D4A1069646100B8F4D8 /* SampleGradients.cpp in Sources */,
+				00244D97106A539500B8F4D8 /* SampleXfermodes.cpp in Sources */,
+				00244DBB106A61B700B8F4D8 /* SampleGM.cpp in Sources */,
+				00244DCD106A630100B8F4D8 /* bitmapfilters.cpp in Sources */,
+				00244DE2106A681600B8F4D8 /* SampleShaders.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -699,7 +725,7 @@
 				);
 				GCC_THREADSAFE_STATICS = NO;
 				SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
-				USER_HEADER_SEARCH_PATHS = "../../include/**";
+				USER_HEADER_SEARCH_PATHS = "../../include/** ../../gm";
 			};
 			name = Debug;
 		};
@@ -714,7 +740,7 @@
 				);
 				GCC_THREADSAFE_STATICS = NO;
 				SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
-				USER_HEADER_SEARCH_PATHS = "../../include/**";
+				USER_HEADER_SEARCH_PATHS = "../../include/** ../../gm";
 			};
 			name = Release;
 		};