michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkPictureRecord.h" michael@0: #include "SkTSearch.h" michael@0: #include "SkPixelRef.h" michael@0: #include "SkRRect.h" michael@0: #include "SkBBoxHierarchy.h" michael@0: #include "SkDevice.h" michael@0: #include "SkOffsetTable.h" michael@0: #include "SkPictureStateTree.h" michael@0: #include "SkSurface.h" michael@0: michael@0: #define HEAP_BLOCK_SIZE 4096 michael@0: michael@0: enum { michael@0: // just need a value that save or getSaveCount would never return michael@0: kNoInitialSave = -1, michael@0: }; michael@0: michael@0: // A lot of basic types get stored as a uint32_t: bools, ints, paint indices, etc. michael@0: static int const kUInt32Size = 4; michael@0: michael@0: static const uint32_t kSaveSize = 2 * kUInt32Size; michael@0: static const uint32_t kSaveLayerNoBoundsSize = 4 * kUInt32Size; michael@0: static const uint32_t kSaveLayerWithBoundsSize = 4 * kUInt32Size + sizeof(SkRect); michael@0: michael@0: SkPictureRecord::SkPictureRecord(const SkISize& dimensions, uint32_t flags) michael@0: : INHERITED(dimensions.width(), dimensions.height()) michael@0: , fBoundingHierarchy(NULL) michael@0: , fStateTree(NULL) michael@0: , fFlattenableHeap(HEAP_BLOCK_SIZE) michael@0: , fPaints(&fFlattenableHeap) michael@0: , fRecordFlags(flags) michael@0: , fOptsEnabled(true) { michael@0: #ifdef SK_DEBUG_SIZE michael@0: fPointBytes = fRectBytes = fTextBytes = 0; michael@0: fPointWrites = fRectWrites = fTextWrites = 0; michael@0: #endif michael@0: michael@0: fBitmapHeap = SkNEW(SkBitmapHeap); michael@0: fFlattenableHeap.setBitmapStorage(fBitmapHeap); michael@0: fPathHeap = NULL; // lazy allocate michael@0: michael@0: #ifndef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fFirstSavedLayerIndex = kNoSavedLayerIndex; michael@0: #endif michael@0: michael@0: fInitialSaveCount = kNoInitialSave; michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.init(this); michael@0: #endif michael@0: } michael@0: michael@0: SkPictureRecord::~SkPictureRecord() { michael@0: SkSafeUnref(fBitmapHeap); michael@0: SkSafeUnref(fPathHeap); michael@0: SkSafeUnref(fBoundingHierarchy); michael@0: SkSafeUnref(fStateTree); michael@0: fFlattenableHeap.setBitmapStorage(NULL); michael@0: fPictureRefs.unrefAll(); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: // Return the offset of the paint inside a given op's byte stream. A zero michael@0: // return value means there is no paint (and you really shouldn't be calling michael@0: // this method) michael@0: static inline uint32_t getPaintOffset(DrawType op, uint32_t opSize) { michael@0: // These offsets are where the paint would be if the op size doesn't overflow michael@0: static const uint8_t gPaintOffsets[LAST_DRAWTYPE_ENUM + 1] = { michael@0: 0, // UNUSED - no paint michael@0: 0, // CLIP_PATH - no paint michael@0: 0, // CLIP_REGION - no paint michael@0: 0, // CLIP_RECT - no paint michael@0: 0, // CLIP_RRECT - no paint michael@0: 0, // CONCAT - no paint michael@0: 1, // DRAW_BITMAP - right after op code michael@0: 1, // DRAW_BITMAP_MATRIX - right after op code michael@0: 1, // DRAW_BITMAP_NINE - right after op code michael@0: 1, // DRAW_BITMAP_RECT_TO_RECT - right after op code michael@0: 0, // DRAW_CLEAR - no paint michael@0: 0, // DRAW_DATA - no paint michael@0: 1, // DRAW_OVAL - right after op code michael@0: 1, // DRAW_PAINT - right after op code michael@0: 1, // DRAW_PATH - right after op code michael@0: 0, // DRAW_PICTURE - no paint michael@0: 1, // DRAW_POINTS - right after op code michael@0: 1, // DRAW_POS_TEXT - right after op code michael@0: 1, // DRAW_POS_TEXT_TOP_BOTTOM - right after op code michael@0: 1, // DRAW_POS_TEXT_H - right after op code michael@0: 1, // DRAW_POS_TEXT_H_TOP_BOTTOM - right after op code michael@0: 1, // DRAW_RECT - right after op code michael@0: 1, // DRAW_RRECT - right after op code michael@0: 1, // DRAW_SPRITE - right after op code michael@0: 1, // DRAW_TEXT - right after op code michael@0: 1, // DRAW_TEXT_ON_PATH - right after op code michael@0: 1, // DRAW_TEXT_TOP_BOTTOM - right after op code michael@0: 1, // DRAW_VERTICES - right after op code michael@0: 0, // RESTORE - no paint michael@0: 0, // ROTATE - no paint michael@0: 0, // SAVE - no paint michael@0: 0, // SAVE_LAYER - see below - this paint's location varies michael@0: 0, // SCALE - no paint michael@0: 0, // SET_MATRIX - no paint michael@0: 0, // SKEW - no paint michael@0: 0, // TRANSLATE - no paint michael@0: 0, // NOOP - no paint michael@0: 0, // BEGIN_GROUP - no paint michael@0: 0, // COMMENT - no paint michael@0: 0, // END_GROUP - no paint michael@0: 1, // DRAWDRRECT - right after op code michael@0: 0, // PUSH_CULL - no paint michael@0: 0, // POP_CULL - no paint michael@0: }; michael@0: michael@0: SK_COMPILE_ASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1, michael@0: need_to_be_in_sync); michael@0: SkASSERT((unsigned)op <= (unsigned)LAST_DRAWTYPE_ENUM); michael@0: michael@0: int overflow = 0; michael@0: if (0 != (opSize & ~MASK_24) || opSize == MASK_24) { michael@0: // This op's size overflows so an extra uint32_t will be written michael@0: // after the op code michael@0: overflow = sizeof(uint32_t); michael@0: } michael@0: michael@0: if (SAVE_LAYER == op) { michael@0: static const uint32_t kSaveLayerNoBoundsPaintOffset = 2 * kUInt32Size; michael@0: static const uint32_t kSaveLayerWithBoundsPaintOffset = 2 * kUInt32Size + sizeof(SkRect); michael@0: michael@0: if (kSaveLayerNoBoundsSize == opSize) { michael@0: return kSaveLayerNoBoundsPaintOffset + overflow; michael@0: } else { michael@0: SkASSERT(kSaveLayerWithBoundsSize == opSize); michael@0: return kSaveLayerWithBoundsPaintOffset + overflow; michael@0: } michael@0: } michael@0: michael@0: SkASSERT(0 != gPaintOffsets[op]); // really shouldn't be calling this method michael@0: return gPaintOffsets[op] * sizeof(uint32_t) + overflow; michael@0: } michael@0: michael@0: void SkPictureRecord::willSave(SaveFlags flags) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.save(flags); michael@0: #else michael@0: // record the offset to us, making it non-positive to distinguish a save michael@0: // from a clip entry. michael@0: fRestoreOffsetStack.push(-(int32_t)fWriter.bytesWritten()); michael@0: this->recordSave(flags); michael@0: #endif michael@0: michael@0: this->INHERITED::willSave(flags); michael@0: } michael@0: michael@0: void SkPictureRecord::recordSave(SaveFlags flags) { michael@0: // op + flags michael@0: uint32_t size = kSaveSize; michael@0: size_t initialOffset = this->addDraw(SAVE, &size); michael@0: this->addInt(flags); michael@0: michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: SkCanvas::SaveLayerStrategy SkPictureRecord::willSaveLayer(const SkRect* bounds, michael@0: const SkPaint* paint, SaveFlags flags) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.saveLayer(bounds, paint, flags); michael@0: #else michael@0: // record the offset to us, making it non-positive to distinguish a save michael@0: // from a clip entry. michael@0: fRestoreOffsetStack.push(-(int32_t)fWriter.bytesWritten()); michael@0: this->recordSaveLayer(bounds, paint, flags); michael@0: if (kNoSavedLayerIndex == fFirstSavedLayerIndex) { michael@0: fFirstSavedLayerIndex = fRestoreOffsetStack.count(); michael@0: } michael@0: #endif michael@0: michael@0: this->INHERITED::willSaveLayer(bounds, paint, flags); michael@0: /* No need for a (potentially very big) layer which we don't actually need michael@0: at this time (and may not be able to afford since during record our michael@0: clip starts out the size of the picture, which is often much larger michael@0: than the size of the actual device we'll use during playback). michael@0: */ michael@0: return kNoLayer_SaveLayerStrategy; michael@0: } michael@0: michael@0: void SkPictureRecord::recordSaveLayer(const SkRect* bounds, const SkPaint* paint, michael@0: SaveFlags flags) { michael@0: // op + bool for 'bounds' michael@0: uint32_t size = 2 * kUInt32Size; michael@0: if (NULL != bounds) { michael@0: size += sizeof(*bounds); // + rect michael@0: } michael@0: // + paint index + flags michael@0: size += 2 * kUInt32Size; michael@0: michael@0: SkASSERT(kSaveLayerNoBoundsSize == size || kSaveLayerWithBoundsSize == size); michael@0: michael@0: size_t initialOffset = this->addDraw(SAVE_LAYER, &size); michael@0: this->addRectPtr(bounds); michael@0: SkASSERT(initialOffset+getPaintOffset(SAVE_LAYER, size) == fWriter.bytesWritten()); michael@0: this->addPaintPtr(paint); michael@0: this->addInt(flags); michael@0: michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: bool SkPictureRecord::isDrawingToLayer() const { michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: return fMCMgr.isDrawingToLayer(); michael@0: #else michael@0: return fFirstSavedLayerIndex != kNoSavedLayerIndex; michael@0: #endif michael@0: } michael@0: michael@0: /* michael@0: * Read the op code from 'offset' in 'writer'. michael@0: */ michael@0: #ifdef SK_DEBUG michael@0: static DrawType peek_op(SkWriter32* writer, int32_t offset) { michael@0: return (DrawType)(writer->readTAt(offset) >> 24); michael@0: } michael@0: #endif michael@0: michael@0: /* michael@0: * Read the op code from 'offset' in 'writer' and extract the size too. michael@0: */ michael@0: static DrawType peek_op_and_size(SkWriter32* writer, int32_t offset, uint32_t* size) { michael@0: uint32_t peek = writer->readTAt(offset); michael@0: michael@0: uint32_t op; michael@0: UNPACK_8_24(peek, op, *size); michael@0: if (MASK_24 == *size) { michael@0: // size required its own slot right after the op code michael@0: *size = writer->readTAt(offset + kUInt32Size); michael@0: } michael@0: return (DrawType) op; michael@0: } michael@0: michael@0: #ifdef TRACK_COLLAPSE_STATS michael@0: static int gCollapseCount, gCollapseCalls; michael@0: #endif michael@0: michael@0: // Is the supplied paint simply a color? michael@0: static bool is_simple(const SkPaint& p) { michael@0: intptr_t orAccum = (intptr_t)p.getPathEffect() | michael@0: (intptr_t)p.getShader() | michael@0: (intptr_t)p.getXfermode() | michael@0: (intptr_t)p.getMaskFilter() | michael@0: (intptr_t)p.getColorFilter() | michael@0: (intptr_t)p.getRasterizer() | michael@0: (intptr_t)p.getLooper() | michael@0: (intptr_t)p.getImageFilter(); michael@0: return 0 == orAccum; michael@0: } michael@0: michael@0: // CommandInfos are fed to the 'match' method and filled in with command michael@0: // information. michael@0: struct CommandInfo { michael@0: DrawType fActualOp; michael@0: uint32_t fOffset; michael@0: uint32_t fSize; michael@0: }; michael@0: michael@0: /* michael@0: * Attempt to match the provided pattern of commands starting at 'offset' michael@0: * in the byte stream and stopping at the end of the stream. Upon success, michael@0: * return true with all the pattern information filled out in the result michael@0: * array (i.e., actual ops, offsets and sizes). michael@0: * Note this method skips any NOOPs seen in the stream michael@0: */ michael@0: static bool match(SkWriter32* writer, uint32_t offset, michael@0: int* pattern, CommandInfo* result, int numCommands) { michael@0: SkASSERT(offset < writer->bytesWritten()); michael@0: michael@0: uint32_t curOffset = offset; michael@0: uint32_t curSize = 0; michael@0: int numMatched; michael@0: for (numMatched = 0; numMatched < numCommands && curOffset < writer->bytesWritten(); ++numMatched) { michael@0: DrawType op = peek_op_and_size(writer, curOffset, &curSize); michael@0: while (NOOP == op && curOffset < writer->bytesWritten()) { michael@0: curOffset += curSize; michael@0: op = peek_op_and_size(writer, curOffset, &curSize); michael@0: } michael@0: michael@0: if (curOffset >= writer->bytesWritten()) { michael@0: return false; // ran out of byte stream michael@0: } michael@0: michael@0: if (kDRAW_BITMAP_FLAVOR == pattern[numMatched]) { michael@0: if (DRAW_BITMAP != op && DRAW_BITMAP_MATRIX != op && michael@0: DRAW_BITMAP_NINE != op && DRAW_BITMAP_RECT_TO_RECT != op) { michael@0: return false; michael@0: } michael@0: } else if (op != pattern[numMatched]) { michael@0: return false; michael@0: } michael@0: michael@0: result[numMatched].fActualOp = op; michael@0: result[numMatched].fOffset = curOffset; michael@0: result[numMatched].fSize = curSize; michael@0: michael@0: curOffset += curSize; michael@0: } michael@0: michael@0: if (numMatched != numCommands) { michael@0: return false; michael@0: } michael@0: michael@0: curOffset += curSize; michael@0: if (curOffset < writer->bytesWritten()) { michael@0: // Something else between the last command and the end of the stream michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // temporarily here to make code review easier michael@0: static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer, michael@0: SkPaintDictionary* paintDict, michael@0: const CommandInfo& saveLayerInfo, michael@0: const CommandInfo& dbmInfo); michael@0: michael@0: /* michael@0: * Restore has just been called (but not recorded), look back at the michael@0: * matching save* and see if we are in the configuration: michael@0: * SAVE_LAYER michael@0: * DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT michael@0: * RESTORE michael@0: * where the saveLayer's color can be moved into the drawBitmap*'s paint michael@0: */ michael@0: static bool remove_save_layer1(SkWriter32* writer, int32_t offset, michael@0: SkPaintDictionary* paintDict) { michael@0: // back up to the save block michael@0: // TODO: add a stack to track save*/restore offsets rather than searching backwards michael@0: while (offset > 0) { michael@0: offset = writer->readTAt(offset); michael@0: } michael@0: michael@0: int pattern[] = { SAVE_LAYER, kDRAW_BITMAP_FLAVOR, /* RESTORE */ }; michael@0: CommandInfo result[SK_ARRAY_COUNT(pattern)]; michael@0: michael@0: if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) { michael@0: return false; michael@0: } michael@0: michael@0: if (kSaveLayerWithBoundsSize == result[0].fSize) { michael@0: // The saveLayer's bound can offset where the dbm is drawn michael@0: return false; michael@0: } michael@0: michael@0: return merge_savelayer_paint_into_drawbitmp(writer, paintDict, michael@0: result[0], result[1]); michael@0: } michael@0: michael@0: /* michael@0: * Convert the command code located at 'offset' to a NOOP. Leave the size michael@0: * field alone so the NOOP can be skipped later. michael@0: */ michael@0: static void convert_command_to_noop(SkWriter32* writer, uint32_t offset) { michael@0: uint32_t command = writer->readTAt(offset); michael@0: writer->overwriteTAt(offset, (command & MASK_24) | (NOOP << 24)); michael@0: } michael@0: michael@0: /* michael@0: * Attempt to merge the saveLayer's paint into the drawBitmap*'s paint. michael@0: * Return true on success; false otherwise. michael@0: */ michael@0: static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer, michael@0: SkPaintDictionary* paintDict, michael@0: const CommandInfo& saveLayerInfo, michael@0: const CommandInfo& dbmInfo) { michael@0: SkASSERT(SAVE_LAYER == saveLayerInfo.fActualOp); michael@0: SkASSERT(DRAW_BITMAP == dbmInfo.fActualOp || michael@0: DRAW_BITMAP_MATRIX == dbmInfo.fActualOp || michael@0: DRAW_BITMAP_NINE == dbmInfo.fActualOp || michael@0: DRAW_BITMAP_RECT_TO_RECT == dbmInfo.fActualOp); michael@0: michael@0: uint32_t dbmPaintOffset = getPaintOffset(dbmInfo.fActualOp, dbmInfo.fSize); michael@0: uint32_t slPaintOffset = getPaintOffset(SAVE_LAYER, saveLayerInfo.fSize); michael@0: michael@0: // we have a match, now we need to get the paints involved michael@0: uint32_t dbmPaintId = writer->readTAt(dbmInfo.fOffset + dbmPaintOffset); michael@0: uint32_t saveLayerPaintId = writer->readTAt(saveLayerInfo.fOffset + slPaintOffset); michael@0: michael@0: if (0 == saveLayerPaintId) { michael@0: // In this case the saveLayer/restore isn't needed at all - just kill the saveLayer michael@0: // and signal the caller (by returning true) to not add the RESTORE op michael@0: convert_command_to_noop(writer, saveLayerInfo.fOffset); michael@0: return true; michael@0: } michael@0: michael@0: if (0 == dbmPaintId) { michael@0: // In this case just make the DBM* use the saveLayer's paint, kill the saveLayer michael@0: // and signal the caller (by returning true) to not add the RESTORE op michael@0: convert_command_to_noop(writer, saveLayerInfo.fOffset); michael@0: writer->overwriteTAt(dbmInfo.fOffset + dbmPaintOffset, saveLayerPaintId); michael@0: return true; michael@0: } michael@0: michael@0: SkAutoTDelete saveLayerPaint(paintDict->unflatten(saveLayerPaintId)); michael@0: if (NULL == saveLayerPaint.get() || !is_simple(*saveLayerPaint)) { michael@0: return false; michael@0: } michael@0: michael@0: // For this optimization we only fold the saveLayer and drawBitmapRect michael@0: // together if the saveLayer's draw is simple (i.e., no fancy effects) and michael@0: // and the only difference in the colors is that the saveLayer's can have michael@0: // an alpha while the drawBitmapRect's is opaque. michael@0: // TODO: it should be possible to fold them together even if they both michael@0: // have different non-255 alphas michael@0: SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque michael@0: michael@0: SkAutoTDelete dbmPaint(paintDict->unflatten(dbmPaintId)); michael@0: if (NULL == dbmPaint.get() || dbmPaint->getColor() != layerColor) { michael@0: return false; michael@0: } michael@0: michael@0: SkColor newColor = SkColorSetA(dbmPaint->getColor(), michael@0: SkColorGetA(saveLayerPaint->getColor())); michael@0: dbmPaint->setColor(newColor); michael@0: michael@0: const SkFlatData* data = paintDict->findAndReturnFlat(*dbmPaint); michael@0: if (NULL == data) { michael@0: return false; michael@0: } michael@0: michael@0: // kill the saveLayer and alter the DBMR2R's paint to be the modified one michael@0: convert_command_to_noop(writer, saveLayerInfo.fOffset); michael@0: writer->overwriteTAt(dbmInfo.fOffset + dbmPaintOffset, data->index()); michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Restore has just been called (but not recorded), look back at the michael@0: * matching save* and see if we are in the configuration: michael@0: * SAVE_LAYER (with NULL == bounds) michael@0: * SAVE michael@0: * CLIP_RECT michael@0: * DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT michael@0: * RESTORE michael@0: * RESTORE michael@0: * where the saveLayer's color can be moved into the drawBitmap*'s paint michael@0: */ michael@0: static bool remove_save_layer2(SkWriter32* writer, int32_t offset, michael@0: SkPaintDictionary* paintDict) { michael@0: michael@0: // back up to the save block michael@0: // TODO: add a stack to track save*/restore offsets rather than searching backwards michael@0: while (offset > 0) { michael@0: offset = writer->readTAt(offset); michael@0: } michael@0: michael@0: int pattern[] = { SAVE_LAYER, SAVE, CLIP_RECT, kDRAW_BITMAP_FLAVOR, RESTORE, /* RESTORE */ }; michael@0: CommandInfo result[SK_ARRAY_COUNT(pattern)]; michael@0: michael@0: if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) { michael@0: return false; michael@0: } michael@0: michael@0: if (kSaveLayerWithBoundsSize == result[0].fSize) { michael@0: // The saveLayer's bound can offset where the dbm is drawn michael@0: return false; michael@0: } michael@0: michael@0: return merge_savelayer_paint_into_drawbitmp(writer, paintDict, michael@0: result[0], result[3]); michael@0: } michael@0: michael@0: static bool is_drawing_op(DrawType op) { michael@0: return (op > CONCAT && op < ROTATE) || DRAW_DRRECT == op; michael@0: } michael@0: michael@0: /* michael@0: * Restore has just been called (but not recorded), so look back at the michael@0: * matching save(), and see if we can eliminate the pair of them, due to no michael@0: * intervening matrix/clip calls. michael@0: * michael@0: * If so, update the writer and return true, in which case we won't even record michael@0: * the restore() call. If we still need the restore(), return false. michael@0: */ michael@0: static bool collapse_save_clip_restore(SkWriter32* writer, int32_t offset, michael@0: SkPaintDictionary* paintDict) { michael@0: #ifdef TRACK_COLLAPSE_STATS michael@0: gCollapseCalls += 1; michael@0: #endif michael@0: michael@0: int32_t restoreOffset = (int32_t)writer->bytesWritten(); michael@0: michael@0: // back up to the save block michael@0: while (offset > 0) { michael@0: offset = writer->readTAt(offset); michael@0: } michael@0: michael@0: // now offset points to a save michael@0: offset = -offset; michael@0: uint32_t opSize; michael@0: DrawType op = peek_op_and_size(writer, offset, &opSize); michael@0: if (SAVE_LAYER == op) { michael@0: // not ready to cull these out yet (mrr) michael@0: return false; michael@0: } michael@0: SkASSERT(SAVE == op); michael@0: SkASSERT(kSaveSize == opSize); michael@0: michael@0: // get the save flag (last 4-bytes of the space allocated for the opSize) michael@0: SkCanvas::SaveFlags saveFlags = (SkCanvas::SaveFlags) writer->readTAt(offset + 4); michael@0: if (SkCanvas::kMatrixClip_SaveFlag != saveFlags) { michael@0: // This function's optimization is only correct for kMatrixClip style saves. michael@0: // TODO: set checkMatrix & checkClip booleans here and then check for the michael@0: // offending operations in the following loop. michael@0: return false; michael@0: } michael@0: michael@0: // Walk forward until we get back to either a draw-verb (abort) or we hit michael@0: // our restore (success). michael@0: int32_t saveOffset = offset; michael@0: michael@0: offset += opSize; michael@0: while (offset < restoreOffset) { michael@0: op = peek_op_and_size(writer, offset, &opSize); michael@0: if (is_drawing_op(op) || (SAVE_LAYER == op)) { michael@0: // drawing verb, abort michael@0: return false; michael@0: } michael@0: offset += opSize; michael@0: } michael@0: michael@0: #ifdef TRACK_COLLAPSE_STATS michael@0: gCollapseCount += 1; michael@0: SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls, michael@0: (double)gCollapseCount / gCollapseCalls, "%"); michael@0: #endif michael@0: michael@0: writer->rewindToOffset(saveOffset); michael@0: return true; michael@0: } michael@0: michael@0: typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset, michael@0: SkPaintDictionary* paintDict); michael@0: enum PictureRecordOptType { michael@0: kRewind_OptType, // Optimization rewinds the command stream michael@0: kCollapseSaveLayer_OptType, // Optimization eliminates a save/restore pair michael@0: }; michael@0: michael@0: enum PictureRecordOptFlags { michael@0: kSkipIfBBoxHierarchy_Flag = 0x1, // Optimization should be skipped if the michael@0: // SkPicture has a bounding box hierarchy. michael@0: }; michael@0: michael@0: struct PictureRecordOpt { michael@0: PictureRecordOptProc fProc; michael@0: PictureRecordOptType fType; michael@0: unsigned fFlags; michael@0: }; michael@0: /* michael@0: * A list of the optimizations that are tried upon seeing a restore michael@0: * TODO: add a real API for such optimizations michael@0: * Add the ability to fire optimizations on any op (not just RESTORE) michael@0: */ michael@0: static const PictureRecordOpt gPictureRecordOpts[] = { michael@0: // 'collapse_save_clip_restore' is skipped if there is a BBoxHierarchy michael@0: // because it is redundant with the state traversal optimization in michael@0: // SkPictureStateTree, and applying the optimization introduces significant michael@0: // record time overhead because it requires rewinding contents that were michael@0: // recorded into the BBoxHierarchy. michael@0: { collapse_save_clip_restore, kRewind_OptType, kSkipIfBBoxHierarchy_Flag }, michael@0: { remove_save_layer1, kCollapseSaveLayer_OptType, 0 }, michael@0: { remove_save_layer2, kCollapseSaveLayer_OptType, 0 } michael@0: }; michael@0: michael@0: // This is called after an optimization has been applied to the command stream michael@0: // in order to adjust the contents and state of the bounding box hierarchy and michael@0: // state tree to reflect the optimization. michael@0: static void apply_optimization_to_bbh(PictureRecordOptType opt, SkPictureStateTree* stateTree, michael@0: SkBBoxHierarchy* boundingHierarchy) { michael@0: switch (opt) { michael@0: case kCollapseSaveLayer_OptType: michael@0: if (NULL != stateTree) { michael@0: stateTree->saveCollapsed(); michael@0: } michael@0: break; michael@0: case kRewind_OptType: michael@0: if (NULL != boundingHierarchy) { michael@0: boundingHierarchy->rewindInserts(); michael@0: } michael@0: // Note: No need to touch the state tree for this to work correctly. michael@0: // Unused branches do not burden the playback, and pruning the tree michael@0: // would be O(N^2), so it is best to leave it alone. michael@0: break; michael@0: default: michael@0: SkASSERT(0); michael@0: } michael@0: } michael@0: michael@0: void SkPictureRecord::willRestore() { michael@0: // FIXME: SkDeferredCanvas needs to be refactored to respect michael@0: // save/restore balancing so that the following test can be michael@0: // turned on permanently. michael@0: #if 0 michael@0: SkASSERT(fRestoreOffsetStack.count() > 1); michael@0: #endif michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: if (fMCMgr.getSaveCount() == 1) { michael@0: return; michael@0: } michael@0: michael@0: fMCMgr.restore(); michael@0: #else michael@0: // check for underflow michael@0: if (fRestoreOffsetStack.count() == 0) { michael@0: return; michael@0: } michael@0: michael@0: if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) { michael@0: fFirstSavedLayerIndex = kNoSavedLayerIndex; michael@0: } michael@0: michael@0: size_t opt = 0; michael@0: if (fOptsEnabled) { michael@0: for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) { michael@0: if (0 != (gPictureRecordOpts[opt].fFlags & kSkipIfBBoxHierarchy_Flag) michael@0: && NULL != fBoundingHierarchy) { michael@0: continue; michael@0: } michael@0: if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) { michael@0: // Some optimization fired so don't add the RESTORE michael@0: apply_optimization_to_bbh(gPictureRecordOpts[opt].fType, michael@0: fStateTree, fBoundingHierarchy); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!fOptsEnabled || SK_ARRAY_COUNT(gPictureRecordOpts) == opt) { michael@0: // No optimization fired so add the RESTORE michael@0: this->recordRestore(); michael@0: } michael@0: michael@0: fRestoreOffsetStack.pop(); michael@0: #endif michael@0: michael@0: this->INHERITED::willRestore(); michael@0: } michael@0: michael@0: void SkPictureRecord::recordRestore(bool fillInSkips) { michael@0: uint32_t initialOffset, size; michael@0: if (fillInSkips) { michael@0: this->fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.bytesWritten()); michael@0: } michael@0: size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code michael@0: initialOffset = this->addDraw(RESTORE, &size); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::didTranslate(SkScalar dx, SkScalar dy) { michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.translate(dx, dy); michael@0: #else michael@0: // op + dx + dy michael@0: uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar); michael@0: size_t initialOffset = this->addDraw(TRANSLATE, &size); michael@0: this->addScalar(dx); michael@0: this->addScalar(dy); michael@0: this->validate(initialOffset, size); michael@0: #endif michael@0: this->INHERITED::didTranslate(dx, dy); michael@0: } michael@0: michael@0: void SkPictureRecord::didScale(SkScalar sx, SkScalar sy) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.scale(sx, sy); michael@0: #else michael@0: // op + sx + sy michael@0: uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar); michael@0: size_t initialOffset = this->addDraw(SCALE, &size); michael@0: this->addScalar(sx); michael@0: this->addScalar(sy); michael@0: this->validate(initialOffset, size); michael@0: #endif michael@0: this->INHERITED::didScale(sx, sy); michael@0: } michael@0: michael@0: void SkPictureRecord::didRotate(SkScalar degrees) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.rotate(degrees); michael@0: #else michael@0: // op + degrees michael@0: uint32_t size = 1 * kUInt32Size + sizeof(SkScalar); michael@0: size_t initialOffset = this->addDraw(ROTATE, &size); michael@0: this->addScalar(degrees); michael@0: this->validate(initialOffset, size); michael@0: #endif michael@0: this->INHERITED::didRotate(degrees); michael@0: } michael@0: michael@0: void SkPictureRecord::didSkew(SkScalar sx, SkScalar sy) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.skew(sx, sy); michael@0: #else michael@0: // op + sx + sy michael@0: uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar); michael@0: size_t initialOffset = this->addDraw(SKEW, &size); michael@0: this->addScalar(sx); michael@0: this->addScalar(sy); michael@0: this->validate(initialOffset, size); michael@0: #endif michael@0: this->INHERITED::didSkew(sx, sy); michael@0: } michael@0: michael@0: void SkPictureRecord::didConcat(const SkMatrix& matrix) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.concat(matrix); michael@0: #else michael@0: this->recordConcat(matrix); michael@0: #endif michael@0: this->INHERITED::didConcat(matrix); michael@0: } michael@0: michael@0: void SkPictureRecord::recordConcat(const SkMatrix& matrix) { michael@0: this->validate(fWriter.bytesWritten(), 0); michael@0: // op + matrix michael@0: uint32_t size = kUInt32Size + matrix.writeToMemory(NULL); michael@0: size_t initialOffset = this->addDraw(CONCAT, &size); michael@0: this->addMatrix(matrix); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::didSetMatrix(const SkMatrix& matrix) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.setMatrix(matrix); michael@0: #else michael@0: this->validate(fWriter.bytesWritten(), 0); michael@0: // op + matrix michael@0: uint32_t size = kUInt32Size + matrix.writeToMemory(NULL); michael@0: size_t initialOffset = this->addDraw(SET_MATRIX, &size); michael@0: this->addMatrix(matrix); michael@0: this->validate(initialOffset, size); michael@0: #endif michael@0: this->INHERITED::didSetMatrix(matrix); michael@0: } michael@0: michael@0: static bool regionOpExpands(SkRegion::Op op) { michael@0: switch (op) { michael@0: case SkRegion::kUnion_Op: michael@0: case SkRegion::kXOR_Op: michael@0: case SkRegion::kReverseDifference_Op: michael@0: case SkRegion::kReplace_Op: michael@0: return true; michael@0: case SkRegion::kIntersect_Op: michael@0: case SkRegion::kDifference_Op: michael@0: return false; michael@0: default: michael@0: SkDEBUGFAIL("unknown region op"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) { michael@0: fMCMgr.fillInSkips(&fWriter, restoreOffset); michael@0: } michael@0: #else michael@0: void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) { michael@0: int32_t offset = fRestoreOffsetStack.top(); michael@0: while (offset > 0) { michael@0: uint32_t peek = fWriter.readTAt(offset); michael@0: fWriter.overwriteTAt(offset, restoreOffset); michael@0: offset = peek; michael@0: } michael@0: michael@0: #ifdef SK_DEBUG michael@0: // assert that the final offset value points to a save verb michael@0: uint32_t opSize; michael@0: DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize); michael@0: SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp); michael@0: #endif michael@0: } michael@0: #endif michael@0: michael@0: void SkPictureRecord::beginRecording() { michael@0: // we have to call this *after* our constructor, to ensure that it gets michael@0: // recorded. This is balanced by restoreToCount() call from endRecording, michael@0: // which in-turn calls our overridden restore(), so those get recorded too. michael@0: fInitialSaveCount = this->save(kMatrixClip_SaveFlag); michael@0: } michael@0: michael@0: void SkPictureRecord::endRecording() { michael@0: SkASSERT(kNoInitialSave != fInitialSaveCount); michael@0: this->restoreToCount(fInitialSaveCount); michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.finish(); michael@0: #endif michael@0: } michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: int SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) { michael@0: size_t offset = fWriter.bytesWritten(); michael@0: this->addInt(-1); michael@0: return offset; michael@0: } michael@0: #else michael@0: int SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) { michael@0: if (fRestoreOffsetStack.isEmpty()) { michael@0: return -1; michael@0: } michael@0: michael@0: // The RestoreOffset field is initially filled with a placeholder michael@0: // value that points to the offset of the previous RestoreOffset michael@0: // in the current stack level, thus forming a linked list so that michael@0: // the restore offsets can be filled in when the corresponding michael@0: // restore command is recorded. michael@0: int32_t prevOffset = fRestoreOffsetStack.top(); michael@0: michael@0: if (regionOpExpands(op)) { michael@0: // Run back through any previous clip ops, and mark their offset to michael@0: // be 0, disabling their ability to trigger a jump-to-restore, otherwise michael@0: // they could hide this clips ability to expand the clip (i.e. go from michael@0: // empty to non-empty). michael@0: this->fillRestoreOffsetPlaceholdersForCurrentStackLevel(0); michael@0: michael@0: // Reset the pointer back to the previous clip so that subsequent michael@0: // restores don't overwrite the offsets we just cleared. michael@0: prevOffset = 0; michael@0: } michael@0: michael@0: size_t offset = fWriter.bytesWritten(); michael@0: this->addInt(prevOffset); michael@0: fRestoreOffsetStack.top() = offset; michael@0: return offset; michael@0: } michael@0: #endif michael@0: michael@0: void SkPictureRecord::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.clipRect(rect, op, doAA); michael@0: #else michael@0: this->recordClipRect(rect, op, kSoft_ClipEdgeStyle == edgeStyle); michael@0: #endif michael@0: this->INHERITED::onClipRect(rect, op, edgeStyle); michael@0: } michael@0: michael@0: int SkPictureRecord::recordClipRect(const SkRect& rect, SkRegion::Op op, bool doAA) { michael@0: // id + rect + clip params michael@0: uint32_t size = 1 * kUInt32Size + sizeof(rect) + 1 * kUInt32Size; michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: size += kUInt32Size; // + restore offset michael@0: #else michael@0: // recordRestoreOffsetPlaceholder doesn't always write an offset michael@0: if (!fRestoreOffsetStack.isEmpty()) { michael@0: // + restore offset michael@0: size += kUInt32Size; michael@0: } michael@0: #endif michael@0: size_t initialOffset = this->addDraw(CLIP_RECT, &size); michael@0: this->addRect(rect); michael@0: this->addInt(ClipParams_pack(op, doAA)); michael@0: int offset = this->recordRestoreOffsetPlaceholder(op); michael@0: michael@0: this->validate(initialOffset, size); michael@0: return offset; michael@0: } michael@0: michael@0: void SkPictureRecord::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.clipRRect(rrect, op, doAA); michael@0: #else michael@0: this->recordClipRRect(rrect, op, kSoft_ClipEdgeStyle == edgeStyle); michael@0: #endif michael@0: if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) { michael@0: this->updateClipConservativelyUsingBounds(rrect.getBounds(), op, false); michael@0: } else { michael@0: this->INHERITED::onClipRRect(rrect, op, edgeStyle); michael@0: } michael@0: } michael@0: michael@0: int SkPictureRecord::recordClipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { michael@0: // op + rrect + clip params michael@0: uint32_t size = 1 * kUInt32Size + SkRRect::kSizeInMemory + 1 * kUInt32Size; michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: size += kUInt32Size; // + restore offset michael@0: #else michael@0: // recordRestoreOffsetPlaceholder doesn't always write an offset michael@0: if (!fRestoreOffsetStack.isEmpty()) { michael@0: // + restore offset michael@0: size += kUInt32Size; michael@0: } michael@0: #endif michael@0: size_t initialOffset = this->addDraw(CLIP_RRECT, &size); michael@0: this->addRRect(rrect); michael@0: this->addInt(ClipParams_pack(op, doAA)); michael@0: int offset = recordRestoreOffsetPlaceholder(op); michael@0: this->validate(initialOffset, size); michael@0: return offset; michael@0: } michael@0: michael@0: void SkPictureRecord::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.clipPath(path, op, doAA); michael@0: #else michael@0: int pathID = this->addPathToHeap(path); michael@0: this->recordClipPath(pathID, op, kSoft_ClipEdgeStyle == edgeStyle); michael@0: #endif michael@0: michael@0: if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) { michael@0: this->updateClipConservativelyUsingBounds(path.getBounds(), op, michael@0: path.isInverseFillType()); michael@0: } else { michael@0: this->INHERITED::onClipPath(path, op, edgeStyle); michael@0: } michael@0: } michael@0: michael@0: int SkPictureRecord::recordClipPath(int pathID, SkRegion::Op op, bool doAA) { michael@0: // op + path index + clip params michael@0: uint32_t size = 3 * kUInt32Size; michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: size += kUInt32Size; // + restore offset michael@0: #else michael@0: // recordRestoreOffsetPlaceholder doesn't always write an offset michael@0: if (!fRestoreOffsetStack.isEmpty()) { michael@0: // + restore offset michael@0: size += kUInt32Size; michael@0: } michael@0: #endif michael@0: size_t initialOffset = this->addDraw(CLIP_PATH, &size); michael@0: this->addInt(pathID); michael@0: this->addInt(ClipParams_pack(op, doAA)); michael@0: int offset = recordRestoreOffsetPlaceholder(op); michael@0: this->validate(initialOffset, size); michael@0: return offset; michael@0: } michael@0: michael@0: void SkPictureRecord::onClipRegion(const SkRegion& region, SkRegion::Op op) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.clipRegion(region, op); michael@0: #else michael@0: this->recordClipRegion(region, op); michael@0: #endif michael@0: this->INHERITED::onClipRegion(region, op); michael@0: } michael@0: michael@0: int SkPictureRecord::recordClipRegion(const SkRegion& region, SkRegion::Op op) { michael@0: // op + clip params + region michael@0: uint32_t size = 2 * kUInt32Size + region.writeToMemory(NULL); michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: size += kUInt32Size; // + restore offset michael@0: #else michael@0: // recordRestoreOffsetPlaceholder doesn't always write an offset michael@0: if (!fRestoreOffsetStack.isEmpty()) { michael@0: // + restore offset michael@0: size += kUInt32Size; michael@0: } michael@0: #endif michael@0: size_t initialOffset = this->addDraw(CLIP_REGION, &size); michael@0: this->addRegion(region); michael@0: this->addInt(ClipParams_pack(op, false)); michael@0: int offset = this->recordRestoreOffsetPlaceholder(op); michael@0: michael@0: this->validate(initialOffset, size); michael@0: return offset; michael@0: } michael@0: michael@0: void SkPictureRecord::clear(SkColor color) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + color michael@0: uint32_t size = 2 * kUInt32Size; michael@0: size_t initialOffset = this->addDraw(DRAW_CLEAR, &size); michael@0: this->addInt(color); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawPaint(const SkPaint& paint) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + paint index michael@0: uint32_t size = 2 * kUInt32Size; michael@0: size_t initialOffset = this->addDraw(DRAW_PAINT, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_PAINT, size) == fWriter.bytesWritten()); michael@0: this->addPaint(paint); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[], michael@0: const SkPaint& paint) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + paint index + mode + count + point data michael@0: uint32_t size = 4 * kUInt32Size + count * sizeof(SkPoint); michael@0: size_t initialOffset = this->addDraw(DRAW_POINTS, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_POINTS, size) == fWriter.bytesWritten()); michael@0: this->addPaint(paint); michael@0: this->addInt(mode); michael@0: this->addInt(count); michael@0: fWriter.writeMul4(pts, count * sizeof(SkPoint)); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + paint index + rect michael@0: uint32_t size = 2 * kUInt32Size + sizeof(oval); michael@0: size_t initialOffset = this->addDraw(DRAW_OVAL, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.bytesWritten()); michael@0: this->addPaint(paint); michael@0: this->addRect(oval); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + paint index + rect michael@0: uint32_t size = 2 * kUInt32Size + sizeof(rect); michael@0: size_t initialOffset = this->addDraw(DRAW_RECT, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.bytesWritten()); michael@0: this->addPaint(paint); michael@0: this->addRect(rect); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: if (rrect.isRect()) { michael@0: this->SkPictureRecord::drawRect(rrect.getBounds(), paint); michael@0: } else if (rrect.isOval()) { michael@0: this->SkPictureRecord::drawOval(rrect.getBounds(), paint); michael@0: } else { michael@0: // op + paint index + rrect michael@0: uint32_t initialOffset, size; michael@0: size = 2 * kUInt32Size + SkRRect::kSizeInMemory; michael@0: initialOffset = this->addDraw(DRAW_RRECT, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_RRECT, size) == fWriter.bytesWritten()); michael@0: this->addPaint(paint); michael@0: this->addRRect(rrect); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: } michael@0: michael@0: void SkPictureRecord::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, michael@0: const SkPaint& paint) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + paint index + rrects michael@0: uint32_t initialOffset, size; michael@0: size = 2 * kUInt32Size + SkRRect::kSizeInMemory * 2; michael@0: initialOffset = this->addDraw(DRAW_DRRECT, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_DRRECT, size) == fWriter.bytesWritten()); michael@0: this->addPaint(paint); michael@0: this->addRRect(outer); michael@0: this->addRRect(inner); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + paint index + path index michael@0: uint32_t size = 3 * kUInt32Size; michael@0: size_t initialOffset = this->addDraw(DRAW_PATH, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_PATH, size) == fWriter.bytesWritten()); michael@0: this->addPaint(paint); michael@0: this->addPath(path); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, michael@0: const SkPaint* paint = NULL) { michael@0: if (bitmap.drawsNothing()) { michael@0: return; michael@0: } michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + paint index + bitmap index + left + top michael@0: uint32_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar); michael@0: size_t initialOffset = this->addDraw(DRAW_BITMAP, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP, size) == fWriter.bytesWritten()); michael@0: this->addPaintPtr(paint); michael@0: int bitmapID = this->addBitmap(bitmap); michael@0: this->addScalar(left); michael@0: this->addScalar(top); michael@0: this->validate(initialOffset, size); michael@0: this->trackBitmapUse(bitmapID, initialOffset); michael@0: } michael@0: michael@0: void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, michael@0: const SkRect& dst, const SkPaint* paint, michael@0: DrawBitmapRectFlags flags) { michael@0: if (bitmap.drawsNothing()) { michael@0: return; michael@0: } michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: // id + paint index + bitmap index + bool for 'src' + flags michael@0: uint32_t size = 5 * kUInt32Size; michael@0: if (NULL != src) { michael@0: size += sizeof(*src); // + rect michael@0: } michael@0: size += sizeof(dst); // + rect michael@0: michael@0: size_t initialOffset = this->addDraw(DRAW_BITMAP_RECT_TO_RECT, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_RECT_TO_RECT, size) michael@0: == fWriter.bytesWritten()); michael@0: this->addPaintPtr(paint); michael@0: int bitmapID = this->addBitmap(bitmap); michael@0: this->addRectPtr(src); // may be null michael@0: this->addRect(dst); michael@0: this->addInt(flags); michael@0: this->validate(initialOffset, size); michael@0: this->trackBitmapUse(bitmapID, initialOffset); michael@0: } michael@0: michael@0: void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix, michael@0: const SkPaint* paint) { michael@0: if (bitmap.drawsNothing()) { michael@0: return; michael@0: } michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // id + paint index + bitmap index + matrix michael@0: uint32_t size = 3 * kUInt32Size + matrix.writeToMemory(NULL); michael@0: size_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_MATRIX, size) == fWriter.bytesWritten()); michael@0: this->addPaintPtr(paint); michael@0: int bitmapID = this->addBitmap(bitmap); michael@0: this->addMatrix(matrix); michael@0: this->validate(initialOffset, size); michael@0: this->trackBitmapUse(bitmapID, initialOffset); michael@0: } michael@0: michael@0: void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, michael@0: const SkRect& dst, const SkPaint* paint) { michael@0: if (bitmap.drawsNothing()) { michael@0: return; michael@0: } michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + paint index + bitmap id + center + dst rect michael@0: uint32_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst); michael@0: size_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_NINE, size) == fWriter.bytesWritten()); michael@0: this->addPaintPtr(paint); michael@0: int bitmapID = this->addBitmap(bitmap); michael@0: this->addIRect(center); michael@0: this->addRect(dst); michael@0: this->validate(initialOffset, size); michael@0: this->trackBitmapUse(bitmapID, initialOffset); michael@0: } michael@0: michael@0: void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top, michael@0: const SkPaint* paint = NULL) { michael@0: if (bitmap.drawsNothing()) { michael@0: return; michael@0: } michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + paint index + bitmap index + left + top michael@0: uint32_t size = 5 * kUInt32Size; michael@0: size_t initialOffset = this->addDraw(DRAW_SPRITE, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_SPRITE, size) == fWriter.bytesWritten()); michael@0: this->addPaintPtr(paint); michael@0: int bitmapID = this->addBitmap(bitmap); michael@0: this->addInt(left); michael@0: this->addInt(top); michael@0: this->validate(initialOffset, size); michael@0: this->trackBitmapUse(bitmapID, initialOffset); michael@0: } michael@0: michael@0: void SkPictureRecord::ComputeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) { michael@0: SkPaint::FontMetrics metrics; michael@0: paint.getFontMetrics(&metrics); michael@0: SkRect bounds; michael@0: // construct a rect so we can see any adjustments from the paint. michael@0: // we use 0,1 for left,right, just so the rect isn't empty michael@0: bounds.set(0, metrics.fTop, SK_Scalar1, metrics.fBottom); michael@0: (void)paint.computeFastBounds(bounds, &bounds); michael@0: topbot[0] = bounds.fTop; michael@0: topbot[1] = bounds.fBottom; michael@0: } michael@0: michael@0: void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData& flat, michael@0: SkScalar minY, SkScalar maxY) { michael@0: WriteTopBot(paint, flat); michael@0: this->addScalar(flat.topBot()[0] + minY); michael@0: this->addScalar(flat.topBot()[1] + maxY); michael@0: } michael@0: michael@0: void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x, michael@0: SkScalar y, const SkPaint& paint) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: bool fast = !paint.isVerticalText() && paint.canComputeFastBounds(); michael@0: michael@0: // op + paint index + length + 'length' worth of chars + x + y michael@0: uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * sizeof(SkScalar); michael@0: if (fast) { michael@0: size += 2 * sizeof(SkScalar); // + top & bottom michael@0: } michael@0: michael@0: DrawType op = fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT; michael@0: size_t initialOffset = this->addDraw(op, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.bytesWritten()); michael@0: const SkFlatData* flatPaintData = addPaint(paint); michael@0: SkASSERT(flatPaintData); michael@0: this->addText(text, byteLength); michael@0: this->addScalar(x); michael@0: this->addScalar(y); michael@0: if (fast) { michael@0: this->addFontMetricsTopBottom(paint, *flatPaintData, y, y); michael@0: } michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawPosText(const void* text, size_t byteLength, michael@0: const SkPoint pos[], const SkPaint& paint) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: size_t points = paint.countText(text, byteLength); michael@0: if (0 == points) michael@0: return; michael@0: michael@0: bool canUseDrawH = true; michael@0: SkScalar minY = pos[0].fY; michael@0: SkScalar maxY = pos[0].fY; michael@0: // check if the caller really should have used drawPosTextH() michael@0: { michael@0: const SkScalar firstY = pos[0].fY; michael@0: for (size_t index = 1; index < points; index++) { michael@0: if (pos[index].fY != firstY) { michael@0: canUseDrawH = false; michael@0: if (pos[index].fY < minY) { michael@0: minY = pos[index].fY; michael@0: } else if (pos[index].fY > maxY) { michael@0: maxY = pos[index].fY; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds(); michael@0: bool fast = canUseDrawH && fastBounds; michael@0: michael@0: // op + paint index + length + 'length' worth of data + num points michael@0: uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size; michael@0: if (canUseDrawH) { michael@0: if (fast) { michael@0: size += 2 * sizeof(SkScalar); // + top & bottom michael@0: } michael@0: // + y-pos + actual x-point data michael@0: size += sizeof(SkScalar) + points * sizeof(SkScalar); michael@0: } else { michael@0: // + x&y point data michael@0: size += points * sizeof(SkPoint); michael@0: if (fastBounds) { michael@0: size += 2 * sizeof(SkScalar); // + top & bottom michael@0: } michael@0: } michael@0: michael@0: DrawType op; michael@0: if (fast) { michael@0: op = DRAW_POS_TEXT_H_TOP_BOTTOM; michael@0: } else if (canUseDrawH) { michael@0: op = DRAW_POS_TEXT_H; michael@0: } else if (fastBounds) { michael@0: op = DRAW_POS_TEXT_TOP_BOTTOM; michael@0: } else { michael@0: op = DRAW_POS_TEXT; michael@0: } michael@0: size_t initialOffset = this->addDraw(op, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.bytesWritten()); michael@0: const SkFlatData* flatPaintData = this->addPaint(paint); michael@0: SkASSERT(flatPaintData); michael@0: this->addText(text, byteLength); michael@0: this->addInt(points); michael@0: michael@0: #ifdef SK_DEBUG_SIZE michael@0: size_t start = fWriter.bytesWritten(); michael@0: #endif michael@0: if (canUseDrawH) { michael@0: if (fast) { michael@0: this->addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY); michael@0: } michael@0: this->addScalar(pos[0].fY); michael@0: SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar)); michael@0: for (size_t index = 0; index < points; index++) michael@0: *xptr++ = pos[index].fX; michael@0: } else { michael@0: fWriter.writeMul4(pos, points * sizeof(SkPoint)); michael@0: if (fastBounds) { michael@0: this->addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY); michael@0: } michael@0: } michael@0: #ifdef SK_DEBUG_SIZE michael@0: fPointBytes += fWriter.bytesWritten() - start; michael@0: fPointWrites += points; michael@0: #endif michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength, michael@0: const SkScalar xpos[], SkScalar constY, michael@0: const SkPaint& paint) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: const SkFlatData* flatPaintData = this->getFlatPaintData(paint); michael@0: this->drawPosTextHImpl(text, byteLength, xpos, constY, paint, flatPaintData); michael@0: } michael@0: michael@0: void SkPictureRecord::drawPosTextHImpl(const void* text, size_t byteLength, michael@0: const SkScalar xpos[], SkScalar constY, michael@0: const SkPaint& paint, const SkFlatData* flatPaintData) { michael@0: size_t points = paint.countText(text, byteLength); michael@0: if (0 == points) michael@0: return; michael@0: michael@0: bool fast = !paint.isVerticalText() && paint.canComputeFastBounds(); michael@0: michael@0: // op + paint index + length + 'length' worth of data + num points michael@0: uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size; michael@0: if (fast) { michael@0: size += 2 * sizeof(SkScalar); // + top & bottom michael@0: } michael@0: // + y + the actual points michael@0: size += 1 * kUInt32Size + points * sizeof(SkScalar); michael@0: size_t initialOffset = this->addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H, michael@0: &size); michael@0: SkASSERT(flatPaintData); michael@0: this->addFlatPaint(flatPaintData); michael@0: michael@0: this->addText(text, byteLength); michael@0: this->addInt(points); michael@0: michael@0: #ifdef SK_DEBUG_SIZE michael@0: size_t start = fWriter.bytesWritten(); michael@0: #endif michael@0: if (fast) { michael@0: this->addFontMetricsTopBottom(paint, *flatPaintData, constY, constY); michael@0: } michael@0: this->addScalar(constY); michael@0: fWriter.writeMul4(xpos, points * sizeof(SkScalar)); michael@0: #ifdef SK_DEBUG_SIZE michael@0: fPointBytes += fWriter.bytesWritten() - start; michael@0: fPointWrites += points; michael@0: #endif michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength, michael@0: const SkPath& path, const SkMatrix* matrix, michael@0: const SkPaint& paint) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + paint index + length + 'length' worth of data + path index + matrix michael@0: const SkMatrix& m = matrix ? *matrix : SkMatrix::I(); michael@0: uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + kUInt32Size + m.writeToMemory(NULL); michael@0: size_t initialOffset = this->addDraw(DRAW_TEXT_ON_PATH, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_TEXT_ON_PATH, size) == fWriter.bytesWritten()); michael@0: this->addPaint(paint); michael@0: this->addText(text, byteLength); michael@0: this->addPath(path); michael@0: this->addMatrix(m); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawPicture(SkPicture& picture) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + picture index michael@0: uint32_t size = 2 * kUInt32Size; michael@0: size_t initialOffset = this->addDraw(DRAW_PICTURE, &size); michael@0: this->addPicture(picture); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount, michael@0: const SkPoint vertices[], const SkPoint texs[], michael@0: const SkColor colors[], SkXfermode* xfer, michael@0: const uint16_t indices[], int indexCount, michael@0: const SkPaint& paint) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: uint32_t flags = 0; michael@0: if (texs) { michael@0: flags |= DRAW_VERTICES_HAS_TEXS; michael@0: } michael@0: if (colors) { michael@0: flags |= DRAW_VERTICES_HAS_COLORS; michael@0: } michael@0: if (indexCount > 0) { michael@0: flags |= DRAW_VERTICES_HAS_INDICES; michael@0: } michael@0: if (NULL != xfer) { michael@0: SkXfermode::Mode mode; michael@0: if (xfer->asMode(&mode) && SkXfermode::kModulate_Mode != mode) { michael@0: flags |= DRAW_VERTICES_HAS_XFER; michael@0: } michael@0: } michael@0: michael@0: // op + paint index + flags + vmode + vCount + vertices michael@0: uint32_t size = 5 * kUInt32Size + vertexCount * sizeof(SkPoint); michael@0: if (flags & DRAW_VERTICES_HAS_TEXS) { michael@0: size += vertexCount * sizeof(SkPoint); // + uvs michael@0: } michael@0: if (flags & DRAW_VERTICES_HAS_COLORS) { michael@0: size += vertexCount * sizeof(SkColor); // + vert colors michael@0: } michael@0: if (flags & DRAW_VERTICES_HAS_INDICES) { michael@0: // + num indices + indices michael@0: size += 1 * kUInt32Size + SkAlign4(indexCount * sizeof(uint16_t)); michael@0: } michael@0: if (flags & DRAW_VERTICES_HAS_XFER) { michael@0: size += kUInt32Size; // mode enum michael@0: } michael@0: michael@0: size_t initialOffset = this->addDraw(DRAW_VERTICES, &size); michael@0: SkASSERT(initialOffset+getPaintOffset(DRAW_VERTICES, size) == fWriter.bytesWritten()); michael@0: this->addPaint(paint); michael@0: this->addInt(flags); michael@0: this->addInt(vmode); michael@0: this->addInt(vertexCount); michael@0: this->addPoints(vertices, vertexCount); michael@0: if (flags & DRAW_VERTICES_HAS_TEXS) { michael@0: this->addPoints(texs, vertexCount); michael@0: } michael@0: if (flags & DRAW_VERTICES_HAS_COLORS) { michael@0: fWriter.writeMul4(colors, vertexCount * sizeof(SkColor)); michael@0: } michael@0: if (flags & DRAW_VERTICES_HAS_INDICES) { michael@0: this->addInt(indexCount); michael@0: fWriter.writePad(indices, indexCount * sizeof(uint16_t)); michael@0: } michael@0: if (flags & DRAW_VERTICES_HAS_XFER) { michael@0: SkXfermode::Mode mode = SkXfermode::kModulate_Mode; michael@0: (void)xfer->asMode(&mode); michael@0: this->addInt(mode); michael@0: } michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::drawData(const void* data, size_t length) { michael@0: michael@0: #ifdef SK_COLLAPSE_MATRIX_CLIP_STATE michael@0: fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType); michael@0: #endif michael@0: michael@0: // op + length + 'length' worth of data michael@0: uint32_t size = 2 * kUInt32Size + SkAlign4(length); michael@0: size_t initialOffset = this->addDraw(DRAW_DATA, &size); michael@0: this->addInt(length); michael@0: fWriter.writePad(data, length); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::beginCommentGroup(const char* description) { michael@0: // op/size + length of string + \0 terminated chars michael@0: int length = strlen(description); michael@0: uint32_t size = 2 * kUInt32Size + SkAlign4(length + 1); michael@0: size_t initialOffset = this->addDraw(BEGIN_COMMENT_GROUP, &size); michael@0: fWriter.writeString(description, length); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::addComment(const char* kywd, const char* value) { michael@0: // op/size + 2x length of string + 2x \0 terminated chars michael@0: int kywdLen = strlen(kywd); michael@0: int valueLen = strlen(value); michael@0: uint32_t size = 3 * kUInt32Size + SkAlign4(kywdLen + 1) + SkAlign4(valueLen + 1); michael@0: size_t initialOffset = this->addDraw(COMMENT, &size); michael@0: fWriter.writeString(kywd, kywdLen); michael@0: fWriter.writeString(value, valueLen); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::endCommentGroup() { michael@0: // op/size michael@0: uint32_t size = 1 * kUInt32Size; michael@0: size_t initialOffset = this->addDraw(END_COMMENT_GROUP, &size); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: // [op/size] [rect] [skip offset] michael@0: static const uint32_t kPushCullOpSize = 2 * kUInt32Size + sizeof(SkRect); michael@0: void SkPictureRecord::onPushCull(const SkRect& cullRect) { michael@0: // Skip identical cull rects. michael@0: if (!fCullOffsetStack.isEmpty()) { michael@0: const SkRect& prevCull = fWriter.readTAt(fCullOffsetStack.top() - sizeof(SkRect)); michael@0: if (prevCull == cullRect) { michael@0: // Skipped culls are tracked on the stack, but they point to the previous offset. michael@0: fCullOffsetStack.push(fCullOffsetStack.top()); michael@0: return; michael@0: } michael@0: michael@0: SkASSERT(prevCull.contains(cullRect)); michael@0: } michael@0: michael@0: uint32_t size = kPushCullOpSize; michael@0: size_t initialOffset = this->addDraw(PUSH_CULL, &size); michael@0: // PUSH_CULL's size should stay constant (used to rewind). michael@0: SkASSERT(size == kPushCullOpSize); michael@0: michael@0: this->addRect(cullRect); michael@0: fCullOffsetStack.push(fWriter.bytesWritten()); michael@0: this->addInt(0); michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: void SkPictureRecord::onPopCull() { michael@0: SkASSERT(!fCullOffsetStack.isEmpty()); michael@0: michael@0: uint32_t cullSkipOffset = fCullOffsetStack.top(); michael@0: fCullOffsetStack.pop(); michael@0: michael@0: // Skipped push, do the same for pop. michael@0: if (!fCullOffsetStack.isEmpty() && cullSkipOffset == fCullOffsetStack.top()) { michael@0: return; michael@0: } michael@0: michael@0: // Collapse empty push/pop pairs. michael@0: if ((size_t)(cullSkipOffset + kUInt32Size) == fWriter.bytesWritten()) { michael@0: SkASSERT(fWriter.bytesWritten() >= kPushCullOpSize); michael@0: SkASSERT(PUSH_CULL == peek_op(&fWriter, fWriter.bytesWritten() - kPushCullOpSize)); michael@0: fWriter.rewindToOffset(fWriter.bytesWritten() - kPushCullOpSize); michael@0: return; michael@0: } michael@0: michael@0: // op only michael@0: uint32_t size = kUInt32Size; michael@0: size_t initialOffset = this->addDraw(POP_CULL, &size); michael@0: michael@0: // update the cull skip offset to point past this op. michael@0: fWriter.overwriteTAt(cullSkipOffset, fWriter.bytesWritten()); michael@0: michael@0: this->validate(initialOffset, size); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkSurface* SkPictureRecord::onNewSurface(const SkImageInfo& info) { michael@0: return SkSurface::NewPicture(info.fWidth, info.fHeight); michael@0: } michael@0: michael@0: void SkPictureRecord::trackBitmapUse(int bitmapID, size_t offset) { michael@0: #ifndef SK_ALLOW_BITMAP_TRACKING michael@0: return; michael@0: #endif michael@0: michael@0: if (!(fRecordFlags & SkPicture::kOptimizeForClippedPlayback_RecordingFlag)) { michael@0: return; michael@0: } michael@0: michael@0: if (SkBitmapHeap::INVALID_SLOT == bitmapID) { michael@0: return; michael@0: } michael@0: michael@0: if (NULL == fBitmapUseOffsets) { michael@0: fBitmapUseOffsets.reset(SkNEW(SkOffsetTable)); michael@0: } michael@0: michael@0: fBitmapUseOffsets->add(bitmapID, offset); michael@0: } michael@0: michael@0: int SkPictureRecord::addBitmap(const SkBitmap& bitmap) { michael@0: const int index = fBitmapHeap->insert(bitmap); michael@0: // In debug builds, a bad return value from insert() will crash, allowing for debugging. In michael@0: // release builds, the invalid value will be recorded so that the reader will know that there michael@0: // was a problem. michael@0: SkASSERT(index != SkBitmapHeap::INVALID_SLOT); michael@0: this->addInt(index); michael@0: return index; michael@0: } michael@0: michael@0: void SkPictureRecord::addMatrix(const SkMatrix& matrix) { michael@0: fWriter.writeMatrix(matrix); michael@0: } michael@0: michael@0: const SkFlatData* SkPictureRecord::getFlatPaintData(const SkPaint& paint) { michael@0: return fPaints.findAndReturnFlat(paint); michael@0: } michael@0: michael@0: const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) { michael@0: const SkFlatData* data = paint ? getFlatPaintData(*paint) : NULL; michael@0: this->addFlatPaint(data); michael@0: return data; michael@0: } michael@0: michael@0: void SkPictureRecord::addFlatPaint(const SkFlatData* flatPaint) { michael@0: int index = flatPaint ? flatPaint->index() : 0; michael@0: this->addInt(index); michael@0: } michael@0: michael@0: int SkPictureRecord::addPathToHeap(const SkPath& path) { michael@0: if (NULL == fPathHeap) { michael@0: fPathHeap = SkNEW(SkPathHeap); michael@0: } michael@0: #ifdef SK_DEDUP_PICTURE_PATHS michael@0: return fPathHeap->insert(path); michael@0: #else michael@0: return fPathHeap->append(path); michael@0: #endif michael@0: } michael@0: michael@0: void SkPictureRecord::addPath(const SkPath& path) { michael@0: this->addInt(this->addPathToHeap(path)); michael@0: } michael@0: michael@0: void SkPictureRecord::addPicture(SkPicture& picture) { michael@0: int index = fPictureRefs.find(&picture); michael@0: if (index < 0) { // not found michael@0: index = fPictureRefs.count(); michael@0: *fPictureRefs.append() = &picture; michael@0: picture.ref(); michael@0: } michael@0: // follow the convention of recording a 1-based index michael@0: this->addInt(index + 1); michael@0: } michael@0: michael@0: void SkPictureRecord::addPoint(const SkPoint& point) { michael@0: #ifdef SK_DEBUG_SIZE michael@0: size_t start = fWriter.bytesWritten(); michael@0: #endif michael@0: fWriter.writePoint(point); michael@0: #ifdef SK_DEBUG_SIZE michael@0: fPointBytes += fWriter.bytesWritten() - start; michael@0: fPointWrites++; michael@0: #endif michael@0: } michael@0: michael@0: void SkPictureRecord::addPoints(const SkPoint pts[], int count) { michael@0: fWriter.writeMul4(pts, count * sizeof(SkPoint)); michael@0: #ifdef SK_DEBUG_SIZE michael@0: fPointBytes += count * sizeof(SkPoint); michael@0: fPointWrites++; michael@0: #endif michael@0: } michael@0: michael@0: void SkPictureRecord::addRect(const SkRect& rect) { michael@0: #ifdef SK_DEBUG_SIZE michael@0: size_t start = fWriter.bytesWritten(); michael@0: #endif michael@0: fWriter.writeRect(rect); michael@0: #ifdef SK_DEBUG_SIZE michael@0: fRectBytes += fWriter.bytesWritten() - start; michael@0: fRectWrites++; michael@0: #endif michael@0: } michael@0: michael@0: void SkPictureRecord::addRectPtr(const SkRect* rect) { michael@0: if (fWriter.writeBool(rect != NULL)) { michael@0: fWriter.writeRect(*rect); michael@0: } michael@0: } michael@0: michael@0: void SkPictureRecord::addIRect(const SkIRect& rect) { michael@0: fWriter.write(&rect, sizeof(rect)); michael@0: } michael@0: michael@0: void SkPictureRecord::addIRectPtr(const SkIRect* rect) { michael@0: if (fWriter.writeBool(rect != NULL)) { michael@0: *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect; michael@0: } michael@0: } michael@0: michael@0: void SkPictureRecord::addRRect(const SkRRect& rrect) { michael@0: fWriter.writeRRect(rrect); michael@0: } michael@0: michael@0: void SkPictureRecord::addRegion(const SkRegion& region) { michael@0: fWriter.writeRegion(region); michael@0: } michael@0: michael@0: void SkPictureRecord::addText(const void* text, size_t byteLength) { michael@0: #ifdef SK_DEBUG_SIZE michael@0: size_t start = fWriter.bytesWritten(); michael@0: #endif michael@0: addInt(byteLength); michael@0: fWriter.writePad(text, byteLength); michael@0: #ifdef SK_DEBUG_SIZE michael@0: fTextBytes += fWriter.bytesWritten() - start; michael@0: fTextWrites++; michael@0: #endif michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #ifdef SK_DEBUG_SIZE michael@0: size_t SkPictureRecord::size() const { michael@0: size_t result = 0; michael@0: size_t sizeData; michael@0: bitmaps(&sizeData); michael@0: result += sizeData; michael@0: matrices(&sizeData); michael@0: result += sizeData; michael@0: paints(&sizeData); michael@0: result += sizeData; michael@0: paths(&sizeData); michael@0: result += sizeData; michael@0: pictures(&sizeData); michael@0: result += sizeData; michael@0: regions(&sizeData); michael@0: result += sizeData; michael@0: result += streamlen(); michael@0: return result; michael@0: } michael@0: michael@0: int SkPictureRecord::bitmaps(size_t* size) const { michael@0: size_t result = 0; michael@0: int count = fBitmaps.count(); michael@0: for (int index = 0; index < count; index++) michael@0: result += sizeof(fBitmaps[index]) + fBitmaps[index]->size(); michael@0: *size = result; michael@0: return count; michael@0: } michael@0: michael@0: int SkPictureRecord::matrices(size_t* size) const { michael@0: int count = fMatrices.count(); michael@0: *size = sizeof(fMatrices[0]) * count; michael@0: return count; michael@0: } michael@0: michael@0: int SkPictureRecord::paints(size_t* size) const { michael@0: size_t result = 0; michael@0: int count = fPaints.count(); michael@0: for (int index = 0; index < count; index++) michael@0: result += sizeof(fPaints[index]) + fPaints[index]->size(); michael@0: *size = result; michael@0: return count; michael@0: } michael@0: michael@0: int SkPictureRecord::paths(size_t* size) const { michael@0: size_t result = 0; michael@0: int count = fPaths.count(); michael@0: for (int index = 0; index < count; index++) michael@0: result += sizeof(fPaths[index]) + fPaths[index]->size(); michael@0: *size = result; michael@0: return count; michael@0: } michael@0: michael@0: int SkPictureRecord::regions(size_t* size) const { michael@0: size_t result = 0; michael@0: int count = fRegions.count(); michael@0: for (int index = 0; index < count; index++) michael@0: result += sizeof(fRegions[index]) + fRegions[index]->size(); michael@0: *size = result; michael@0: return count; michael@0: } michael@0: michael@0: size_t SkPictureRecord::streamlen() const { michael@0: return fWriter.size(); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef SK_DEBUG_VALIDATE michael@0: void SkPictureRecord::validate(uint32_t initialOffset, uint32_t size) const { michael@0: SkASSERT(fWriter.size() == initialOffset + size); michael@0: michael@0: validateBitmaps(); michael@0: validateMatrices(); michael@0: validatePaints(); michael@0: validatePaths(); michael@0: validateRegions(); michael@0: } michael@0: michael@0: void SkPictureRecord::validateBitmaps() const { michael@0: int count = fBitmapHeap->count(); michael@0: SkASSERT((unsigned) count < 0x1000); michael@0: for (int index = 0; index < count; index++) { michael@0: const SkBitmap* bitPtr = fBitmapHeap->getBitmap(index); michael@0: SkASSERT(bitPtr); michael@0: bitPtr->validate(); michael@0: } michael@0: } michael@0: michael@0: void SkPictureRecord::validateMatrices() const { michael@0: int count = fMatrices.count(); michael@0: SkASSERT((unsigned) count < 0x1000); michael@0: for (int index = 0; index < count; index++) { michael@0: const SkFlatData* matrix = fMatrices[index]; michael@0: SkASSERT(matrix); michael@0: // matrix->validate(); michael@0: } michael@0: } michael@0: michael@0: void SkPictureRecord::validatePaints() const { michael@0: int count = fPaints.count(); michael@0: SkASSERT((unsigned) count < 0x1000); michael@0: for (int index = 0; index < count; index++) { michael@0: const SkFlatData* paint = fPaints[index]; michael@0: SkASSERT(paint); michael@0: // paint->validate(); michael@0: } michael@0: } michael@0: michael@0: void SkPictureRecord::validatePaths() const { michael@0: if (NULL == fPathHeap) { michael@0: return; michael@0: } michael@0: michael@0: int count = fPathHeap->count(); michael@0: SkASSERT((unsigned) count < 0x1000); michael@0: for (int index = 0; index < count; index++) { michael@0: const SkPath& path = (*fPathHeap)[index]; michael@0: path.validate(); michael@0: } michael@0: } michael@0: michael@0: void SkPictureRecord::validateRegions() const { michael@0: int count = fRegions.count(); michael@0: SkASSERT((unsigned) count < 0x1000); michael@0: for (int index = 0; index < count; index++) { michael@0: const SkFlatData* region = fRegions[index]; michael@0: SkASSERT(region); michael@0: // region->validate(); michael@0: } michael@0: } michael@0: #endif