add experimental canvas pipe



git-svn-id: http://skia.googlecode.com/svn/trunk@1187 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/pipe/SkGPipe.h b/include/pipe/SkGPipe.h
new file mode 100644
index 0000000..5c729a2
--- /dev/null
+++ b/include/pipe/SkGPipe.h
@@ -0,0 +1,61 @@
+/*
+    Copyright 2011 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef SkGPipe_DEFINED
+#define SkGPipe_DEFINED
+
+#include "SkWriter32.h"
+
+class SkCanvas;
+
+class SkGPipeReader {
+public:
+    SkGPipeReader(SkCanvas* target);
+    ~SkGPipeReader();
+
+    enum Status {
+        kDone_Status,   //!< no more data expected from reader
+        kEOF_Status,    //!< need more data from reader
+        kError_Status   //!< encountered error
+    };
+
+    Status playback(const void* data, size_t length);
+
+private:
+    SkCanvas*           fCanvas;
+    class SkGPipeState* fState;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkGPipeWriter {
+public:
+    SkGPipeWriter();
+    ~SkGPipeWriter();
+
+    bool isRecording() const { return NULL != fCanvas; }
+    SkCanvas* startRecording();
+    void endRecording();
+
+    size_t flatten(void* buffer);
+
+private:
+    class SkGPipeCanvas* fCanvas;
+    SkWriter32 fWriter;
+};
+
+#endif
diff --git a/src/pipe/SkGPipePriv.h b/src/pipe/SkGPipePriv.h
new file mode 100644
index 0000000..ceef2bb
--- /dev/null
+++ b/src/pipe/SkGPipePriv.h
@@ -0,0 +1,202 @@
+/*
+    Copyright 2011 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#ifndef SkGPipePriv_DEFINED
+#define SkGPipePriv_DEFINED
+
+#include "SkTypes.h"
+
+#define UNIMPLEMENTED
+
+enum DrawOps {
+    // these match Canvas apis
+    kClipPath_DrawOp,
+    kClipRegion_DrawOp,
+    kClipRect_DrawOp,
+    kConcat_DrawOp,
+    kDrawBitmap_DrawOp,
+    kDrawBitmapMatrix_DrawOp,
+    kDrawBitmapRect_DrawOp,
+    kDrawClear_DrawOp,
+    kDrawData_DrawOp,
+    kDrawPaint_DrawOp,
+    kDrawPath_DrawOp,
+    kDrawPicture_DrawOp,
+    kDrawPoints_DrawOp,
+    kDrawPosText_DrawOp,
+    kDrawPosTextH_DrawOp,
+    kDrawRect_DrawOp,
+    kDrawShape_DrawOp,
+    kDrawSprite_DrawOp,
+    kDrawText_DrawOp,
+    kDrawTextOnPath_DrawOp,
+    kDrawVertices_DrawOp,
+    kRestore_DrawOp,
+    kRotate_DrawOp,
+    kSave_DrawOp,
+    kSaveLayer_DrawOp,
+    kScale_DrawOp,
+    kSetMatrix_DrawOp,
+    kSkew_DrawOp,
+    kTranslate_DrawOp,
+
+    // these edit paints
+    kPaintOp_DrawOp,
+
+    // these are signals to playback, not drawing verbs
+    kDone_DrawOp,
+};
+
+/**
+ *  DrawOp packs into a 32bit int as follows
+ *
+ *  DrawOp:8 - Flags:4 - Data:20
+ *
+ *  Flags and Data are called out separately, so we can reuse Data between
+ *  different Ops that might have different Flags. e.g. Data might be a Paint
+ *  index for both drawRect (no flags) and saveLayer (does have flags).
+ *
+ *  All Ops that take a SkPaint use their Data field to store the index to
+ *  the paint (previously defined with kPaintOp_DrawOp).
+ */
+
+#define DRAWOPS_OP_BITS     8
+#define DRAWOPS_FLAG_BITS   4
+#define DRAWOPS_DATA_BITS   20
+
+#define DRAWOPS_OP_MASK     ((1 << DRAWOPS_OP_BITS) - 1)
+#define DRAWOPS_FLAG_MASK   ((1 << DRAWOPS_FLAG_BITS) - 1)
+#define DRAWOPS_DATA_MASK   ((1 << DRAWOPS_DATA_BITS) - 1)
+
+static unsigned DrawOp_unpackOp(uint32_t op32) {
+    return (op32 >> (DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS));
+}
+
+static unsigned DrawOp_unpackFlags(uint32_t op32) {
+    return (op32 >> DRAWOPS_DATA_BITS) & DRAWOPS_FLAG_MASK;
+}
+
+static unsigned DrawOp_unpackData(uint32_t op32) {
+    return op32 & DRAWOPS_DATA_MASK;
+}
+
+static uint32_t DrawOp_packOpFlagData(DrawOps op, unsigned flags, unsigned data) {
+    SkASSERT(0 == (op & ~DRAWOPS_OP_MASK));
+    SkASSERT(0 == (flags & ~DRAWOPS_FLAG_MASK));
+    SkASSERT(0 == (data & ~DRAWOPS_DATA_MASK));
+
+    return (op << DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS) |
+           (flags << DRAWOPS_DATA_BITS) |
+            data;
+}
+
+/** DrawOp specific flag bits
+ */
+
+enum {
+    kSaveLayer_HasBounds_DrawOpFlag = 1 << 0,
+    kSaveLayer_HasPaint_DrawOpFlag = 1 << 1,
+};
+enum {
+    kClear_HasColor_DrawOpFlag  = 1 << 0
+};
+enum {
+    kDrawTextOnPath_HasMatrix_DrawOpFlag = 1 << 0
+};
+enum {
+    kDrawVertices_HasTexs_DrawOpFlag     = 1 << 0,
+    kDrawVertices_HasColors_DrawOpFlag   = 1 << 1,
+    kDrawVertices_HasIndices_DrawOpFlag  = 1 << 2,
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum PaintOps {
+    kReset_PaintOp,     // no arg
+    
+    kFlags_PaintOp,     // arg inline
+    kColor_PaintOp,     // arg 32
+    kStyle_PaintOp,     // arg inline
+    kJoin_PaintOp,      // arg inline
+    kCap_PaintOp,       // arg inline
+    kWidth_PaintOp,     // arg scalar
+    kMiter_PaintOp,// arg scalar
+    
+    kEncoding_PaintOp,  // arg inline - text
+    kHinting_PaintOp,   // arg inline - text
+    kAlign_PaintOp,     // arg inline - text
+    kTextSize_PaintOp,  // arg scalar - text
+    kTextScaleX_PaintOp,// arg scalar - text
+    kTextSkewX_PaintOp, // arg scalar - text
+    
+    kPathEffect_PaintOp,    // arg inline
+    kShader_PaintOp,
+    kXfermode_PaintOp,
+    kMaskFilter_PaintOp,
+    kColorFilter_PaintOp,
+    kRasterizer_PaintOp,
+    kDrawLooper_PaintOp,
+};
+
+#define PAINTOPS_OP_BITS     8
+#define PAINTOPS_FLAG_BITS   4
+#define PAINTOPS_DATA_BITS   20
+
+#define PAINTOPS_OP_MASK     ((1 << PAINTOPS_OP_BITS) - 1)
+#define PAINTOPS_FLAG_MASK   ((1 << PAINTOPS_FLAG_BITS) - 1)
+#define PAINTOPS_DATA_MASK   ((1 << PAINTOPS_DATA_BITS) - 1)
+
+static unsigned PaintOp_unpackOp(uint32_t op32) {
+    return (op32 >> (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS));
+}
+
+static unsigned PaintOp_unpackFlags(uint32_t op32) {
+    return (op32 >> PAINTOPS_DATA_BITS) & PAINTOPS_FLAG_MASK;
+}
+
+static unsigned PaintOp_unpackData(uint32_t op32) {
+    return op32 & PAINTOPS_DATA_MASK;
+}
+
+static uint32_t PaintOp_packOp(PaintOps op) {
+    SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
+    
+    return (op << PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS);
+}
+
+static uint32_t PaintOp_packOpData(PaintOps op, unsigned data) {
+    SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
+    SkASSERT(0 == (data & ~PAINTOPS_DATA_MASK));
+    
+    return (op << PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS) | data;
+}
+
+static uint32_t PaintOp_packOpFlagData(PaintOps op, unsigned flags, unsigned data) {
+    SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
+    SkASSERT(0 == (flags & ~PAINTOPS_FLAG_MASK));
+    SkASSERT(0 == (data & ~PAINTOPS_DATA_MASK));
+    
+    return (op << PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS) |
+    (flags << PAINTOPS_DATA_BITS) |
+    data;
+}
+
+enum {
+    kLastOp_PaintOpFlag = 1 << 0
+};
+
+#endif
diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp
new file mode 100644
index 0000000..94f05da
--- /dev/null
+++ b/src/pipe/SkGPipeRead.cpp
@@ -0,0 +1,500 @@
+/*
+    Copyright 2011 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkGPipePriv.h"
+#include "SkReader32.h"
+
+class SkGPipeState {
+public:
+    SkGPipeState();
+    ~SkGPipeState();
+    
+    const SkPaint& getPaint(uint32_t drawOp32) const;
+    
+    //  Extracts index from DrawOp_unpackData().
+    //  Returns the specified paint from our list, or creates a new paint if
+    //  index == count. If index > count, return NULL
+    SkPaint* editPaint(uint32_t drawOp32);
+    
+private:
+    SkTDArray<SkPaint*> fPaints;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> const T* skip(SkReader32* reader, int count = 1) {
+    SkASSERT(count >= 0);
+    size_t size = sizeof(T) * count;
+    SkASSERT(SkAlign4(size) == size);
+    return reinterpret_cast<const T*>(reader->skip(size));
+}
+
+template <typename T> const T* skipAlign(SkReader32* reader, int count = 1) {
+    SkASSERT(count >= 0);
+    size_t size = SkAlign4(sizeof(T) * count);
+    return reinterpret_cast<const T*>(reader->skip(size));
+}
+
+static void readRegion(SkReader32* reader, SkRegion* rgn) {
+    size_t size = rgn->unflatten(reader->peek());
+    SkASSERT(SkAlign4(size) == size);
+    (void)reader->skip(size);
+}
+
+static void readMatrix(SkReader32* reader, SkMatrix* matrix) {
+    size_t size = matrix->unflatten(reader->peek());
+    SkASSERT(SkAlign4(size) == size);
+    (void)reader->skip(size);
+}
+
+const SkPaint& SkGPipeState::getPaint(uint32_t op32) const {
+    unsigned index = DrawOp_unpackData(op32);
+    if (index >= fPaints.count()) {
+        SkASSERT(!"paint index out of range");
+        index = 0;  // we always have at least 1 paint
+    }
+    return *fPaints[index];
+}
+
+SkPaint* SkGPipeState::editPaint(uint32_t op32) {
+    unsigned index = DrawOp_unpackData(op32);
+
+    if (index > fPaints.count()) {
+        SkASSERT(!"paint index out of range");
+        return NULL;
+    }
+
+    if (index == fPaints.count()) {
+        *fPaints.append() = SkNEW(SkPaint);
+    }
+    return fPaints[index];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void clipPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    SkPath path;
+    path.unflatten(*reader);
+    canvas->clipPath(path, (SkRegion::Op)DrawOp_unpackData(op32));
+}
+
+static void clipRegion_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                          SkGPipeState* state) {
+    SkRegion rgn;
+    readRegion(reader, &rgn);
+    canvas->clipRegion(rgn, (SkRegion::Op)DrawOp_unpackData(op32));
+}
+
+static void clipRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    canvas->clipRect(*skip<SkRect>(reader), (SkRegion::Op)DrawOp_unpackData(op32));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void setMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    SkMatrix matrix;
+    readMatrix(reader, &matrix);
+    canvas->setMatrix(matrix);
+}
+
+static void concat_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    SkMatrix matrix;
+    readMatrix(reader, &matrix);
+    canvas->concat(matrix);
+}
+
+static void scale_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    const SkScalar* param = skip<SkScalar>(reader, 2);
+    canvas->scale(param[0], param[1]);
+}
+
+static void skew_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    const SkScalar* param = skip<SkScalar>(reader, 2);
+    canvas->skew(param[0], param[1]);
+}
+
+static void rotate_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    canvas->rotate(reader->readScalar());
+}
+
+static void translate_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    const SkScalar* param = skip<SkScalar>(reader, 2);
+    canvas->translate(param[0], param[1]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void save_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                    SkGPipeState* state) {
+    canvas->save((SkCanvas::SaveFlags)DrawOp_unpackData(op32));
+}
+
+static void saveLayer_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    unsigned flags = DrawOp_unpackData(op32);
+
+    const SkRect* bounds = NULL;
+    if (flags & kSaveLayer_HasBounds_DrawOpFlag) {
+        bounds = skip<SkRect>(reader);
+    }
+    const SkPaint* paint = NULL;
+    if (flags & kSaveLayer_HasPaint_DrawOpFlag) {
+        paint = &state->getPaint(reader->readU32());
+    }
+    canvas->saveLayer(bounds, paint,
+                      (SkCanvas::SaveFlags)DrawOp_unpackFlags(op32));
+}
+
+static void restore_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                       SkGPipeState* state) {
+    canvas->restore();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawClear_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    SkColor color = 0;
+    if (DrawOp_unpackFlags(op32) & kClear_HasColor_DrawOpFlag) {
+        color = reader->readU32();
+    }
+    canvas->clear(color);
+}
+
+static void drawPaint_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    canvas->drawPaint(state->getPaint(op32));
+}
+
+static void drawPoints_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                          SkGPipeState* state) {
+    SkCanvas::PointMode mode = (SkCanvas::PointMode)DrawOp_unpackFlags(op32);
+    size_t count = reader->readU32();
+    const SkPoint* pts = skip<SkPoint>(reader, count);
+    canvas->drawPoints(mode, count, pts, state->getPaint(op32));
+}
+
+static void drawRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    canvas->drawRect(*skip<SkRect>(reader), state->getPaint(op32));
+}
+
+static void drawPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    SkPath path;
+    path.unflatten(*reader);
+    canvas->drawPath(path, state->getPaint(op32));
+}
+
+static void drawVertices_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                            SkGPipeState* state) {
+    unsigned flags = DrawOp_unpackFlags(op32);
+
+    SkCanvas::VertexMode mode = (SkCanvas::VertexMode)reader->readU32();
+    int vertexCount = reader->readU32();
+    const SkPoint* verts = skip<SkPoint>(reader, vertexCount);
+
+    const SkPoint* texs = NULL;
+    if (flags & kDrawVertices_HasTexs_DrawOpFlag) {
+        texs = skip<SkPoint>(reader, vertexCount);
+    }
+
+    const SkColor* colors = NULL;
+    if (flags & kDrawVertices_HasColors_DrawOpFlag) {
+        colors = skip<SkColor>(reader, vertexCount);
+    }
+
+    // TODO: flatten/unflatten xfermodes
+    SkXfermode* xfer = NULL;
+
+    int indexCount = 0;
+    const uint16_t* indices = NULL;
+    if (flags & kDrawVertices_HasIndices_DrawOpFlag) {
+        indexCount = reader->readU32();
+        indices = skipAlign<uint16_t>(reader, indexCount);
+    }
+
+    canvas->drawVertices(mode, vertexCount, verts, texs, colors, xfer,
+                         indices, indexCount, state->getPaint(op32));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    size_t len = reader->readU32();
+    const void* text = reader->skip(SkAlign4(len));
+    const SkScalar* xy = skip<SkScalar>(reader, 2);
+    canvas->drawText(text, len, xy[0], xy[1], state->getPaint(op32));
+}
+
+static void drawPosText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    size_t len = reader->readU32();
+    const void* text = reader->skip(SkAlign4(len));
+    size_t posCount = reader->readU32();    // compute by our writer
+    const SkPoint* pos = skip<SkPoint>(reader, posCount);
+    canvas->drawPosText(text, len, pos, state->getPaint(op32));
+}
+
+static void drawPosTextH_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    size_t len = reader->readU32();
+    const void* text = reader->skip(SkAlign4(len));
+    size_t posCount = reader->readU32();    // compute by our writer
+    const SkScalar* xpos = skip<SkScalar>(reader, posCount);
+    SkScalar constY = reader->readScalar();
+    canvas->drawPosTextH(text, len, xpos, constY, state->getPaint(op32));
+}
+
+static void drawTextOnPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                              SkGPipeState* state) {
+    size_t len = reader->readU32();
+    const void* text = reader->skip(SkAlign4(len));
+
+    SkPath path;
+    path.unflatten(*reader);
+
+    SkMatrix matrixStorage;
+    const SkMatrix* matrix = NULL;
+    if (DrawOp_unpackFlags(op32) & kDrawTextOnPath_HasMatrix_DrawOpFlag) {
+        readMatrix(reader, &matrixStorage);
+        matrix = &matrixStorage;
+    }
+
+    canvas->drawTextOnPath(text, len, path, matrix, state->getPaint(op32));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawBitmap_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                          SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+static void drawBitmapMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                                SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+static void drawBitmapRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                              SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+static void drawSprite_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                          SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void drawData_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    // since we don't have a paint, we can use data for our (small) sizes
+    size_t size = DrawOp_unpackData(op32);
+    if (0 == size) {
+        size = reader->readU32();
+    }
+    const void* data = reader->skip(SkAlign4(size));
+    canvas->drawData(data, size);
+}
+
+static void drawShape_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+static void drawPicture_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                           SkGPipeState* state) {
+    UNIMPLEMENTED
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void inflate_patheffect(SkReader32* reader, SkPaint* paint) {
+}
+
+static void inflate_shader(SkReader32* reader, SkPaint* paint) {
+}
+
+static void inflate_xfermode(SkReader32* reader, SkPaint* paint) {
+}
+
+static void inflate_maskfilter(SkReader32* reader, SkPaint* paint) {
+}
+
+static void inflate_colorfilter(SkReader32* reader, SkPaint* paint) {
+}
+
+static void inflate_rasterizer(SkReader32* reader, SkPaint* paint) {
+}
+
+static void inflate_drawlooper(SkReader32* reader, SkPaint* paint) {
+}
+
+static void paintOp_rp(SkCanvas*, SkReader32* reader, uint32_t op32,
+                       SkGPipeState* state) {
+    SkPaint* p = state->editPaint(op32);
+    int done;
+
+    do {
+        uint32_t p32 = reader->readU32();
+        unsigned op = PaintOp_unpackOp(p32);
+        unsigned data = PaintOp_unpackData(p32);
+        done = PaintOp_unpackFlags(p32) & kLastOp_PaintOpFlag;
+
+        SkDebugf(" read %08X op=%d flags=%d data=%d\n", p32, op, done, data);
+
+        switch (op) {
+            case kReset_PaintOp: p->reset(); break;
+            case kFlags_PaintOp: p->setFlags(data); break;
+            case kColor_PaintOp: p->setColor(reader->readU32()); break;
+            case kStyle_PaintOp: p->setStyle((SkPaint::Style)data); break;
+            case kJoin_PaintOp: p->setStrokeJoin((SkPaint::Join)data); break;
+            case kCap_PaintOp: p->setStrokeCap((SkPaint::Cap)data); break;
+            case kWidth_PaintOp: p->setStrokeWidth(reader->readScalar()); break;
+            case kMiter_PaintOp: p->setStrokeMiter(reader->readScalar()); break;
+            case kEncoding_PaintOp:
+                p->setTextEncoding((SkPaint::TextEncoding)data);
+                break;
+            case kHinting_PaintOp: p->setHinting((SkPaint::Hinting)data); break;
+            case kAlign_PaintOp: p->setTextAlign((SkPaint::Align)data); break;
+            case kTextSize_PaintOp: p->setTextSize(reader->readScalar()); break;
+            case kTextScaleX_PaintOp: p->setTextScaleX(reader->readScalar()); break;
+            case kTextSkewX_PaintOp: p->setTextSkewX(reader->readScalar()); break;
+                
+            // flag to reference a cached index instead of inflating?
+            case kPathEffect_PaintOp: inflate_patheffect(reader, p); break;
+            case kShader_PaintOp: inflate_shader(reader, p); break;
+            case kXfermode_PaintOp: inflate_xfermode(reader, p); break;
+            case kMaskFilter_PaintOp: inflate_maskfilter(reader, p); break;
+            case kColorFilter_PaintOp: inflate_colorfilter(reader, p); break;
+            case kRasterizer_PaintOp: inflate_rasterizer(reader, p); break;
+            case kDrawLooper_PaintOp: inflate_drawlooper(reader, p); break;
+            default: SkASSERT(!"bad paintop"); return;
+        }
+    } while (!done);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void done_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState*) {}
+
+typedef void (*ReadProc)(SkCanvas*, SkReader32*, uint32_t op32, SkGPipeState*);
+
+static const ReadProc gReadTable[] = {
+    clipPath_rp,
+    clipRegion_rp,
+    clipRect_rp,
+    concat_rp,
+    drawBitmap_rp,
+    drawBitmapMatrix_rp,
+    drawBitmapRect_rp,
+    drawClear_rp,
+    drawData_rp,
+    drawPaint_rp,
+    drawPath_rp,
+    drawPicture_rp,
+    drawPoints_rp,
+    drawPosText_rp,
+    drawPosTextH_rp,
+    drawRect_rp,
+    drawShape_rp,
+    drawSprite_rp,
+    drawText_rp,
+    drawTextOnPath_rp,
+    drawVertices_rp,
+    restore_rp,
+    rotate_rp,
+    save_rp,
+    saveLayer_rp,
+    scale_rp,
+    setMatrix_rp,
+    skew_rp,
+    translate_rp,
+    paintOp_rp,
+    done_rp
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGPipeState::SkGPipeState() {
+    // start out with one paint in default state
+    *fPaints.append() = SkNEW(SkPaint);
+}
+
+SkGPipeState::~SkGPipeState() {
+    fPaints.deleteAll();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGPipe.h"
+
+SkGPipeReader::SkGPipeReader(SkCanvas* target) {
+    SkSafeRef(target);
+    fCanvas = target;
+    fState = NULL;
+}
+
+SkGPipeReader::~SkGPipeReader() {
+    SkSafeUnref(fCanvas);
+    delete fState;
+}
+
+SkGPipeReader::Status SkGPipeReader::playback(const void* data, size_t length) {
+    if (NULL == fCanvas) {
+        return kError_Status;
+    }
+
+    if (NULL == fState) {
+        fState = new SkGPipeState;
+    }
+
+    const ReadProc* table = gReadTable;
+    SkReader32 reader(data, length);
+    SkCanvas* canvas = fCanvas;
+    
+    while (!reader.eof()) {
+        uint32_t op32 = reader.readU32();
+        unsigned op = DrawOp_unpackOp(op32);
+        
+        if (op >= SK_ARRAY_COUNT(gReadTable)) {
+            SkDebugf("---- bad op during GPipeState::playback\n");
+            return kError_Status;
+        }
+        if (kDone_DrawOp == op) {
+            return kDone_Status;
+        }
+        table[op](canvas, &reader, op32, fState);
+    }
+    return kEOF_Status;
+}
+
+
diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp
new file mode 100644
index 0000000..a330713
--- /dev/null
+++ b/src/pipe/SkGPipeWrite.cpp
@@ -0,0 +1,539 @@
+/*
+    Copyright 2011 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkGPipePriv.h"
+#include "SkGPipeState.h"
+#include "SkWriter32.h"
+
+static void writeRegion(SkWriter32* writer, const SkRegion& rgn) {
+    size_t size = rgn.flatten(NULL);
+    SkASSERT(SkAlign4(size) == size);
+    rgn.flatten(writer->reserve(size));
+}
+
+static void writeMatrix(SkWriter32* writer, const SkMatrix& matrix) {
+    size_t size = matrix.flatten(NULL);
+    SkASSERT(SkAlign4(size) == size);
+    matrix.flatten(writer->reserve(size));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkGPipeCanvas : public SkCanvas {
+public:
+    SkGPipeCanvas(SkWriter32* writer);
+    virtual ~SkGPipeCanvas();
+
+    void finish() {
+        if (!fDone) {
+            this->writeOp(kDone_DrawOp);
+            fDone = true;
+        }
+    }
+
+    // overrides from SkCanvas
+    virtual int save(SaveFlags);
+    virtual int saveLayer(const SkRect* bounds, const SkPaint*, SaveFlags);
+    virtual void restore();
+    virtual bool translate(SkScalar dx, SkScalar dy);
+    virtual bool scale(SkScalar sx, SkScalar sy);
+    virtual bool rotate(SkScalar degrees);
+    virtual bool skew(SkScalar sx, SkScalar sy);
+    virtual bool concat(const SkMatrix& matrix);
+    virtual void setMatrix(const SkMatrix& matrix);
+    virtual bool clipRect(const SkRect& rect, SkRegion::Op op);
+    virtual bool clipPath(const SkPath& path, SkRegion::Op op);
+    virtual bool clipRegion(const SkRegion& region, SkRegion::Op op);
+    virtual void clear(SkColor);
+    virtual void drawPaint(const SkPaint& paint);
+    virtual void drawPoints(PointMode, size_t count, const SkPoint pts[],
+                            const SkPaint&);
+    virtual void drawRect(const SkRect& rect, const SkPaint&);
+    virtual void drawPath(const SkPath& path, const SkPaint&);
+    virtual void drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+                            const SkPaint*);
+    virtual void drawBitmapRect(const SkBitmap&, const SkIRect* src,
+                                const SkRect& dst, const SkPaint*);
+    virtual void drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
+                                  const SkPaint*);
+    virtual void drawSprite(const SkBitmap&, int left, int top,
+                            const SkPaint*);
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x, 
+                          SkScalar y, const SkPaint&);
+    virtual void drawPosText(const void* text, size_t byteLength, 
+                             const SkPoint pos[], const SkPaint&);
+    virtual void drawPosTextH(const void* text, size_t byteLength,
+                      const SkScalar xpos[], SkScalar constY, const SkPaint&);
+    virtual void drawTextOnPath(const void* text, size_t byteLength, 
+                            const SkPath& path, const SkMatrix* matrix, 
+                                const SkPaint&);
+    virtual void drawPicture(SkPicture& picture);
+    virtual void drawShape(SkShape*);
+    virtual void drawVertices(VertexMode, int vertexCount,
+                          const SkPoint vertices[], const SkPoint texs[],
+                          const SkColor colors[], SkXfermode*,
+                          const uint16_t indices[], int indexCount,
+                              const SkPaint&);
+    virtual void drawData(const void*, size_t);
+
+private:
+    SkWriter32& fWriter;
+    bool        fDone;
+
+    void writeOp(DrawOps op, unsigned flags, unsigned data) {
+        fWriter.write32(DrawOp_packOpFlagData(op, flags, data));
+    }
+    
+    void writeOp(DrawOps op) {
+        fWriter.write32(DrawOp_packOpFlagData(op, 0, 0));
+    }
+    
+    SkTDArray<SkPaint*> fPaints;
+    unsigned writePaint(const SkPaint&);
+
+    typedef SkCanvas INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+SkGPipeCanvas::SkGPipeCanvas(SkWriter32* writer) : fWriter(*writer) {
+    fDone = false;
+    // always begin with 1 default paint
+    *fPaints.append() = SkNEW(SkPaint);
+}
+
+SkGPipeCanvas::~SkGPipeCanvas() {
+    this->finish();
+
+    fPaints.deleteAll();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkGPipeCanvas::save(SaveFlags flags) {
+    this->writeOp(kSave_DrawOp, 0, flags);
+    return this->INHERITED::save(flags);
+}
+
+int SkGPipeCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                             SaveFlags saveFlags) {
+    unsigned index = 0; // just to avoid the warning
+    unsigned opFlags = 0;
+
+    if (bounds) {
+        opFlags |= kSaveLayer_HasBounds_DrawOpFlag;
+    }
+    if (paint) {
+        opFlags |= kSaveLayer_HasPaint_DrawOpFlag;
+        index = this->writePaint(*paint);
+    }
+
+    this->writeOp(kSaveLayer_DrawOp, opFlags, saveFlags);
+    if (bounds) {
+        fWriter.writeRect(*bounds);
+    }
+    if (paint) {
+        fWriter.write32(index);
+    }
+    
+    // we just pass on the save, so we don't create a layer
+    return this->INHERITED::save(saveFlags);
+}
+
+void SkGPipeCanvas::restore() {
+    this->writeOp(kRestore_DrawOp);
+    this->INHERITED::restore();
+}
+
+bool SkGPipeCanvas::translate(SkScalar dx, SkScalar dy) {
+    if (dx || dy) {
+        this->writeOp(kTranslate_DrawOp);
+        fWriter.writeScalar(dx);
+        fWriter.writeScalar(dy);
+    }
+    return this->INHERITED::translate(dx, dy);
+}
+
+bool SkGPipeCanvas::scale(SkScalar sx, SkScalar sy) {
+    if (sx || sy) {
+        this->writeOp(kScale_DrawOp);
+        fWriter.writeScalar(sx);
+        fWriter.writeScalar(sy);
+    }
+    return this->INHERITED::scale(sx, sy);
+}
+
+bool SkGPipeCanvas::rotate(SkScalar degrees) {
+    if (degrees) {
+        this->writeOp(kRotate_DrawOp);
+        fWriter.writeScalar(degrees);
+    }
+    return this->INHERITED::rotate(degrees);
+}
+
+bool SkGPipeCanvas::skew(SkScalar sx, SkScalar sy) {
+    if (sx || sy) {
+        this->writeOp(kSkew_DrawOp);
+        fWriter.writeScalar(sx);
+        fWriter.writeScalar(sy);
+    }
+    return this->INHERITED::skew(sx, sy);
+}
+
+bool SkGPipeCanvas::concat(const SkMatrix& matrix) {
+    if (!matrix.isIdentity()) {
+        this->writeOp(kConcat_DrawOp);
+        writeMatrix(&fWriter, matrix);
+    }
+    return this->INHERITED::concat(matrix);
+}
+
+void SkGPipeCanvas::setMatrix(const SkMatrix& matrix) {
+    this->writeOp(kSetMatrix_DrawOp);
+    writeMatrix(&fWriter, matrix);
+    this->INHERITED::setMatrix(matrix);
+}
+
+bool SkGPipeCanvas::clipRect(const SkRect& rect, SkRegion::Op rgnOp) {
+    this->writeOp(kClipRect_DrawOp, 0, rgnOp);
+    fWriter.writeRect(rect);
+    return this->INHERITED::clipRect(rect, rgnOp);
+}
+
+bool SkGPipeCanvas::clipPath(const SkPath& path, SkRegion::Op rgnOp) {
+    this->writeOp(kClipPath_DrawOp, 0, rgnOp);
+    path.flatten(fWriter);
+    // we just pass on the bounds of the path
+    return this->INHERITED::clipRect(path.getBounds(), rgnOp);
+}
+
+bool SkGPipeCanvas::clipRegion(const SkRegion& region, SkRegion::Op rgnOp) {
+    this->writeOp(kClipRegion_DrawOp, 0, rgnOp);
+    writeRegion(&fWriter, region);
+    return this->INHERITED::clipRegion(region, rgnOp);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGPipeCanvas::clear(SkColor color) {
+    unsigned flags = 0;
+    if (color) {
+        flags |= kClear_HasColor_DrawOpFlag;
+    }
+    this->writeOp(kDrawClear_DrawOp, flags, 0);
+    if (color) {
+        fWriter.write32(color);
+    }
+}
+
+void SkGPipeCanvas::drawPaint(const SkPaint& paint) {
+    unsigned paintIndex = this->writePaint(paint);
+    this->writeOp(kDrawPaint_DrawOp, 0, paintIndex);
+}
+
+void SkGPipeCanvas::drawPoints(PointMode mode, size_t count,
+                                   const SkPoint pts[], const SkPaint& paint) {
+    if (count) {
+        unsigned paintIndex = this->writePaint(paint);
+        this->writeOp(kDrawPoints_DrawOp, mode, paintIndex);
+        fWriter.write32(count);
+        fWriter.write(pts, count * sizeof(SkPoint));
+    }
+}
+
+void SkGPipeCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+    unsigned paintIndex = this->writePaint(paint);
+    this->writeOp(kDrawRect_DrawOp, 0, paintIndex);
+    fWriter.writeRect(rect);
+}
+
+void SkGPipeCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    unsigned paintIndex = this->writePaint(paint);
+    this->writeOp(kDrawPath_DrawOp, 0, paintIndex);
+    path.flatten(fWriter);
+}
+
+void SkGPipeCanvas::drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+                                   const SkPaint*) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawBitmapRect(const SkBitmap&, const SkIRect* src,
+                                       const SkRect& dst, const SkPaint*) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
+                                         const SkPaint*) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawSprite(const SkBitmap&, int left, int top,
+                                   const SkPaint*) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawText(const void* text, size_t byteLength, SkScalar x, 
+                                 SkScalar y, const SkPaint& paint) {
+    if (byteLength) {
+        unsigned paintIndex = this->writePaint(paint);
+        this->writeOp(kDrawText_DrawOp, 0, paintIndex);
+        fWriter.write32(byteLength);
+        fWriter.writePad(text, byteLength);
+        fWriter.writeScalar(x);
+        fWriter.writeScalar(y);
+    }
+}
+
+void SkGPipeCanvas::drawPosText(const void* text, size_t byteLength, 
+                                const SkPoint pos[], const SkPaint& paint) {
+    if (byteLength) {
+        unsigned paintIndex = this->writePaint(paint);
+        this->writeOp(kDrawPosText_DrawOp, 0, paintIndex);
+        fWriter.write32(byteLength);
+        fWriter.writePad(text, byteLength);
+        int count = paint.textToGlyphs(text, byteLength, NULL);
+        fWriter.write32(count);
+        fWriter.write(pos, count * sizeof(SkPoint));
+    }
+}
+
+void SkGPipeCanvas::drawPosTextH(const void* text, size_t byteLength,
+                                 const SkScalar xpos[], SkScalar constY,
+                                 const SkPaint& paint) {
+    if (byteLength) {
+        unsigned paintIndex = this->writePaint(paint);
+        this->writeOp(kDrawPosTextH_DrawOp, 0, paintIndex);
+        fWriter.write32(byteLength);
+        fWriter.writePad(text, byteLength);
+        int count = paint.textToGlyphs(text, byteLength, NULL);
+        fWriter.write32(count);
+        fWriter.write(xpos, count * sizeof(SkScalar));
+        fWriter.writeScalar(constY);
+    }
+}
+
+void SkGPipeCanvas::drawTextOnPath(const void* text, size_t byteLength, 
+                                   const SkPath& path, const SkMatrix* matrix, 
+                                   const SkPaint& paint) {
+    if (byteLength) {
+        unsigned flags = 0;
+        if (matrix) {
+            flags |= kDrawTextOnPath_HasMatrix_DrawOpFlag;
+        }
+        unsigned paintIndex = this->writePaint(paint);
+        this->writeOp(kDrawTextOnPath_DrawOp, flags, paintIndex);
+
+        fWriter.write32(byteLength);
+        fWriter.writePad(text, byteLength);
+
+        path.flatten(fWriter);
+        if (matrix) {
+            writeMatrix(&fWriter, *matrix);
+        }
+    }
+}
+
+void SkGPipeCanvas::drawPicture(SkPicture& picture) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawShape(SkShape* shape) {
+    UNIMPLEMENTED
+}
+
+void SkGPipeCanvas::drawVertices(VertexMode mode, int vertexCount,
+                                 const SkPoint vertices[], const SkPoint texs[],
+                                 const SkColor colors[], SkXfermode*,
+                                 const uint16_t indices[], int indexCount,
+                                 const SkPaint& paint) {
+    if (0 == vertexCount) {
+        return;
+    }
+
+    unsigned paintIndex = this->writePaint(paint);
+    unsigned flags = 0;
+    if (texs) {
+        flags |= kDrawVertices_HasTexs_DrawOpFlag;
+    }
+    if (colors) {
+        flags |= kDrawVertices_HasColors_DrawOpFlag;
+    }
+    if (indices && indexCount > 0) {
+        flags |= kDrawVertices_HasIndices_DrawOpFlag;
+    }
+    
+    this->writeOp(kDrawVertices_DrawOp, flags, paintIndex);
+    fWriter.write32(mode);
+    fWriter.write32(vertexCount);
+    fWriter.write(vertices, vertexCount * sizeof(SkPoint));
+    if (texs) {
+        fWriter.write(texs, vertexCount * sizeof(SkPoint));
+    }
+    if (colors) {
+        fWriter.write(colors, vertexCount * sizeof(SkColor));
+    }
+
+    // TODO: flatten xfermode
+
+    if (indices && indexCount > 0) {
+        fWriter.write32(indexCount);
+        fWriter.writePad(indices, indexCount * sizeof(uint16_t));
+    }
+}
+
+void SkGPipeCanvas::drawData(const void* data, size_t size) {
+    if (size) {
+        unsigned data = 0;
+        if (size < (1 << DRAWOPS_DATA_BITS)) {
+            data = (unsigned)size;
+        }
+        this->writeOp(kDrawData_DrawOp, 0, data);
+        if (0 == data) {
+            fWriter.write32(size);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> uint32_t castToU32(T value) {
+    union {
+        T           fSrc;
+        uint32_t    fDst;
+    } data;
+    data.fSrc = value;
+    return data.fDst;
+}
+
+unsigned SkGPipeCanvas::writePaint(const SkPaint& paint) {
+    const SkPaint& base = *fPaints[0];
+    uint32_t storage[32];
+    uint32_t* ptr = storage;
+    uint32_t* last = NULL;
+
+    if (base.getFlags() != paint.getFlags()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOpData(kFlags_PaintOp, paint.getFlags());
+    }
+    if (base.getColor() != paint.getColor()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOp(kColor_PaintOp);
+        *ptr++ = paint.getColor();
+    }
+    if (base.getStyle() != paint.getStyle()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOpData(kStyle_PaintOp, paint.getStyle());
+    }
+    if (base.getStrokeJoin() != paint.getStrokeJoin()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOpData(kJoin_PaintOp, paint.getStrokeJoin());
+    }
+    if (base.getStrokeCap() != paint.getStrokeCap()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOpData(kCap_PaintOp, paint.getStrokeCap());
+    }
+    if (base.getStrokeWidth() != paint.getStrokeWidth()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOp(kWidth_PaintOp);
+        *ptr++ = castToU32(paint.getStrokeWidth());
+    }
+    if (base.getStrokeMiter() != paint.getStrokeMiter()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOp(kMiter_PaintOp);
+        *ptr++ = castToU32(paint.getStrokeMiter());
+    }
+    if (base.getTextEncoding() != paint.getTextEncoding()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOpData(kEncoding_PaintOp, paint.getTextEncoding());
+    }
+    if (base.getHinting() != paint.getHinting()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOpData(kHinting_PaintOp, paint.getHinting());
+    }
+    if (base.getTextAlign() != paint.getTextAlign()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOpData(kAlign_PaintOp, paint.getTextAlign());
+    }
+    if (base.getTextSize() != paint.getTextSize()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOp(kTextSize_PaintOp);
+        *ptr++ = castToU32(paint.getTextSize());
+    }
+    if (base.getTextScaleX() != paint.getTextScaleX()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOp(kTextScaleX_PaintOp);
+        *ptr++ = castToU32(paint.getTextScaleX());
+    }
+    if (base.getTextSkewX() != paint.getTextSkewX()) {
+        last = ptr;
+        *ptr++ = PaintOp_packOp(kTextSkewX_PaintOp);
+        *ptr++ = castToU32(paint.getTextSkewX());
+    }
+    *fPaints[0] = paint;
+
+    if (ptr > storage) {
+        this->writeOp(kPaintOp_DrawOp, 0, 0);
+        size_t size = (char*)ptr - (char*)storage;
+        *last |= kLastOp_PaintOpFlag << PAINTOPS_DATA_BITS;
+        fWriter.write(storage, (char*)ptr - (char*)storage);
+        for (size_t i = 0; i < size/4; i++) {
+            SkDebugf("[%d] %08X\n", i, storage[i]);
+        }
+    }
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGPipe.h"
+
+#define MIN_WRITE_BLOCK_SIZE    (4 * 1024)
+
+SkGPipeWriter::SkGPipeWriter() : fWriter(MIN_WRITE_BLOCK_SIZE) {
+    fCanvas = NULL;
+}
+
+SkGPipeWriter::~SkGPipeWriter() {
+    SkSafeUnref(fCanvas);
+}
+
+SkCanvas* SkGPipeWriter::startRecording() {
+    if (NULL == fCanvas) {
+        fCanvas = SkNEW_ARGS(SkGPipeCanvas, (&fWriter));
+    }
+    return fCanvas;
+}
+
+void SkGPipeWriter::endRecording() {
+    if (fCanvas) {
+        fCanvas->finish();
+        fCanvas->unref();
+        fCanvas = NULL;
+    }
+}
+
+size_t SkGPipeWriter::flatten(void* buffer) {
+    if (buffer) {
+        fWriter.flatten(buffer);
+    }
+    return fWriter.size();
+}
+