Chromium Code Reviews| Index: src/core/SkPictureRecord.cpp |
| =================================================================== |
| --- src/core/SkPictureRecord.cpp (revision 9110) |
| +++ src/core/SkPictureRecord.cpp (working copy) |
| @@ -63,75 +63,97 @@ |
| /////////////////////////////////////////////////////////////////////////////// |
| +enum DrawTypeFlags { |
| + kHasPaint_DrawTypeFlag = 1 << 0, |
| + kModifiesState_DrawTypeFlag = 1 << 1, // modifes clip or matrix state |
| + kCanRecordBounds_DrawTypeFlag = 1 << 2, |
| +}; |
| + |
| +static const uint8_t gDrawTypeFlags[LAST_DRAWTYPE_ENUM + 1] = { |
| + 0, // UNUSED |
| + kModifiesState_DrawTypeFlag, // CLIP_PATH |
| + kModifiesState_DrawTypeFlag, // CLIP_REGION |
| + kModifiesState_DrawTypeFlag, // CLIP_RECT |
| + kModifiesState_DrawTypeFlag, // CLIP_RRECT |
| + kModifiesState_DrawTypeFlag, // CONCAT |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_BITMAP |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_BITMAP_MATRIX |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_BITMAP_NINE |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_BITMAP_RECT_TO_RECT |
| + 0, // DRAW_CLEAR |
| + 0, // DRAW_DATA |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_OVAL |
| + kHasPaint_DrawTypeFlag, // DRAW_PAINT |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_PATH |
| + kModifiesState_DrawTypeFlag, // DRAW_PICTURE |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_POINTS |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_POS_TEXT |
| + kHasPaint_DrawTypeFlag, // DRAW_POS_TEXT_TOP_BOTTOM |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_POS_TEXT_H |
| + kHasPaint_DrawTypeFlag, // DRAW_POS_TEXT_H_TOP_BOTTOM |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_RECT |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_RRECT |
| + kHasPaint_DrawTypeFlag, // DRAW_SPRITE |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_TEXT |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_TEXT_ON_PATH |
| + kHasPaint_DrawTypeFlag, // DRAW_TEXT_TOP_BOTTOM |
| + kHasPaint_DrawTypeFlag | kCanRecordBounds_DrawTypeFlag, // DRAW_VERTICES |
| + kModifiesState_DrawTypeFlag, // RESTORE |
| + kModifiesState_DrawTypeFlag, // ROTATE |
| + kModifiesState_DrawTypeFlag, // SAVE |
| + kHasPaint_DrawTypeFlag | kModifiesState_DrawTypeFlag, // SAVE_LAYER |
| + kModifiesState_DrawTypeFlag, // SCALE |
| + kModifiesState_DrawTypeFlag, // SET_MATRIX |
| + kModifiesState_DrawTypeFlag, // SKEW |
| + kModifiesState_DrawTypeFlag, // TRANSLATE |
| + 0, // NOOP |
| +}; |
| + |
| +bool SkPictureRecord::canRecordBounds(DrawType op) { |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + SkASSERT((unsigned)op <= (unsigned)LAST_DRAWTYPE_ENUM); |
| + return SkToBool(gDrawTypeFlags[op] & kCanRecordBounds_DrawTypeFlag); |
| +#else |
| + return false; |
| +#endif |
| +} |
| + |
| +static inline bool modifies_state(DrawType op) { |
| + SkASSERT((unsigned)op <= (unsigned)LAST_DRAWTYPE_ENUM); |
| + return SkToBool(gDrawTypeFlags[op] & kModifiesState_DrawTypeFlag); |
| +} |
| + |
| // Return the offset of the paint inside a given op's byte stream. A zero |
| // return value means there is no paint (and you really shouldn't be calling |
| // this method) |
| static inline uint32_t getPaintOffset(DrawType op, uint32_t opSize) { |
| // These offsets are where the paint would be if the op size doesn't overflow |
| - static const uint8_t gPaintOffsets[LAST_DRAWTYPE_ENUM + 1] = { |
| - 0, // UNUSED - no paint |
| - 0, // CLIP_PATH - no paint |
| - 0, // CLIP_REGION - no paint |
| - 0, // CLIP_RECT - no paint |
| - 0, // CLIP_RRECT - no paint |
| - 0, // CONCAT - no paint |
| - 1, // DRAW_BITMAP - right after op code |
| - 1, // DRAW_BITMAP_MATRIX - right after op code |
| - 1, // DRAW_BITMAP_NINE - right after op code |
| - 1, // DRAW_BITMAP_RECT_TO_RECT - right after op code |
| - 0, // DRAW_CLEAR - no paint |
| - 0, // DRAW_DATA - no paint |
| - 1, // DRAW_OVAL - right after op code |
| - 1, // DRAW_PAINT - right after op code |
| - 1, // DRAW_PATH - right after op code |
| - 0, // DRAW_PICTURE - no paint |
| - 1, // DRAW_POINTS - right after op code |
| - 1, // DRAW_POS_TEXT - right after op code |
| - 1, // DRAW_POS_TEXT_TOP_BOTTOM - right after op code |
| - 1, // DRAW_POS_TEXT_H - right after op code |
| - 1, // DRAW_POS_TEXT_H_TOP_BOTTOM - right after op code |
| - 1, // DRAW_RECT - right after op code |
| - 1, // DRAW_RRECT - right after op code |
| - 1, // DRAW_SPRITE - right after op code |
| - 1, // DRAW_TEXT - right after op code |
| - 1, // DRAW_TEXT_ON_PATH - right after op code |
| - 1, // DRAW_TEXT_TOP_BOTTOM - right after op code |
| - 1, // DRAW_VERTICES - right after op code |
| - 0, // RESTORE - no paint |
| - 0, // ROTATE - no paint |
| - 0, // SAVE - no paint |
| - 0, // SAVE_LAYER - see below - this paint's location varies |
| - 0, // SCALE - no paint |
| - 0, // SET_MATRIX - no paint |
| - 0, // SKEW - no paint |
| - 0, // TRANSLATE - no paint |
| - 0, // NOOP - no paint |
| - }; |
| - |
| - SkASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1); |
| SkASSERT((unsigned)op <= (unsigned)LAST_DRAWTYPE_ENUM); |
| - int overflow = 0; |
| + int offset = sizeof(uint32_t); // storage for op and 24-bit size |
| if (0 != (opSize & ~MASK_24) || opSize == MASK_24) { |
| // This op's size overflows so an extra uint32_t will be written |
| // after the op code |
| - overflow = sizeof(uint32_t); |
| + offset += sizeof(uint32_t); |
| } |
| + if (SkPictureRecord::canRecordBounds(op)) { |
| + offset += sizeof(SkIRect); |
| + } |
| if (SAVE_LAYER == op) { |
| - static const uint32_t kSaveLayerNoBoundsPaintOffset = 2 * kUInt32Size; |
| - static const uint32_t kSaveLayerWithBoundsPaintOffset = 2 * kUInt32Size + sizeof(SkRect); |
| + static const uint32_t kSaveLayerNoBoundsPaintOffset = kUInt32Size; |
| + static const uint32_t kSaveLayerWithBoundsPaintOffset = kUInt32Size + sizeof(SkRect); |
| if (kSaveLayerNoBoundsSize == opSize) { |
| - return kSaveLayerNoBoundsPaintOffset + overflow; |
| + offset += kSaveLayerNoBoundsPaintOffset; |
| } else { |
| SkASSERT(kSaveLayerWithBoundsSize == opSize); |
| - return kSaveLayerWithBoundsPaintOffset + overflow; |
| + offset += kSaveLayerWithBoundsPaintOffset; |
| } |
| } |
| - SkASSERT(0 != gPaintOffsets[op]); // really shouldn't be calling this method |
| - return gPaintOffsets[op] * sizeof(uint32_t) + overflow; |
| + SkASSERT(kHasPaint_DrawTypeFlag & gDrawTypeFlags[op]); // Shouldn't be calling this method |
| + return offset; |
| } |
| SkDevice* SkPictureRecord::setDevice(SkDevice* device) { |
| @@ -147,6 +169,7 @@ |
| // op + flags |
| uint32_t size = 2 * kUInt32Size; |
| uint32_t initialOffset = this->addDraw(SAVE, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addInt(flags); |
| validate(initialOffset, size); |
| @@ -170,8 +193,9 @@ |
| SkASSERT(kSaveLayerNoBoundsSize == size || kSaveLayerWithBoundsSize == size); |
| uint32_t initialOffset = this->addDraw(SAVE_LAYER, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addRectPtr(bounds); |
| - SkASSERT(initialOffset+getPaintOffset(SAVE_LAYER, size) == fWriter.size()); |
| + SkASSERT(initialOffset + getPaintOffset(SAVE_LAYER, size) == fWriter.size()); |
| addPaintPtr(paint); |
| addInt(flags); |
| @@ -586,6 +610,7 @@ |
| size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code |
| initialOffset = this->addDraw(RESTORE, &size); |
| } |
| + SkASSERT(kInvalidOffset != initialOffset); |
| fRestoreOffsetStack.pop(); |
| @@ -593,10 +618,91 @@ |
| return this->INHERITED::restore(); |
| } |
| +uint32_t SkPictureRecord::addDraw(DrawType drawType, uint32_t* size, |
| + const SkPaint* paint, const SkRect* localBounds) { |
| + SkIRect devClipBounds; // Bounds of the current clip in device space |
| + if (!this->getClipDeviceBounds(&devClipBounds) && !modifies_state(drawType)) { |
| + // Optimize-out calls that are clipped-out. State modifying commands |
| + // can not be optimized-out because they may have an impact on future calls |
| + // if the clip is ever expanded. For example, with a union clip op. |
| + return kInvalidOffset; |
| + } |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + bool writeBounds = canRecordBounds(drawType); |
| + SkIRect clippedDevBounds; // Bounds of the clipped draw in device space |
|
Tom Hudson
2013/05/14 17:00:49
Nit: clippedDevBounds and devClipBounds are a bit
|
| + if (writeBounds) { |
| + SkASSERT(!modifies_state(drawType)); // Ensure devClipBounds is initialized |
|
Tom Hudson
2013/05/14 17:00:49
Not obvious how the comment follows from the asser
|
| + if (NULL == localBounds || (NULL != paint && !paint->canComputeFastBounds())) { |
| + clippedDevBounds = devClipBounds; |
| + } else { |
| + SkRect paintBounds; |
| + SkRect rectifiedBounds = *localBounds; |
| + rectifiedBounds.sort(); |
| + if (NULL != paint) { |
| + if (drawType == DRAW_POINTS) { |
| + paintBounds = paint->computeFastStrokeBounds(rectifiedBounds, &paintBounds); |
| + } else { |
| + paintBounds = paint->computeFastBounds(rectifiedBounds, &paintBounds); |
| + } |
| + } else { |
| + paintBounds = *localBounds; |
| + } |
| + if (paintBounds.isEmpty()) { |
| + // Area affected by draw op is empty even after accounting |
| + // for stroke width. |
| + return kInvalidOffset; |
| + } |
| + SkRect devBounds; |
| + this->getTotalMatrix().mapRect(&devBounds, paintBounds); |
| + devBounds.roundOut(&clippedDevBounds); |
| + if (!clippedDevBounds.intersect(devClipBounds)) { |
| + // Draw lies outside of clip. The clip can only get further |
| + // restricted at playback time, so it is safe to reject during |
| + // record stage. |
| + return kInvalidOffset; |
| + } |
| + } |
| + *size += sizeof(SkIRect); |
| + } |
| +#else |
| + sk_ignore_unused_variable(paint); |
| + sk_ignore_unused_variable(localBounds); |
| +#endif |
| + |
| + uint32_t offset = fWriter.size(); |
| + |
| + this->predrawNotify(); |
| + |
| +#ifdef SK_DEBUG_TRACE |
| + SkDebugf("add %s\n", DrawTypeToString(drawType)); |
| +#endif |
| + SkASSERT(0 != *size); |
| + SkASSERT(((uint8_t) drawType) == drawType); |
| + if (0 != (*size & ~MASK_24) || *size == MASK_24) { |
| + fWriter.writeInt(PACK_8_24(drawType, MASK_24)); |
| + *size += 1; |
| + fWriter.writeInt(*size); |
| + } else { |
| + fWriter.writeInt(PACK_8_24(drawType, *size)); |
| + } |
| + |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + if (writeBounds) { |
| + fWriter.write(&clippedDevBounds, sizeof(clippedDevBounds)); |
| + fLastDrawBounds = clippedDevBounds; |
| + } else { |
| + fLastDrawBounds = devClipBounds; |
| + } |
| +#endif |
| + |
| + return offset; |
| +} |
| + |
| bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) { |
| // op + dx + dy |
| uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar); |
| uint32_t initialOffset = this->addDraw(TRANSLATE, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addScalar(dx); |
| addScalar(dy); |
| validate(initialOffset, size); |
| @@ -607,6 +713,7 @@ |
| // op + sx + sy |
| uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar); |
| uint32_t initialOffset = this->addDraw(SCALE, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addScalar(sx); |
| addScalar(sy); |
| validate(initialOffset, size); |
| @@ -617,6 +724,7 @@ |
| // op + degrees |
| uint32_t size = 1 * kUInt32Size + sizeof(SkScalar); |
| uint32_t initialOffset = this->addDraw(ROTATE, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addScalar(degrees); |
| validate(initialOffset, size); |
| return this->INHERITED::rotate(degrees); |
| @@ -626,6 +734,7 @@ |
| // op + sx + sy |
| uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar); |
| uint32_t initialOffset = this->addDraw(SKEW, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addScalar(sx); |
| addScalar(sy); |
| validate(initialOffset, size); |
| @@ -637,6 +746,7 @@ |
| // op + matrix index |
| uint32_t size = 2 * kUInt32Size; |
| uint32_t initialOffset = this->addDraw(CONCAT, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addMatrix(matrix); |
| validate(initialOffset, size); |
| return this->INHERITED::concat(matrix); |
| @@ -647,6 +757,7 @@ |
| // op + matrix index |
| uint32_t size = 2 * kUInt32Size; |
| uint32_t initialOffset = this->addDraw(SET_MATRIX, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addMatrix(matrix); |
| validate(initialOffset, size); |
| this->INHERITED::setMatrix(matrix); |
| @@ -728,6 +839,7 @@ |
| size += kUInt32Size; |
| } |
| uint32_t initialOffset = this->addDraw(CLIP_RECT, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addRect(rect); |
| addInt(ClipParams_pack(op, doAA)); |
| recordRestoreOffsetPlaceholder(op); |
| @@ -749,6 +861,7 @@ |
| size += kUInt32Size; |
| } |
| uint32_t initialOffset = this->addDraw(CLIP_RRECT, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addRRect(rrect); |
| addInt(ClipParams_pack(op, doAA)); |
| recordRestoreOffsetPlaceholder(op); |
| @@ -777,6 +890,7 @@ |
| size += kUInt32Size; |
| } |
| uint32_t initialOffset = this->addDraw(CLIP_PATH, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addPath(path); |
| addInt(ClipParams_pack(op, doAA)); |
| recordRestoreOffsetPlaceholder(op); |
| @@ -870,6 +984,7 @@ |
| size += kUInt32Size; |
| } |
| uint32_t initialOffset = this->addDraw(CLIP_REGION, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addRegion(region); |
| addInt(ClipParams_pack(op, false)); |
| recordRestoreOffsetPlaceholder(op); |
| @@ -882,6 +997,9 @@ |
| // op + color |
| uint32_t size = 2 * kUInt32Size; |
| uint32_t initialOffset = this->addDraw(DRAW_CLEAR, &size); |
| + if (kInvalidOffset == initialOffset) { |
|
Tom Hudson
2013/05/14 17:00:49
Why not asserts here as above?
|
| + return; |
| + } |
| addInt(color); |
| validate(initialOffset, size); |
| } |
| @@ -890,7 +1008,10 @@ |
| // op + paint index |
| uint32_t size = 2 * kUInt32Size; |
| uint32_t initialOffset = this->addDraw(DRAW_PAINT, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_PAINT, size) == fWriter.size()); |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_PAINT, size) == fWriter.size()); |
| addPaint(paint); |
| validate(initialOffset, size); |
| } |
| @@ -899,8 +1020,17 @@ |
| const SkPaint& paint) { |
| // op + paint index + mode + count + point data |
| uint32_t size = 4 * kUInt32Size + count * sizeof(SkPoint); |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + SkRect bbox; |
| + bbox.set(pts, count); |
| + uint32_t initialOffset = this->addDraw(DRAW_POINTS, &size, &paint, &bbox); |
| +#else |
| uint32_t initialOffset = this->addDraw(DRAW_POINTS, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_POINTS, size) == fWriter.size()); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_POINTS, size) == fWriter.size()); |
| addPaint(paint); |
| addInt(mode); |
| addInt(count); |
| @@ -911,8 +1041,15 @@ |
| void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) { |
| // op + paint index + rect |
| uint32_t size = 2 * kUInt32Size + sizeof(oval); |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + uint32_t initialOffset = this->addDraw(DRAW_OVAL, &size, &paint, &oval); |
| +#else |
| uint32_t initialOffset = this->addDraw(DRAW_OVAL, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.size()); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_OVAL, size) == fWriter.size()); |
| addPaint(paint); |
| addRect(oval); |
| validate(initialOffset, size); |
| @@ -921,8 +1058,15 @@ |
| void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) { |
| // op + paint index + rect |
| uint32_t size = 2 * kUInt32Size + sizeof(rect); |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + uint32_t initialOffset = this->addDraw(DRAW_RECT, &size, &paint, &rect); |
| +#else |
| uint32_t initialOffset = this->addDraw(DRAW_RECT, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.size()); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_RECT, size) == fWriter.size()); |
| addPaint(paint); |
| addRect(rect); |
| validate(initialOffset, size); |
| @@ -935,10 +1079,16 @@ |
| this->SkPictureRecord::drawOval(rrect.getBounds(), paint); |
| } else { |
| // op + paint index + rrect |
| - uint32_t initialOffset, size; |
| - size = 2 * kUInt32Size + SkRRect::kSizeInMemory; |
| - initialOffset = this->addDraw(DRAW_RRECT, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_RRECT, size) == fWriter.size()); |
| + uint32_t size = 2 * kUInt32Size + SkRRect::kSizeInMemory; |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + uint32_t initialOffset = this->addDraw(DRAW_RRECT, &size, &paint, &rrect.getBounds()); |
| +#else |
| + uint32_t initialOffset = this->addDraw(DRAW_RRECT, &size); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_RRECT, size) == fWriter.size()); |
| addPaint(paint); |
| addRRect(rrect); |
| validate(initialOffset, size); |
| @@ -948,8 +1098,16 @@ |
| void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) { |
| // op + paint index + path index |
| uint32_t size = 3 * kUInt32Size; |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + const SkRect* bounds = path.isInverseFillType() ? NULL : &path.getBounds(); |
| + uint32_t initialOffset = this->addDraw(DRAW_PATH, &size, &paint, bounds); |
| +#else |
| uint32_t initialOffset = this->addDraw(DRAW_PATH, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_PATH, size) == fWriter.size()); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_PATH, size) == fWriter.size()); |
| addPaint(paint); |
| addPath(path); |
| validate(initialOffset, size); |
| @@ -959,8 +1117,16 @@ |
| const SkPaint* paint = NULL) { |
| // op + paint index + bitmap index + left + top |
| uint32_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar); |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + SkRect bounds = SkRect::MakeXYWH(left, top, bitmap.width(), bitmap.height()); |
| + uint32_t initialOffset = this->addDraw(DRAW_BITMAP, &size, paint, &bounds); |
| +#else |
| uint32_t initialOffset = this->addDraw(DRAW_BITMAP, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP, size) == fWriter.size()); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_BITMAP, size) == fWriter.size()); |
| addPaintPtr(paint); |
| addBitmap(bitmap); |
| addScalar(left); |
| @@ -977,8 +1143,15 @@ |
| } |
| size += sizeof(dst); // + rect |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + uint32_t initialOffset = this->addDraw(DRAW_BITMAP_RECT_TO_RECT, &size, paint, &dst); |
| +#else |
| uint32_t initialOffset = this->addDraw(DRAW_BITMAP_RECT_TO_RECT, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_RECT_TO_RECT, size) == fWriter.size()); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_BITMAP_RECT_TO_RECT, size) == fWriter.size()); |
| addPaintPtr(paint); |
| addBitmap(bitmap); |
| addRectPtr(src); // may be null |
| @@ -990,8 +1163,17 @@ |
| const SkPaint* paint) { |
| // id + paint index + bitmap index + matrix index |
| uint32_t size = 4 * kUInt32Size; |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + SkRect bounds = SkRect::MakeWH(bitmap.width(), bitmap.height()); |
| + matrix.mapRect(&bounds); |
| + uint32_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size, paint, &bounds); |
| +#else |
| uint32_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_MATRIX, size) == fWriter.size()); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_BITMAP_MATRIX, size) == fWriter.size()); |
| addPaintPtr(paint); |
| addBitmap(bitmap); |
| addMatrix(matrix); |
| @@ -1002,8 +1184,15 @@ |
| const SkRect& dst, const SkPaint* paint) { |
| // op + paint index + bitmap id + center + dst rect |
| uint32_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst); |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + uint32_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size, paint, &dst); |
| +#else |
| uint32_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_NINE, size) == fWriter.size()); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_BITMAP_NINE, size) == fWriter.size()); |
| addPaintPtr(paint); |
| addBitmap(bitmap); |
| addIRect(center); |
| @@ -1012,11 +1201,19 @@ |
| } |
| void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top, |
| - const SkPaint* paint = NULL) { |
| + const SkPaint* paint) { |
| // op + paint index + bitmap index + left + top |
| uint32_t size = 5 * kUInt32Size; |
| + // Note: Bounds encoding not supported on drawSprite. Because drawSprite |
| + // ignores the current transform matrix, the bounds will not follow the |
| + // playback canvas's initial tranform matrix at playback time, and |
| + // recorded bounds are assumed to be in the playback canvas' initial |
| + // local frame of reference. |
| uint32_t initialOffset = this->addDraw(DRAW_SPRITE, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_SPRITE, size) == fWriter.size()); |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_SPRITE, size) == fWriter.size()); |
| addPaintPtr(paint); |
| addBitmap(bitmap); |
| addInt(left); |
| @@ -1024,6 +1221,7 @@ |
| validate(initialOffset, size); |
| } |
| +#if !(SK_RECORD_BOUNDS_IN_PICTURE) |
| // Return fontmetrics.fTop,fBottom in topbot[0,1], after they have been |
| // tweaked by paint.computeFastBounds(). |
| // |
| @@ -1048,28 +1246,95 @@ |
| addScalar(flat.topBot()[0] + minY); |
| addScalar(flat.topBot()[1] + maxY); |
| } |
| +#endif |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| +static void pad_text_bbox_horizontally(const SkPaint::FontMetrics& metrics, SkRect* bbox) { |
| + // Pad horizontal bounds on each side by max vertical extents. |
| + // This is sort of arbitrary, but seems to produce reasonable results. |
| + // If there were a way of getting max glyph X-extents to pad by, that |
| + // may be better here, but FontMetrics fXMin and fXMax seem incorrect |
| + // on most platforms (too small in Linux, never even set in Windows). |
| + SkScalar pad = (metrics.fBottom - metrics.fTop); |
| + SkASSERT(pad > 0); |
| + bbox->fLeft -= pad; |
| + bbox->fRight += pad; |
| +} |
| +#endif |
| + |
| void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x, |
| SkScalar y, const SkPaint& paint) { |
| - bool fast = !paint.isVerticalText() && paint.canComputeFastBounds(); |
| + // Note: The DRAW_TEXT_TOP_BOTTOM optimization is redundant if we are |
| + // already recording full bounds. |
| // op + paint index + length + 'length' worth of chars + x + y |
| uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * sizeof(SkScalar); |
| + |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + SkRect bbox; |
| + paint.measureText(text, byteLength, &bbox); |
| + SkPaint::FontMetrics metrics; |
| + paint.getFontMetrics(&metrics); |
| + |
| + // Vertical and aligned text need to be offset |
| + if (paint.isVerticalText()) { |
| + SkScalar h = bbox.fBottom - bbox.fTop; |
| + if (paint.getTextAlign() == SkPaint::kCenter_Align) { |
| + bbox.fTop -= h / 2; |
| + bbox.fBottom -= h / 2; |
| + } |
| + // Pad top and bottom with max extents from FontMetrics |
| + bbox.fBottom += metrics.fBottom; |
| + bbox.fTop += metrics.fTop; |
| + } else { |
| + SkScalar w = bbox.fRight - bbox.fLeft; |
| + if (paint.getTextAlign() == SkPaint::kCenter_Align) { |
| + bbox.fLeft -= w / 2; |
| + bbox.fRight -= w / 2; |
| + } else if (paint.getTextAlign() == SkPaint::kRight_Align) { |
| + bbox.fLeft -= w; |
| + bbox.fRight -= w; |
| + } |
| + // Set vertical bounds to max extents from font metrics |
| + bbox.fTop = metrics.fTop; |
| + bbox.fBottom = metrics.fBottom; |
| + } |
| + pad_text_bbox_horizontally(metrics, &bbox); |
| + bbox.fLeft += x; |
| + bbox.fRight += x; |
| + bbox.fTop += y; |
| + bbox.fBottom += y; |
| + uint32_t initialOffset = this->addDraw(DRAW_TEXT, &size, &paint, &bbox); |
| +#else |
| + bool fast = !paint.isVerticalText() && paint.canComputeFastBounds(); |
| + |
| if (fast) { |
| size += 2 * sizeof(SkScalar); // + top & bottom |
| } |
| - |
| DrawType op = fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT; |
| uint32_t initialOffset = this->addDraw(op, &size); |
| - SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size()); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_TEXT, size) == fWriter.size()); |
| +#else |
| + SkASSERT(initialOffset + getPaintOffset(op, size) == fWriter.size()); |
| +#endif |
| const SkFlatData* flatPaintData = addPaint(paint); |
| - SkASSERT(flatPaintData); |
| addText(text, byteLength); |
| addScalar(x); |
| addScalar(y); |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + // We still need to call addPaint even if we don't use flatPaintData |
| + sk_ignore_unused_variable(flatPaintData); |
| +#else |
| + SkASSERT(flatPaintData); |
| if (fast) { |
| addFontMetricsTopBottom(paint, *flatPaintData, y, y); |
| } |
| +#endif |
| validate(initialOffset, size); |
| } |
| @@ -1097,37 +1362,66 @@ |
| } |
| } |
| + // Note: The DRAW_TEXT_[H_]TOP_BOTTOM optimization is redundant if we are |
| + // already recording full bounds. |
| +#if !(SK_RECORD_BOUNDS_IN_PICTURE) |
| bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds(); |
| bool fast = canUseDrawH && fastBounds; |
| +#endif |
| // op + paint index + length + 'length' worth of data + num points |
| uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size; |
| if (canUseDrawH) { |
| +#if !(SK_RECORD_BOUNDS_IN_PICTURE) |
| if (fast) { |
| size += 2 * sizeof(SkScalar); // + top & bottom |
| } |
| +#endif |
| // + y-pos + actual x-point data |
| size += sizeof(SkScalar) + points * sizeof(SkScalar); |
| } else { |
| // + x&y point data |
| size += points * sizeof(SkPoint); |
| +#if !(SK_RECORD_BOUNDS_IN_PICTURE) |
| if (fastBounds) { |
| size += 2 * sizeof(SkScalar); // + top & bottom |
| } |
| +#endif |
| } |
| DrawType op; |
| +#if !(SK_RECORD_BOUNDS_IN_PICTURE) |
| if (fast) { |
| op = DRAW_POS_TEXT_H_TOP_BOTTOM; |
| - } else if (canUseDrawH) { |
| + } else |
| +#endif |
| + if (canUseDrawH) { |
| op = DRAW_POS_TEXT_H; |
| +#if !(SK_RECORD_BOUNDS_IN_PICTURE) |
| } else if (fastBounds) { |
| op = DRAW_POS_TEXT_TOP_BOTTOM; |
| +#endif |
| } else { |
| op = DRAW_POS_TEXT; |
| } |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + SkRect bbox; |
| + bbox.set(pos, paint.countText(text, byteLength)); |
| + SkPaint::FontMetrics metrics; |
| + paint.getFontMetrics(&metrics); |
| + bbox.fTop += metrics.fTop; |
| + bbox.fBottom += metrics.fBottom; |
| + |
| + // pad on left and right by half of max vertical glyph extents |
| + pad_text_bbox_horizontally(metrics, &bbox); |
| + uint32_t initialOffset = this->addDraw(op, &size, &paint, &bbox); |
| +#else |
| uint32_t initialOffset = this->addDraw(op, &size); |
| - SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size()); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(op, size) == fWriter.size()); |
| const SkFlatData* flatPaintData = addPaint(paint); |
| SkASSERT(flatPaintData); |
| addText(text, byteLength); |
| @@ -1137,18 +1431,28 @@ |
| size_t start = fWriter.size(); |
| #endif |
| if (canUseDrawH) { |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + // We still need to call addPaint even if we don't use flatPaintData |
| + sk_ignore_unused_variable(flatPaintData); |
| +#else |
| if (fast) { |
| addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY); |
| } |
| +#endif |
| addScalar(pos[0].fY); |
| SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar)); |
| for (size_t index = 0; index < points; index++) |
| *xptr++ = pos[index].fX; |
| } else { |
| fWriter.writeMul4(pos, points * sizeof(SkPoint)); |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + // We still need to call addPaint even if we don't use flatPaintData |
| + sk_ignore_unused_variable(flatPaintData); |
| +#else |
| if (fastBounds) { |
| addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY); |
| } |
| +#endif |
| } |
| #ifdef SK_DEBUG_SIZE |
| fPointBytes += fWriter.size() - start; |
| @@ -1160,35 +1464,70 @@ |
| void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength, |
| const SkScalar xpos[], SkScalar constY, |
| const SkPaint& paint) { |
| - size_t points = paint.countText(text, byteLength); |
| - if (0 == points) |
| + size_t numChars = paint.countText(text, byteLength); |
| + if (0 == numChars) |
| return; |
| - bool fast = !paint.isVerticalText() && paint.canComputeFastBounds(); |
| - |
| // op + paint index + length + 'length' worth of data + num points |
| uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size; |
| + |
| + // Note: The DRAW_POS_TEXT_H_TOP_BOTTOM optimization is redundant if we are |
| + // already recording full bounds. |
| +#if !(SK_RECORD_BOUNDS_IN_PICTURE) |
| + bool fast = !paint.isVerticalText() && paint.canComputeFastBounds(); |
| if (fast) { |
| size += 2 * sizeof(SkScalar); // + top & bottom |
| } |
| +#endif |
| + |
| // + y + the actual points |
| - size += 1 * kUInt32Size + points * sizeof(SkScalar); |
| + size += 1 * kUInt32Size + numChars * sizeof(SkScalar); |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + SkRect bbox; |
| + bbox.fLeft = xpos[0]; |
| + bbox.fRight = xpos[numChars - 1]; |
| + // if we had a guarantee that these will be monotonically increasing, this could be sped up |
|
Tom Hudson
2013/05/14 17:00:49
Would this break on RTL? Do we have a guarantee th
|
| + for (size_t i = 0; i < numChars; ++i) { |
| + if (xpos[i] < bbox.fLeft) { |
| + bbox.fLeft = xpos[i]; |
| + } |
| + if (xpos[i] > bbox.fRight) { |
| + bbox.fRight = xpos[i]; |
| + } |
| + } |
| + SkPaint::FontMetrics metrics; |
| + paint.getFontMetrics(&metrics); |
| + pad_text_bbox_horizontally(metrics, &bbox); |
| + bbox.fTop = metrics.fTop + constY; |
| + bbox.fBottom = metrics.fBottom + constY; |
| + uint32_t initialOffset = this->addDraw(DRAW_POS_TEXT_H, &size, &paint, &bbox); |
| +#else |
| uint32_t initialOffset = this->addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H, |
| &size); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_TEXT_ON_PATH, size) == fWriter.size()); |
| const SkFlatData* flatPaintData = addPaint(paint); |
| SkASSERT(flatPaintData); |
| addText(text, byteLength); |
| - addInt(points); |
| + addInt(numChars); |
| #ifdef SK_DEBUG_SIZE |
| size_t start = fWriter.size(); |
| #endif |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + // We still need to call addPaint even if we don't use flatPaintData |
| + sk_ignore_unused_variable(flatPaintData); |
| +#else |
| if (fast) { |
| addFontMetricsTopBottom(paint, *flatPaintData, constY, constY); |
| } |
| +#endif |
| addScalar(constY); |
| - fWriter.writeMul4(xpos, points * sizeof(SkScalar)); |
| + fWriter.writeMul4(xpos, numChars * sizeof(SkScalar)); |
| #ifdef SK_DEBUG_SIZE |
| fPointBytes += fWriter.size() - start; |
| fPointWrites += points; |
| @@ -1201,8 +1540,24 @@ |
| const SkPaint& paint) { |
| // op + paint index + length + 'length' worth of data + path index + matrix index |
| uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * kUInt32Size; |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + SkRect bbox = path.getBounds(); |
| + SkPaint::FontMetrics metrics; |
| + paint.getFontMetrics(&metrics); |
| + // pad out all sides by the max glyph height above baseline |
| + SkScalar pad = metrics.fTop; |
| + bbox.fLeft += pad; |
| + bbox.fRight -= pad; |
| + bbox.fTop += pad; |
| + bbox.fBottom -= pad; |
| + uint32_t initialOffset = this->addDraw(DRAW_TEXT_ON_PATH, &size, &paint, &bbox); |
| +#else |
| uint32_t initialOffset = this->addDraw(DRAW_TEXT_ON_PATH, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_TEXT_ON_PATH, size) == fWriter.size()); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_TEXT_ON_PATH, size) == fWriter.size()); |
| addPaint(paint); |
| addText(text, byteLength); |
| addPath(path); |
| @@ -1214,6 +1569,7 @@ |
| // op + picture index |
| uint32_t size = 2 * kUInt32Size; |
| uint32_t initialOffset = this->addDraw(DRAW_PICTURE, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addPicture(picture); |
| validate(initialOffset, size); |
| } |
| @@ -1246,9 +1602,17 @@ |
| // + num indices + indices |
| size += 1 * kUInt32Size + SkAlign4(indexCount * sizeof(uint16_t)); |
| } |
| - |
| +#if SK_RECORD_BOUNDS_IN_PICTURE |
| + SkRect bbox; |
| + bbox.set(vertices, vertexCount); |
| + uint32_t initialOffset = this->addDraw(DRAW_VERTICES, &size, &paint, &bbox); |
| +#else |
| uint32_t initialOffset = this->addDraw(DRAW_VERTICES, &size); |
| - SkASSERT(initialOffset+getPaintOffset(DRAW_VERTICES, size) == fWriter.size()); |
| +#endif |
| + if (kInvalidOffset == initialOffset) { |
| + return; |
| + } |
| + SkASSERT(initialOffset + getPaintOffset(DRAW_VERTICES, size) == fWriter.size()); |
| addPaint(paint); |
| addInt(flags); |
| addInt(vmode); |
| @@ -1271,6 +1635,7 @@ |
| // op + length + 'length' worth of data |
| uint32_t size = 2 * kUInt32Size + SkAlign4(length); |
| uint32_t initialOffset = this->addDraw(DRAW_DATA, &size); |
| + SkASSERT(kInvalidOffset != initialOffset); |
| addInt(length); |
| fWriter.writePad(data, length); |
| validate(initialOffset, size); |