Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2014 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkCanvas.h"
      9 #include "SkPatchUtils.h"
     10 #include "SkPictureData.h"
     11 #include "SkPicturePlayback.h"
     12 #include "SkPictureRecord.h"
     13 #include "SkReader32.h"
     14 #include "SkRSXform.h"
     15 #include "SkTextBlob.h"
     16 #include "SkTDArray.h"
     17 #include "SkTypes.h"
     18 
     19 // matches old SkCanvas::SaveFlags
     20 enum LegacySaveFlags {
     21     kHasAlphaLayer_LegacySaveFlags    = 0x04,
     22     kClipToLayer_LegacySaveFlags      = 0x10,
     23 };
     24 
     25 SkCanvas::SaveLayerFlags SkCanvas::LegacySaveFlagsToSaveLayerFlags(uint32_t flags) {
     26     uint32_t layerFlags = 0;
     27 
     28     if (0 == (flags & kClipToLayer_LegacySaveFlags)) {
     29         layerFlags |= SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag;
     30     }
     31     if (0 == (flags & kHasAlphaLayer_LegacySaveFlags)) {
     32         layerFlags |= kIsOpaque_SaveLayerFlag;
     33     }
     34     return layerFlags;
     35 }
     36 
     37 /*
     38  * Read the next op code and chunk size from 'reader'. The returned size
     39  * is the entire size of the chunk (including the opcode). Thus, the
     40  * offset just prior to calling ReadOpAndSize + 'size' is the offset
     41  * to the next chunk's op code. This also means that the size of a chunk
     42  * with no arguments (just an opcode) will be 4.
     43  */
     44 DrawType SkPicturePlayback::ReadOpAndSize(SkReader32* reader, uint32_t* size) {
     45     uint32_t temp = reader->readInt();
     46     uint32_t op;
     47     if (((uint8_t)temp) == temp) {
     48         // old skp file - no size information
     49         op = temp;
     50         *size = 0;
     51     } else {
     52         UNPACK_8_24(temp, op, *size);
     53         if (MASK_24 == *size) {
     54             *size = reader->readInt();
     55         }
     56     }
     57     return (DrawType)op;
     58 }
     59 
     60 
     61 static const SkRect* get_rect_ptr(SkReader32* reader) {
     62     if (reader->readBool()) {
     63         return &reader->skipT<SkRect>();
     64     } else {
     65         return nullptr;
     66     }
     67 }
     68 
     69 class TextContainer {
     70 public:
     71     size_t length() { return fByteLength; }
     72     const void* text() { return (const void*)fText; }
     73     size_t fByteLength;
     74     const char* fText;
     75 };
     76 
     77 void get_text(SkReader32* reader, TextContainer* text) {
     78     size_t length = text->fByteLength = reader->readInt();
     79     text->fText = (const char*)reader->skip(length);
     80 }
     81 
     82 // FIXME: SkBitmaps are stateful, so we need to copy them to play back in multiple threads.
     83 static SkBitmap shallow_copy(const SkBitmap& bitmap) {
     84     return bitmap;
     85 }
     86 
     87 void SkPicturePlayback::draw(SkCanvas* canvas, SkPicture::AbortCallback* callback) {
     88     AutoResetOpID aroi(this);
     89     SkASSERT(0 == fCurOffset);
     90 
     91     SkReader32 reader(fPictureData->opData()->bytes(), fPictureData->opData()->size());
     92 
     93     // Record this, so we can concat w/ it if we encounter a setMatrix()
     94     SkMatrix initialMatrix = canvas->getTotalMatrix();
     95 
     96     SkAutoCanvasRestore acr(canvas, false);
     97 
     98     while (!reader.eof()) {
     99         if (callback && callback->abort()) {
    100             return;
    101         }
    102 
    103         fCurOffset = reader.offset();
    104         uint32_t size;
    105         DrawType op = ReadOpAndSize(&reader, &size);
    106 
    107         this->handleOp(&reader, op, size, canvas, initialMatrix);
    108     }
    109 }
    110 
    111 void SkPicturePlayback::handleOp(SkReader32* reader,
    112                                  DrawType op,
    113                                  uint32_t size,
    114                                  SkCanvas* canvas,
    115                                  const SkMatrix& initialMatrix) {
    116     switch (op) {
    117         case NOOP: {
    118             SkASSERT(size >= 4);
    119             reader->skip(size - 4);
    120         } break;
    121         case CLIP_PATH: {
    122             const SkPath& path = fPictureData->getPath(reader);
    123             uint32_t packed = reader->readInt();
    124             SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed);
    125             bool doAA = ClipParams_unpackDoAA(packed);
    126             size_t offsetToRestore = reader->readInt();
    127             SkASSERT(!offsetToRestore || offsetToRestore >= reader->offset());
    128             canvas->clipPath(path, regionOp, doAA);
    129             if (canvas->isClipEmpty() && offsetToRestore) {
    130                 reader->setOffset(offsetToRestore);
    131             }
    132         } break;
    133         case CLIP_REGION: {
    134             SkRegion region;
    135             reader->readRegion(&region);
    136             uint32_t packed = reader->readInt();
    137             SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed);
    138             size_t offsetToRestore = reader->readInt();
    139             SkASSERT(!offsetToRestore || offsetToRestore >= reader->offset());
    140             canvas->clipRegion(region, regionOp);
    141             if (canvas->isClipEmpty() && offsetToRestore) {
    142                 reader->setOffset(offsetToRestore);
    143             }
    144         } break;
    145         case CLIP_RECT: {
    146             const SkRect& rect = reader->skipT<SkRect>();
    147             uint32_t packed = reader->readInt();
    148             SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed);
    149             bool doAA = ClipParams_unpackDoAA(packed);
    150             size_t offsetToRestore = reader->readInt();
    151             SkASSERT(!offsetToRestore || offsetToRestore >= reader->offset());
    152             canvas->clipRect(rect, regionOp, doAA);
    153             if (canvas->isClipEmpty() && offsetToRestore) {
    154                 reader->setOffset(offsetToRestore);
    155             }
    156         } break;
    157         case CLIP_RRECT: {
    158             SkRRect rrect;
    159             reader->readRRect(&rrect);
    160             uint32_t packed = reader->readInt();
    161             SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed);
    162             bool doAA = ClipParams_unpackDoAA(packed);
    163             size_t offsetToRestore = reader->readInt();
    164             SkASSERT(!offsetToRestore || offsetToRestore >= reader->offset());
    165             canvas->clipRRect(rrect, regionOp, doAA);
    166             if (canvas->isClipEmpty() && offsetToRestore) {
    167                 reader->setOffset(offsetToRestore);
    168             }
    169         } break;
    170         case PUSH_CULL: break;  // Deprecated, safe to ignore both push and pop.
    171         case POP_CULL:  break;
    172         case CONCAT: {
    173             SkMatrix matrix;
    174             reader->readMatrix(&matrix);
    175             canvas->concat(matrix);
    176             break;
    177         }
    178         case DRAW_ATLAS: {
    179             const SkPaint* paint = fPictureData->getPaint(reader);
    180             const SkImage* atlas = fPictureData->getImage(reader);
    181             const uint32_t flags = reader->readU32();
    182             const int count = reader->readU32();
    183             const SkRSXform* xform = (const SkRSXform*)reader->skip(count * sizeof(SkRSXform));
    184             const SkRect* tex = (const SkRect*)reader->skip(count * sizeof(SkRect));
    185             const SkColor* colors = nullptr;
    186             SkXfermode::Mode mode = SkXfermode::kDst_Mode;
    187             if (flags & DRAW_ATLAS_HAS_COLORS) {
    188                 colors = (const SkColor*)reader->skip(count * sizeof(SkColor));
    189                 mode = (SkXfermode::Mode)reader->readU32();
    190             }
    191             const SkRect* cull = nullptr;
    192             if (flags & DRAW_ATLAS_HAS_CULL) {
    193                 cull = (const SkRect*)reader->skip(sizeof(SkRect));
    194             }
    195             canvas->drawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
    196         } break;
    197         case DRAW_BITMAP: {
    198             const SkPaint* paint = fPictureData->getPaint(reader);
    199             const SkBitmap bitmap = shallow_copy(fPictureData->getBitmap(reader));
    200             const SkPoint& loc = reader->skipT<SkPoint>();
    201             canvas->drawBitmap(bitmap, loc.fX, loc.fY, paint);
    202         } break;
    203         case DRAW_BITMAP_RECT: {
    204             const SkPaint* paint = fPictureData->getPaint(reader);
    205             const SkBitmap bitmap = shallow_copy(fPictureData->getBitmap(reader));
    206             const SkRect* src = get_rect_ptr(reader);   // may be null
    207             const SkRect& dst = reader->skipT<SkRect>();     // required
    208             SkCanvas::SrcRectConstraint constraint = (SkCanvas::SrcRectConstraint)reader->readInt();
    209             canvas->legacy_drawBitmapRect(bitmap, src, dst, paint, constraint);
    210         } break;
    211         case DRAW_BITMAP_MATRIX: {
    212             const SkPaint* paint = fPictureData->getPaint(reader);
    213             const SkBitmap bitmap = shallow_copy(fPictureData->getBitmap(reader));
    214             SkMatrix matrix;
    215             reader->readMatrix(&matrix);
    216 
    217             SkAutoCanvasRestore acr(canvas, true);
    218             canvas->concat(matrix);
    219             canvas->drawBitmap(bitmap, 0, 0, paint);
    220         } break;
    221         case DRAW_BITMAP_NINE: {
    222             const SkPaint* paint = fPictureData->getPaint(reader);
    223             const SkBitmap bitmap = shallow_copy(fPictureData->getBitmap(reader));
    224             const SkIRect& src = reader->skipT<SkIRect>();
    225             const SkRect& dst = reader->skipT<SkRect>();
    226             canvas->drawBitmapNine(bitmap, src, dst, paint);
    227         } break;
    228         case DRAW_CLEAR:
    229             canvas->clear(reader->readInt());
    230             break;
    231         case DRAW_DATA: {
    232             // This opcode is now dead, just need to skip it for backwards compatibility
    233             size_t length = reader->readInt();
    234             (void)reader->skip(length);
    235             // skip handles padding the read out to a multiple of 4
    236         } break;
    237         case DRAW_DRRECT: {
    238             const SkPaint& paint = *fPictureData->getPaint(reader);
    239             SkRRect outer, inner;
    240             reader->readRRect(&outer);
    241             reader->readRRect(&inner);
    242             canvas->drawDRRect(outer, inner, paint);
    243         } break;
    244         case BEGIN_COMMENT_GROUP:
    245             reader->readString();
    246             // deprecated (M44)
    247             break;
    248         case COMMENT:
    249             reader->readString();
    250             reader->readString();
    251             // deprecated (M44)
    252             break;
    253         case END_COMMENT_GROUP:
    254             // deprecated (M44)
    255             break;
    256         case DRAW_IMAGE: {
    257             const SkPaint* paint = fPictureData->getPaint(reader);
    258             const SkImage* image = fPictureData->getImage(reader);
    259             const SkPoint& loc = reader->skipT<SkPoint>();
    260             canvas->drawImage(image, loc.fX, loc.fY, paint);
    261         } break;
    262         case DRAW_IMAGE_NINE: {
    263             const SkPaint* paint = fPictureData->getPaint(reader);
    264             const SkImage* image = fPictureData->getImage(reader);
    265             const SkIRect& center = reader->skipT<SkIRect>();
    266             const SkRect& dst = reader->skipT<SkRect>();
    267             canvas->drawImageNine(image, center, dst, paint);
    268         } break;
    269         case DRAW_IMAGE_RECT_STRICT:
    270         case DRAW_IMAGE_RECT: {
    271             const SkPaint* paint = fPictureData->getPaint(reader);
    272             const SkImage* image = fPictureData->getImage(reader);
    273             const SkRect* src = get_rect_ptr(reader);   // may be null
    274             const SkRect& dst = reader->skipT<SkRect>();     // required
    275             // DRAW_IMAGE_RECT_STRICT assumes this constraint, and doesn't store it
    276             SkCanvas::SrcRectConstraint constraint = SkCanvas::kStrict_SrcRectConstraint;
    277             if (DRAW_IMAGE_RECT == op) {
    278                 // newer op-code stores the constraint explicitly
    279                 constraint = (SkCanvas::SrcRectConstraint)reader->readInt();
    280             }
    281             canvas->legacy_drawImageRect(image, src, dst, paint, constraint);
    282         } break;
    283         case DRAW_OVAL: {
    284             const SkPaint& paint = *fPictureData->getPaint(reader);
    285             canvas->drawOval(reader->skipT<SkRect>(), paint);
    286         } break;
    287         case DRAW_PAINT:
    288             canvas->drawPaint(*fPictureData->getPaint(reader));
    289             break;
    290         case DRAW_PATCH: {
    291             const SkPaint& paint = *fPictureData->getPaint(reader);
    292 
    293             const SkPoint* cubics = (const SkPoint*)reader->skip(SkPatchUtils::kNumCtrlPts *
    294                                                                  sizeof(SkPoint));
    295             uint32_t flag = reader->readInt();
    296             const SkColor* colors = nullptr;
    297             if (flag & DRAW_VERTICES_HAS_COLORS) {
    298                 colors = (const SkColor*)reader->skip(SkPatchUtils::kNumCorners * sizeof(SkColor));
    299             }
    300             const SkPoint* texCoords = nullptr;
    301             if (flag & DRAW_VERTICES_HAS_TEXS) {
    302                 texCoords = (const SkPoint*)reader->skip(SkPatchUtils::kNumCorners *
    303                                                          sizeof(SkPoint));
    304             }
    305             SkAutoTUnref<SkXfermode> xfer;
    306             if (flag & DRAW_VERTICES_HAS_XFER) {
    307                 int mode = reader->readInt();
    308                 if (mode < 0 || mode > SkXfermode::kLastMode) {
    309                     mode = SkXfermode::kModulate_Mode;
    310                 }
    311                 xfer.reset(SkXfermode::Create((SkXfermode::Mode)mode));
    312             }
    313             canvas->drawPatch(cubics, colors, texCoords, xfer, paint);
    314         } break;
    315         case DRAW_PATH: {
    316             const SkPaint& paint = *fPictureData->getPaint(reader);
    317             canvas->drawPath(fPictureData->getPath(reader), paint);
    318         } break;
    319         case DRAW_PICTURE:
    320             canvas->drawPicture(fPictureData->getPicture(reader));
    321             break;
    322         case DRAW_PICTURE_MATRIX_PAINT: {
    323             const SkPaint* paint = fPictureData->getPaint(reader);
    324             SkMatrix matrix;
    325             reader->readMatrix(&matrix);
    326             const SkPicture* pic = fPictureData->getPicture(reader);
    327             canvas->drawPicture(pic, &matrix, paint);
    328         } break;
    329         case DRAW_POINTS: {
    330             const SkPaint& paint = *fPictureData->getPaint(reader);
    331             SkCanvas::PointMode mode = (SkCanvas::PointMode)reader->readInt();
    332             size_t count = reader->readInt();
    333             const SkPoint* pts = (const SkPoint*)reader->skip(sizeof(SkPoint)* count);
    334             canvas->drawPoints(mode, count, pts, paint);
    335         } break;
    336         case DRAW_POS_TEXT: {
    337             const SkPaint& paint = *fPictureData->getPaint(reader);
    338             TextContainer text;
    339             get_text(reader, &text);
    340             size_t points = reader->readInt();
    341             const SkPoint* pos = (const SkPoint*)reader->skip(points * sizeof(SkPoint));
    342             canvas->drawPosText(text.text(), text.length(), pos, paint);
    343         } break;
    344         case DRAW_POS_TEXT_TOP_BOTTOM: {
    345             const SkPaint& paint = *fPictureData->getPaint(reader);
    346             TextContainer text;
    347             get_text(reader, &text);
    348             size_t points = reader->readInt();
    349             const SkPoint* pos = (const SkPoint*)reader->skip(points * sizeof(SkPoint));
    350             const SkScalar top = reader->readScalar();
    351             const SkScalar bottom = reader->readScalar();
    352             if (!canvas->quickRejectY(top, bottom)) {
    353                 canvas->drawPosText(text.text(), text.length(), pos, paint);
    354             }
    355         } break;
    356         case DRAW_POS_TEXT_H: {
    357             const SkPaint& paint = *fPictureData->getPaint(reader);
    358             TextContainer text;
    359             get_text(reader, &text);
    360             size_t xCount = reader->readInt();
    361             const SkScalar constY = reader->readScalar();
    362             const SkScalar* xpos = (const SkScalar*)reader->skip(xCount * sizeof(SkScalar));
    363             canvas->drawPosTextH(text.text(), text.length(), xpos, constY, paint);
    364         } break;
    365         case DRAW_POS_TEXT_H_TOP_BOTTOM: {
    366             const SkPaint& paint = *fPictureData->getPaint(reader);
    367             TextContainer text;
    368             get_text(reader, &text);
    369             size_t xCount = reader->readInt();
    370             const SkScalar* xpos = (const SkScalar*)reader->skip((3 + xCount) * sizeof(SkScalar));
    371             const SkScalar top = *xpos++;
    372             const SkScalar bottom = *xpos++;
    373             const SkScalar constY = *xpos++;
    374             if (!canvas->quickRejectY(top, bottom)) {
    375                 canvas->drawPosTextH(text.text(), text.length(), xpos, constY, paint);
    376             }
    377         } break;
    378         case DRAW_RECT: {
    379             const SkPaint& paint = *fPictureData->getPaint(reader);
    380             canvas->drawRect(reader->skipT<SkRect>(), paint);
    381         } break;
    382         case DRAW_RRECT: {
    383             const SkPaint& paint = *fPictureData->getPaint(reader);
    384             SkRRect rrect;
    385             reader->readRRect(&rrect);
    386             canvas->drawRRect(rrect, paint);
    387         } break;
    388         case DRAW_SPRITE: {
    389             /* const SkPaint* paint = */ fPictureData->getPaint(reader);
    390             /* const SkBitmap bitmap = */ shallow_copy(fPictureData->getBitmap(reader));
    391             /* int left = */ reader->readInt();
    392             /* int top = */ reader->readInt();
    393             // drawSprite removed dec-2015
    394         } break;
    395         case DRAW_TEXT: {
    396             const SkPaint& paint = *fPictureData->getPaint(reader);
    397             TextContainer text;
    398             get_text(reader, &text);
    399             SkScalar x = reader->readScalar();
    400             SkScalar y = reader->readScalar();
    401             canvas->drawText(text.text(), text.length(), x, y, paint);
    402         } break;
    403         case DRAW_TEXT_BLOB: {
    404             const SkPaint& paint = *fPictureData->getPaint(reader);
    405             const SkTextBlob* blob = fPictureData->getTextBlob(reader);
    406             SkScalar x = reader->readScalar();
    407             SkScalar y = reader->readScalar();
    408             canvas->drawTextBlob(blob, x, y, paint);
    409         } break;
    410         case DRAW_TEXT_TOP_BOTTOM: {
    411             const SkPaint& paint = *fPictureData->getPaint(reader);
    412             TextContainer text;
    413             get_text(reader, &text);
    414             const SkScalar* ptr = (const SkScalar*)reader->skip(4 * sizeof(SkScalar));
    415             // ptr[0] == x
    416             // ptr[1] == y
    417             // ptr[2] == top
    418             // ptr[3] == bottom
    419             if (!canvas->quickRejectY(ptr[2], ptr[3])) {
    420                 canvas->drawText(text.text(), text.length(), ptr[0], ptr[1], paint);
    421             }
    422         } break;
    423         case DRAW_TEXT_ON_PATH: {
    424             const SkPaint& paint = *fPictureData->getPaint(reader);
    425             TextContainer text;
    426             get_text(reader, &text);
    427             const SkPath& path = fPictureData->getPath(reader);
    428             SkMatrix matrix;
    429             reader->readMatrix(&matrix);
    430             canvas->drawTextOnPath(text.text(), text.length(), path, &matrix, paint);
    431         } break;
    432         case DRAW_VERTICES: {
    433             SkAutoTUnref<SkXfermode> xfer;
    434             const SkPaint& paint = *fPictureData->getPaint(reader);
    435             DrawVertexFlags flags = (DrawVertexFlags)reader->readInt();
    436             SkCanvas::VertexMode vmode = (SkCanvas::VertexMode)reader->readInt();
    437             int vCount = reader->readInt();
    438             const SkPoint* verts = (const SkPoint*)reader->skip(vCount * sizeof(SkPoint));
    439             const SkPoint* texs = nullptr;
    440             const SkColor* colors = nullptr;
    441             const uint16_t* indices = nullptr;
    442             int iCount = 0;
    443             if (flags & DRAW_VERTICES_HAS_TEXS) {
    444                 texs = (const SkPoint*)reader->skip(vCount * sizeof(SkPoint));
    445             }
    446             if (flags & DRAW_VERTICES_HAS_COLORS) {
    447                 colors = (const SkColor*)reader->skip(vCount * sizeof(SkColor));
    448             }
    449             if (flags & DRAW_VERTICES_HAS_INDICES) {
    450                 iCount = reader->readInt();
    451                 indices = (const uint16_t*)reader->skip(iCount * sizeof(uint16_t));
    452             }
    453             if (flags & DRAW_VERTICES_HAS_XFER) {
    454                 int mode = reader->readInt();
    455                 if (mode < 0 || mode > SkXfermode::kLastMode) {
    456                     mode = SkXfermode::kModulate_Mode;
    457                 }
    458                 xfer.reset(SkXfermode::Create((SkXfermode::Mode)mode));
    459             }
    460             canvas->drawVertices(vmode, vCount, verts, texs, colors, xfer, indices, iCount, paint);
    461         } break;
    462         case RESTORE:
    463             canvas->restore();
    464             break;
    465         case ROTATE:
    466             canvas->rotate(reader->readScalar());
    467             break;
    468         case SAVE:
    469             // SKPs with version < 29 also store a SaveFlags param.
    470             if (size > 4) {
    471                 SkASSERT(8 == size);
    472                 reader->readInt();
    473             }
    474             canvas->save();
    475             break;
    476         case SAVE_LAYER_SAVEFLAGS_DEPRECATED: {
    477             const SkRect* boundsPtr = get_rect_ptr(reader);
    478             const SkPaint* paint = fPictureData->getPaint(reader);
    479             auto flags = SkCanvas::LegacySaveFlagsToSaveLayerFlags(reader->readInt());
    480             canvas->saveLayer(SkCanvas::SaveLayerRec(boundsPtr, paint, flags));
    481         } break;
    482         case SAVE_LAYER_SAVELAYERFLAGS_DEPRECATED_JAN_2016: {
    483             const SkRect* boundsPtr = get_rect_ptr(reader);
    484             const SkPaint* paint = fPictureData->getPaint(reader);
    485             canvas->saveLayer(SkCanvas::SaveLayerRec(boundsPtr, paint, reader->readInt()));
    486         } break;
    487         case SAVE_LAYER_SAVELAYERREC: {
    488             SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, 0);
    489             const uint32_t flatFlags = reader->readInt();
    490             if (flatFlags & SAVELAYERREC_HAS_BOUNDS) {
    491                 rec.fBounds = &reader->skipT<SkRect>();
    492             }
    493             if (flatFlags & SAVELAYERREC_HAS_PAINT) {
    494                 rec.fPaint = fPictureData->getPaint(reader);
    495             }
    496             if (flatFlags & SAVELAYERREC_HAS_BACKDROP) {
    497                 const SkPaint* paint = fPictureData->getPaint(reader);
    498                 rec.fBackdrop = paint->getImageFilter();
    499             }
    500             if (flatFlags & SAVELAYERREC_HAS_FLAGS) {
    501                 rec.fSaveLayerFlags = reader->readInt();
    502             }
    503             canvas->saveLayer(rec);
    504         } break;
    505         case SCALE: {
    506             SkScalar sx = reader->readScalar();
    507             SkScalar sy = reader->readScalar();
    508             canvas->scale(sx, sy);
    509         } break;
    510         case SET_MATRIX: {
    511             SkMatrix matrix;
    512             reader->readMatrix(&matrix);
    513             matrix.postConcat(initialMatrix);
    514             canvas->setMatrix(matrix);
    515         } break;
    516         case SKEW: {
    517             SkScalar sx = reader->readScalar();
    518             SkScalar sy = reader->readScalar();
    519             canvas->skew(sx, sy);
    520         } break;
    521         case TRANSLATE: {
    522             SkScalar dx = reader->readScalar();
    523             SkScalar dy = reader->readScalar();
    524             canvas->translate(dx, dy);
    525         } break;
    526         default:
    527             SkASSERTF(false, "Unknown draw type: %d", op);
    528     }
    529 }
    530 
    531