blob: f7a7633faa6bc0e69be55bbb5bf109aa4c53d31c [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkPictureRecord.h"
9#include "SkTSearch.h"
junov@chromium.org4866cc02012-06-01 21:23:07 +000010#include "SkPixelRef.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000011#include "SkRRect.h"
rileya@google.com9f5898d2012-09-11 20:21:44 +000012#include "SkBBoxHierarchy.h"
junov@chromium.orgd575eed2013-05-08 15:39:13 +000013#include "SkDevice.h"
rileya@google.com9f5898d2012-09-11 20:21:44 +000014#include "SkPictureStateTree.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015
16#define MIN_WRITER_SIZE 16384
17#define HEAP_BLOCK_SIZE 4096
18
junov@chromium.org4e6dfa52012-07-16 14:04:59 +000019enum {
reed@google.comd86e7ab2012-09-27 20:31:31 +000020 // just need a value that save or getSaveCount would never return
junov@chromium.org4e6dfa52012-07-16 14:04:59 +000021 kNoInitialSave = -1,
22};
23
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +000024// A lot of basic types get stored as a uint32_t: bools, ints, paint indices, etc.
25static int const kUInt32Size = 4;
26
djsollen@google.comd4236572013-08-13 14:29:06 +000027static const uint32_t kSaveSize = 2 * kUInt32Size;
robertphillips@google.come37ad352013-03-01 19:44:30 +000028static const uint32_t kSaveLayerNoBoundsSize = 4 * kUInt32Size;
29static const uint32_t kSaveLayerWithBoundsSize = 4 * kUInt32Size + sizeof(SkRect);
30
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000031SkPictureRecord::SkPictureRecord(uint32_t flags, SkBaseDevice* device) :
reed@google.comd86e7ab2012-09-27 20:31:31 +000032 INHERITED(device),
robertphillips@google.com178a2672012-09-13 13:25:30 +000033 fBoundingHierarchy(NULL),
34 fStateTree(NULL),
djsollen@google.com21830d92012-08-07 19:49:41 +000035 fFlattenableHeap(HEAP_BLOCK_SIZE),
36 fMatrices(&fFlattenableHeap),
37 fPaints(&fFlattenableHeap),
38 fRegions(&fFlattenableHeap),
djsollen@google.comd2700ee2012-05-30 16:54:13 +000039 fWriter(MIN_WRITER_SIZE),
40 fRecordFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000041#ifdef SK_DEBUG_SIZE
42 fPointBytes = fRectBytes = fTextBytes = 0;
43 fPointWrites = fRectWrites = fTextWrites = 0;
44#endif
45
46 fRestoreOffsetStack.setReserve(32);
reed@google.com82065d62011-02-07 15:30:46 +000047
djsollen@google.comc9ab9872012-08-29 18:52:07 +000048 fBitmapHeap = SkNEW(SkBitmapHeap);
49 fFlattenableHeap.setBitmapStorage(fBitmapHeap);
reed@android.com8a1c16f2008-12-17 15:59:43 +000050 fPathHeap = NULL; // lazy allocate
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +000051 fFirstSavedLayerIndex = kNoSavedLayerIndex;
reed@google.comd86e7ab2012-09-27 20:31:31 +000052
53 fInitialSaveCount = kNoInitialSave;
reed@android.com8a1c16f2008-12-17 15:59:43 +000054}
55
56SkPictureRecord::~SkPictureRecord() {
djsollen@google.comc9ab9872012-08-29 18:52:07 +000057 SkSafeUnref(fBitmapHeap);
djsollen@google.com21830d92012-08-07 19:49:41 +000058 SkSafeUnref(fPathHeap);
rileya@google.com9f5898d2012-09-11 20:21:44 +000059 SkSafeUnref(fBoundingHierarchy);
60 SkSafeUnref(fStateTree);
djsollen@google.com21830d92012-08-07 19:49:41 +000061 fFlattenableHeap.setBitmapStorage(NULL);
62 fPictureRefs.unrefAll();
reed@android.com8a1c16f2008-12-17 15:59:43 +000063}
64
65///////////////////////////////////////////////////////////////////////////////
66
robertphillips@google.come37ad352013-03-01 19:44:30 +000067// Return the offset of the paint inside a given op's byte stream. A zero
68// return value means there is no paint (and you really shouldn't be calling
69// this method)
70static inline uint32_t getPaintOffset(DrawType op, uint32_t opSize) {
71 // These offsets are where the paint would be if the op size doesn't overflow
skia.committer@gmail.comf140f182013-03-02 07:01:56 +000072 static const uint8_t gPaintOffsets[LAST_DRAWTYPE_ENUM + 1] = {
robertphillips@google.come37ad352013-03-01 19:44:30 +000073 0, // UNUSED - no paint
74 0, // CLIP_PATH - no paint
75 0, // CLIP_REGION - no paint
76 0, // CLIP_RECT - no paint
77 0, // CLIP_RRECT - no paint
78 0, // CONCAT - no paint
79 1, // DRAW_BITMAP - right after op code
80 1, // DRAW_BITMAP_MATRIX - right after op code
81 1, // DRAW_BITMAP_NINE - right after op code
82 1, // DRAW_BITMAP_RECT_TO_RECT - right after op code
83 0, // DRAW_CLEAR - no paint
84 0, // DRAW_DATA - no paint
85 1, // DRAW_OVAL - right after op code
86 1, // DRAW_PAINT - right after op code
87 1, // DRAW_PATH - right after op code
88 0, // DRAW_PICTURE - no paint
89 1, // DRAW_POINTS - right after op code
90 1, // DRAW_POS_TEXT - right after op code
91 1, // DRAW_POS_TEXT_TOP_BOTTOM - right after op code
92 1, // DRAW_POS_TEXT_H - right after op code
93 1, // DRAW_POS_TEXT_H_TOP_BOTTOM - right after op code
94 1, // DRAW_RECT - right after op code
95 1, // DRAW_RRECT - right after op code
96 1, // DRAW_SPRITE - right after op code
97 1, // DRAW_TEXT - right after op code
98 1, // DRAW_TEXT_ON_PATH - right after op code
99 1, // DRAW_TEXT_TOP_BOTTOM - right after op code
100 1, // DRAW_VERTICES - right after op code
101 0, // RESTORE - no paint
102 0, // ROTATE - no paint
103 0, // SAVE - no paint
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000104 0, // SAVE_LAYER - see below - this paint's location varies
robertphillips@google.come37ad352013-03-01 19:44:30 +0000105 0, // SCALE - no paint
106 0, // SET_MATRIX - no paint
107 0, // SKEW - no paint
108 0, // TRANSLATE - no paint
109 0, // NOOP - no paint
robertphillips@google.com0a4805e2013-05-29 13:24:23 +0000110 0, // BEGIN_GROUP - no paint
111 0, // COMMENT - no paint
112 0, // END_GROUP - no paint
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000113 };
robertphillips@google.come37ad352013-03-01 19:44:30 +0000114
115 SkASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1);
116 SkASSERT((unsigned)op <= (unsigned)LAST_DRAWTYPE_ENUM);
117
118 int overflow = 0;
119 if (0 != (opSize & ~MASK_24) || opSize == MASK_24) {
120 // This op's size overflows so an extra uint32_t will be written
121 // after the op code
122 overflow = sizeof(uint32_t);
123 }
124
125 if (SAVE_LAYER == op) {
126 static const uint32_t kSaveLayerNoBoundsPaintOffset = 2 * kUInt32Size;
127 static const uint32_t kSaveLayerWithBoundsPaintOffset = 2 * kUInt32Size + sizeof(SkRect);
128
129 if (kSaveLayerNoBoundsSize == opSize) {
130 return kSaveLayerNoBoundsPaintOffset + overflow;
131 } else {
132 SkASSERT(kSaveLayerWithBoundsSize == opSize);
133 return kSaveLayerWithBoundsPaintOffset + overflow;
134 }
135 }
136
137 SkASSERT(0 != gPaintOffsets[op]); // really shouldn't be calling this method
138 return gPaintOffsets[op] * sizeof(uint32_t) + overflow;
139}
140
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000141SkBaseDevice* SkPictureRecord::setDevice(SkBaseDevice* device) {
mtklein@google.com330313a2013-08-22 15:37:26 +0000142 SkDEBUGFAIL("eeek, don't try to change the device on a recording canvas");
reed@google.comd86e7ab2012-09-27 20:31:31 +0000143 return this->INHERITED::setDevice(device);
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000144}
145
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146int SkPictureRecord::save(SaveFlags flags) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000147 // record the offset to us, making it non-positive to distinguish a save
148 // from a clip entry.
149 fRestoreOffsetStack.push(-(int32_t)fWriter.size());
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000150
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000151 // op + flags
djsollen@google.comd4236572013-08-13 14:29:06 +0000152 uint32_t size = kSaveSize;
robertphillips@google.com8b169312013-10-15 17:47:36 +0000153 size_t initialOffset = this->addDraw(SAVE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154 addInt(flags);
reed@google.com82065d62011-02-07 15:30:46 +0000155
robertphillips@google.com8b169312013-10-15 17:47:36 +0000156 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 return this->INHERITED::save(flags);
158}
159
160int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
161 SaveFlags flags) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000162 // record the offset to us, making it non-positive to distinguish a save
163 // from a clip entry.
164 fRestoreOffsetStack.push(-(int32_t)fWriter.size());
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000165
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000166 // op + bool for 'bounds'
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000167 uint32_t size = 2 * kUInt32Size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000168 if (NULL != bounds) {
169 size += sizeof(*bounds); // + rect
170 }
171 // + paint index + flags
172 size += 2 * kUInt32Size;
173
robertphillips@google.come37ad352013-03-01 19:44:30 +0000174 SkASSERT(kSaveLayerNoBoundsSize == size || kSaveLayerWithBoundsSize == size);
175
robertphillips@google.com8b169312013-10-15 17:47:36 +0000176 size_t initialOffset = this->addDraw(SAVE_LAYER, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 addRectPtr(bounds);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000178 SkASSERT(initialOffset+getPaintOffset(SAVE_LAYER, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 addPaintPtr(paint);
180 addInt(flags);
181
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000182 if (kNoSavedLayerIndex == fFirstSavedLayerIndex) {
183 fFirstSavedLayerIndex = fRestoreOffsetStack.count();
184 }
185
robertphillips@google.com8b169312013-10-15 17:47:36 +0000186 this->validate(initialOffset, size);
reed@android.com261ae4d2009-10-02 16:37:46 +0000187 /* Don't actually call saveLayer, because that will try to allocate an
188 offscreen device (potentially very big) which we don't actually need
189 at this time (and may not be able to afford since during record our
190 clip starts out the size of the picture, which is often much larger
191 than the size of the actual device we'll use during playback).
192 */
junov@chromium.orga907ac32012-02-24 21:54:07 +0000193 int count = this->INHERITED::save(flags);
194 this->clipRectBounds(bounds, flags, NULL);
195 return count;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196}
197
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000198bool SkPictureRecord::isDrawingToLayer() const {
199 return fFirstSavedLayerIndex != kNoSavedLayerIndex;
200}
201
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000202/*
203 * Read the op code from 'offset' in 'writer' and extract the size too.
204 */
205static DrawType peek_op_and_size(SkWriter32* writer, int32_t offset, uint32_t* size) {
206 uint32_t* peek = writer->peek32(offset);
reed@google.comffacd3c2012-08-30 15:31:23 +0000207
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000208 uint32_t op;
209 UNPACK_8_24(*peek, op, *size);
210 if (MASK_24 == *size) {
211 // size required its own slot right after the op code
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000212 *size = *writer->peek32(offset+kUInt32Size);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000213 }
214 return (DrawType) op;
reed@google.comffacd3c2012-08-30 15:31:23 +0000215}
216
217#ifdef TRACK_COLLAPSE_STATS
218 static int gCollapseCount, gCollapseCalls;
219#endif
220
robertphillips@google.come37ad352013-03-01 19:44:30 +0000221// Is the supplied paint simply a color?
222static bool is_simple(const SkPaint& p) {
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000223 intptr_t orAccum = (intptr_t)p.getPathEffect() |
robertphillips@google.come37ad352013-03-01 19:44:30 +0000224 (intptr_t)p.getShader() |
225 (intptr_t)p.getXfermode() |
226 (intptr_t)p.getMaskFilter() |
227 (intptr_t)p.getColorFilter() |
228 (intptr_t)p.getRasterizer() |
229 (intptr_t)p.getLooper() |
230 (intptr_t)p.getImageFilter();
231 return 0 == orAccum;
232}
233
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000234// CommandInfos are fed to the 'match' method and filled in with command
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000235// information.
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000236struct CommandInfo {
237 DrawType fActualOp;
238 uint32_t fOffset;
239 uint32_t fSize;
240};
241
reed@google.comffacd3c2012-08-30 15:31:23 +0000242/*
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000243 * Attempt to match the provided pattern of commands starting at 'offset'
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000244 * in the byte stream and stopping at the end of the stream. Upon success,
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000245 * return true with all the pattern information filled out in the result
246 * array (i.e., actual ops, offsets and sizes).
247 * Note this method skips any NOOPs seen in the stream
248 */
249static bool match(SkWriter32* writer, uint32_t offset,
250 int* pattern, CommandInfo* result, int numCommands) {
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000251 SkASSERT(offset < writer->size());
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000252
253 uint32_t curOffset = offset;
254 uint32_t curSize = 0;
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000255 int numMatched;
256 for (numMatched = 0; numMatched < numCommands && curOffset < writer->size(); ++numMatched) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000257 DrawType op = peek_op_and_size(writer, curOffset, &curSize);
258 while (NOOP == op && curOffset < writer->size()) {
259 curOffset += curSize;
260 op = peek_op_and_size(writer, curOffset, &curSize);
261 }
262
263 if (curOffset >= writer->size()) {
264 return false; // ran out of byte stream
265 }
266
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000267 if (kDRAW_BITMAP_FLAVOR == pattern[numMatched]) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000268 if (DRAW_BITMAP != op && DRAW_BITMAP_MATRIX != op &&
269 DRAW_BITMAP_NINE != op && DRAW_BITMAP_RECT_TO_RECT != op) {
270 return false;
271 }
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000272 } else if (op != pattern[numMatched]) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000273 return false;
274 }
275
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000276 result[numMatched].fActualOp = op;
277 result[numMatched].fOffset = curOffset;
278 result[numMatched].fSize = curSize;
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000279
280 curOffset += curSize;
281 }
282
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000283 if (numMatched != numCommands) {
284 return false;
285 }
286
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000287 curOffset += curSize;
288 if (curOffset < writer->size()) {
289 // Something else between the last command and the end of the stream
290 return false;
291 }
292
293 return true;
294}
295
296// temporarily here to make code review easier
297static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
298 SkPaintDictionary* paintDict,
299 const CommandInfo& saveLayerInfo,
300 const CommandInfo& dbmInfo);
301
302/*
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000303 * Restore has just been called (but not recorded), look back at the
robertphillips@google.come37ad352013-03-01 19:44:30 +0000304 * matching save* and see if we are in the configuration:
305 * SAVE_LAYER
306 * DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
307 * RESTORE
308 * where the saveLayer's color can be moved into the drawBitmap*'s paint
309 */
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000310static bool remove_save_layer1(SkWriter32* writer, int32_t offset,
robertphillips@google.come37ad352013-03-01 19:44:30 +0000311 SkPaintDictionary* paintDict) {
robertphillips@google.come37ad352013-03-01 19:44:30 +0000312 // back up to the save block
313 // TODO: add a stack to track save*/restore offsets rather than searching backwards
314 while (offset > 0) {
315 offset = *writer->peek32(offset);
316 }
317
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000318 int pattern[] = { SAVE_LAYER, kDRAW_BITMAP_FLAVOR, /* RESTORE */ };
319 CommandInfo result[SK_ARRAY_COUNT(pattern)];
320
321 if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
322 return false;
robertphillips@google.come37ad352013-03-01 19:44:30 +0000323 }
324
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000325 if (kSaveLayerWithBoundsSize == result[0].fSize) {
robertphillips@google.come37ad352013-03-01 19:44:30 +0000326 // The saveLayer's bound can offset where the dbm is drawn
327 return false;
328 }
329
robertphillips@google.come37ad352013-03-01 19:44:30 +0000330
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000331 return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
332 result[0], result[1]);
333}
robertphillips@google.come37ad352013-03-01 19:44:30 +0000334
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000335/*
336 * Convert the command code located at 'offset' to a NOOP. Leave the size
337 * field alone so the NOOP can be skipped later.
338 */
339static void convert_command_to_noop(SkWriter32* writer, uint32_t offset) {
340 uint32_t* ptr = writer->peek32(offset);
341 *ptr = (*ptr & MASK_24) | (NOOP << 24);
342}
robertphillips@google.come37ad352013-03-01 19:44:30 +0000343
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000344/*
345 * Attempt to merge the saveLayer's paint into the drawBitmap*'s paint.
346 * Return true on success; false otherwise.
347 */
348static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
349 SkPaintDictionary* paintDict,
350 const CommandInfo& saveLayerInfo,
351 const CommandInfo& dbmInfo) {
352 SkASSERT(SAVE_LAYER == saveLayerInfo.fActualOp);
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000353 SkASSERT(DRAW_BITMAP == dbmInfo.fActualOp ||
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000354 DRAW_BITMAP_MATRIX == dbmInfo.fActualOp ||
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000355 DRAW_BITMAP_NINE == dbmInfo.fActualOp ||
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000356 DRAW_BITMAP_RECT_TO_RECT == dbmInfo.fActualOp);
357
358 uint32_t dbmPaintOffset = getPaintOffset(dbmInfo.fActualOp, dbmInfo.fSize);
359 uint32_t slPaintOffset = getPaintOffset(SAVE_LAYER, saveLayerInfo.fSize);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000360
361 // we have a match, now we need to get the paints involved
robertphillips@google.com5ba0d902013-03-12 16:05:14 +0000362 uint32_t dbmPaintId = *writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
363 uint32_t saveLayerPaintId = *writer->peek32(saveLayerInfo.fOffset+slPaintOffset);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000364
365 if (0 == saveLayerPaintId) {
366 // In this case the saveLayer/restore isn't needed at all - just kill the saveLayer
367 // and signal the caller (by returning true) to not add the RESTORE op
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000368 convert_command_to_noop(writer, saveLayerInfo.fOffset);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000369 return true;
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000370 }
371
robertphillips@google.come37ad352013-03-01 19:44:30 +0000372 if (0 == dbmPaintId) {
373 // In this case just make the DBM* use the saveLayer's paint, kill the saveLayer
374 // and signal the caller (by returning true) to not add the RESTORE op
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000375 convert_command_to_noop(writer, saveLayerInfo.fOffset);
376 uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000377 SkASSERT(0 == *ptr);
378 *ptr = saveLayerPaintId;
379 return true;
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000380 }
robertphillips@google.come37ad352013-03-01 19:44:30 +0000381
382 SkAutoTDelete<SkPaint> saveLayerPaint(paintDict->unflatten(saveLayerPaintId));
383 if (NULL == saveLayerPaint.get() || !is_simple(*saveLayerPaint)) {
384 return false;
385 }
386
387 // For this optimization we only fold the saveLayer and drawBitmapRect
388 // together if the saveLayer's draw is simple (i.e., no fancy effects) and
389 // and the only difference in the colors is that the saveLayer's can have
390 // an alpha while the drawBitmapRect's is opaque.
391 // TODO: it should be possible to fold them together even if they both
392 // have different non-255 alphas
393 SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque
394
395 SkAutoTDelete<SkPaint> dbmPaint(paintDict->unflatten(dbmPaintId));
396 if (NULL == dbmPaint.get() || dbmPaint->getColor() != layerColor) {
397 return false;
398 }
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000399
robertphillips@google.come37ad352013-03-01 19:44:30 +0000400 SkColor newColor = SkColorSetA(dbmPaint->getColor(),
401 SkColorGetA(saveLayerPaint->getColor()));
402 dbmPaint->setColor(newColor);
403
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +0000404 const SkFlatData* data = paintDict->findAndReturnFlat(*dbmPaint);
405 if (NULL == data) {
406 return false;
407 }
robertphillips@google.come37ad352013-03-01 19:44:30 +0000408
409 // kill the saveLayer and alter the DBMR2R's paint to be the modified one
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000410 convert_command_to_noop(writer, saveLayerInfo.fOffset);
411 uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
412 SkASSERT(dbmPaintId == *ptr);
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +0000413 *ptr = data->index();
robertphillips@google.come37ad352013-03-01 19:44:30 +0000414 return true;
415}
416
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000417/*
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000418 * Restore has just been called (but not recorded), look back at the
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000419 * matching save* and see if we are in the configuration:
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000420 * SAVE_LAYER (with NULL == bounds)
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000421 * SAVE
422 * CLIP_RECT
423 * DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
424 * RESTORE
425 * RESTORE
426 * where the saveLayer's color can be moved into the drawBitmap*'s paint
427 */
428static bool remove_save_layer2(SkWriter32* writer, int32_t offset,
429 SkPaintDictionary* paintDict) {
430
431 // back up to the save block
432 // TODO: add a stack to track save*/restore offsets rather than searching backwards
433 while (offset > 0) {
434 offset = *writer->peek32(offset);
435 }
436
437 int pattern[] = { SAVE_LAYER, SAVE, CLIP_RECT, kDRAW_BITMAP_FLAVOR, RESTORE, /* RESTORE */ };
438 CommandInfo result[SK_ARRAY_COUNT(pattern)];
439
440 if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
441 return false;
442 }
443
444 if (kSaveLayerWithBoundsSize == result[0].fSize) {
445 // The saveLayer's bound can offset where the dbm is drawn
446 return false;
447 }
448
449 return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
450 result[0], result[3]);
451}
robertphillips@google.come37ad352013-03-01 19:44:30 +0000452
453/*
454 * Restore has just been called (but not recorded), so look back at the
reed@google.comffacd3c2012-08-30 15:31:23 +0000455 * matching save(), and see if we can eliminate the pair of them, due to no
456 * intervening matrix/clip calls.
457 *
458 * If so, update the writer and return true, in which case we won't even record
459 * the restore() call. If we still need the restore(), return false.
460 */
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000461static bool collapse_save_clip_restore(SkWriter32* writer, int32_t offset,
462 SkPaintDictionary* paintDict) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000463#ifdef TRACK_COLLAPSE_STATS
464 gCollapseCalls += 1;
465#endif
466
467 int32_t restoreOffset = (int32_t)writer->size();
468
469 // back up to the save block
470 while (offset > 0) {
471 offset = *writer->peek32(offset);
472 }
473
474 // now offset points to a save
475 offset = -offset;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000476 uint32_t opSize;
477 DrawType op = peek_op_and_size(writer, offset, &opSize);
478 if (SAVE_LAYER == op) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000479 // not ready to cull these out yet (mrr)
480 return false;
481 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000482 SkASSERT(SAVE == op);
djsollen@google.comd4236572013-08-13 14:29:06 +0000483 SkASSERT(kSaveSize == opSize);
484
485 // get the save flag (last 4-bytes of the space allocated for the opSize)
486 SkCanvas::SaveFlags saveFlags = (SkCanvas::SaveFlags) *writer->peek32(offset+4);
487 if (SkCanvas::kMatrixClip_SaveFlag != saveFlags) {
488 // This function's optimization is only correct for kMatrixClip style saves.
489 // TODO: set checkMatrix & checkClip booleans here and then check for the
490 // offending operations in the following loop.
491 return false;
492 }
reed@google.comffacd3c2012-08-30 15:31:23 +0000493
494 // Walk forward until we get back to either a draw-verb (abort) or we hit
495 // our restore (success).
496 int32_t saveOffset = offset;
497
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000498 offset += opSize;
reed@google.comffacd3c2012-08-30 15:31:23 +0000499 while (offset < restoreOffset) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000500 op = peek_op_and_size(writer, offset, &opSize);
501 if ((op > CONCAT && op < ROTATE) || (SAVE_LAYER == op)) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000502 // drawing verb, abort
503 return false;
504 }
505 offset += opSize;
506 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000507
reed@google.comffacd3c2012-08-30 15:31:23 +0000508#ifdef TRACK_COLLAPSE_STATS
509 gCollapseCount += 1;
510 SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls,
511 (double)gCollapseCount / gCollapseCalls, "%");
512#endif
513
514 writer->rewindToOffset(saveOffset);
515 return true;
516}
517
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000518typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
519 SkPaintDictionary* paintDict);
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000520enum PictureRecordOptType {
521 kRewind_OptType, // Optimization rewinds the command stream
522 kCollapseSaveLayer_OptType, // Optimization eliminates a save/restore pair
523};
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000524
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000525struct PictureRecordOpt {
526 PictureRecordOptProc fProc;
527 PictureRecordOptType fType;
528};
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000529/*
530 * A list of the optimizations that are tried upon seeing a restore
531 * TODO: add a real API for such optimizations
532 * Add the ability to fire optimizations on any op (not just RESTORE)
533 */
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000534static const PictureRecordOpt gPictureRecordOpts[] = {
535 { collapse_save_clip_restore, kRewind_OptType },
536 { remove_save_layer1, kCollapseSaveLayer_OptType },
537 { remove_save_layer2, kCollapseSaveLayer_OptType }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000538};
539
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000540// This is called after an optimization has been applied to the command stream
541// in order to adjust the contents and state of the bounding box hierarchy and
542// state tree to reflect the optimization.
543static void apply_optimization_to_bbh(PictureRecordOptType opt, SkPictureStateTree* stateTree,
544 SkBBoxHierarchy* boundingHierarchy) {
545 switch (opt) {
546 case kCollapseSaveLayer_OptType:
robertphillips@google.com35e15632013-03-15 16:49:34 +0000547 if (NULL != stateTree) {
548 stateTree->saveCollapsed();
549 }
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000550 break;
551 case kRewind_OptType:
552 if (NULL != boundingHierarchy) {
553 boundingHierarchy->rewindInserts();
554 }
555 // Note: No need to touch the state tree for this to work correctly.
556 // Unused branches do not burden the playback, and pruning the tree
557 // would be O(N^2), so it is best to leave it alone.
558 break;
559 default:
560 SkASSERT(0);
561 }
562}
563
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564void SkPictureRecord::restore() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000565 // FIXME: SkDeferredCanvas needs to be refactored to respect
566 // save/restore balancing so that the following test can be
567 // turned on permanently.
568#if 0
569 SkASSERT(fRestoreOffsetStack.count() > 1);
570#endif
571
reed@android.comb4e22d62009-07-09 15:20:25 +0000572 // check for underflow
573 if (fRestoreOffsetStack.count() == 0) {
574 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 }
reed@android.comb4e22d62009-07-09 15:20:25 +0000576
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000577 if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
578 fFirstSavedLayerIndex = kNoSavedLayerIndex;
579 }
580
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000581 uint32_t initialOffset, size;
robertphillips@google.com31d81912013-04-12 15:24:29 +0000582 size_t opt = 0;
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000583 if (!(fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag)) {
584 for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
585 if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
586 // Some optimization fired so don't add the RESTORE
587 size = 0;
588 initialOffset = fWriter.size();
589 apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
590 fStateTree, fBoundingHierarchy);
591 break;
592 }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000593 }
skia.committer@gmail.com4bb50b22013-04-13 07:01:15 +0000594 }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000595
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000596 if ((fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag) ||
597 SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000598 // No optimization fired so add the RESTORE
reed@google.comffacd3c2012-08-30 15:31:23 +0000599 fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000600 size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000601 initialOffset = this->addDraw(RESTORE, &size);
reed@google.comffacd3c2012-08-30 15:31:23 +0000602 }
603
reed@android.comb4e22d62009-07-09 15:20:25 +0000604 fRestoreOffsetStack.pop();
reed@android.com32a42492009-07-10 03:33:52 +0000605
robertphillips@google.com8b169312013-10-15 17:47:36 +0000606 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 return this->INHERITED::restore();
608}
609
610bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000611 // op + dx + dy
612 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000613 size_t initialOffset = this->addDraw(TRANSLATE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 addScalar(dx);
615 addScalar(dy);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000616 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 return this->INHERITED::translate(dx, dy);
618}
619
620bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000621 // op + sx + sy
622 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000623 size_t initialOffset = this->addDraw(SCALE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 addScalar(sx);
625 addScalar(sy);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000626 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 return this->INHERITED::scale(sx, sy);
628}
629
630bool SkPictureRecord::rotate(SkScalar degrees) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000631 // op + degrees
632 uint32_t size = 1 * kUInt32Size + sizeof(SkScalar);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000633 size_t initialOffset = this->addDraw(ROTATE, &size);
reed@google.com82065d62011-02-07 15:30:46 +0000634 addScalar(degrees);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000635 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 return this->INHERITED::rotate(degrees);
637}
638
639bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000640 // op + sx + sy
641 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000642 size_t initialOffset = this->addDraw(SKEW, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 addScalar(sx);
644 addScalar(sy);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000645 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 return this->INHERITED::skew(sx, sy);
647}
648
649bool SkPictureRecord::concat(const SkMatrix& matrix) {
robertphillips@google.com8b169312013-10-15 17:47:36 +0000650 this->validate(fWriter.size(), 0);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000651 // op + matrix index
652 uint32_t size = 2 * kUInt32Size;
robertphillips@google.com8b169312013-10-15 17:47:36 +0000653 size_t initialOffset = this->addDraw(CONCAT, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 addMatrix(matrix);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000655 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 return this->INHERITED::concat(matrix);
657}
658
reed@android.com6e073b92009-01-06 15:03:30 +0000659void SkPictureRecord::setMatrix(const SkMatrix& matrix) {
robertphillips@google.com8b169312013-10-15 17:47:36 +0000660 this->validate(fWriter.size(), 0);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000661 // op + matrix index
662 uint32_t size = 2 * kUInt32Size;
robertphillips@google.com8b169312013-10-15 17:47:36 +0000663 size_t initialOffset = this->addDraw(SET_MATRIX, &size);
reed@android.com6e073b92009-01-06 15:03:30 +0000664 addMatrix(matrix);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000665 this->validate(initialOffset, size);
reed@android.com6e073b92009-01-06 15:03:30 +0000666 this->INHERITED::setMatrix(matrix);
667}
668
reed@google.com45482d12011-08-29 19:02:39 +0000669static bool regionOpExpands(SkRegion::Op op) {
670 switch (op) {
671 case SkRegion::kUnion_Op:
672 case SkRegion::kXOR_Op:
673 case SkRegion::kReverseDifference_Op:
674 case SkRegion::kReplace_Op:
675 return true;
676 case SkRegion::kIntersect_Op:
677 case SkRegion::kDifference_Op:
678 return false;
679 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000680 SkDEBUGFAIL("unknown region op");
reed@google.com45482d12011-08-29 19:02:39 +0000681 return false;
682 }
683}
684
robertphillips@google.come37ad352013-03-01 19:44:30 +0000685void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000686 int32_t offset = fRestoreOffsetStack.top();
687 while (offset > 0) {
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000688 uint32_t* peek = fWriter.peek32(offset);
689 offset = *peek;
690 *peek = restoreOffset;
691 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000692
reed@google.comffacd3c2012-08-30 15:31:23 +0000693#ifdef SK_DEBUG
694 // assert that the final offset value points to a save verb
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000695 uint32_t opSize;
696 DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize);
reed@google.comffacd3c2012-08-30 15:31:23 +0000697 SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
698#endif
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000699}
700
reed@google.comd86e7ab2012-09-27 20:31:31 +0000701void SkPictureRecord::beginRecording() {
702 // we have to call this *after* our constructor, to ensure that it gets
703 // recorded. This is balanced by restoreToCount() call from endRecording,
704 // which in-turn calls our overridden restore(), so those get recorded too.
705 fInitialSaveCount = this->save(kMatrixClip_SaveFlag);
706}
707
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000708void SkPictureRecord::endRecording() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000709 SkASSERT(kNoInitialSave != fInitialSaveCount);
710 this->restoreToCount(fInitialSaveCount);
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000711}
712
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000713void SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
reed@google.com21b519d2012-10-02 17:42:15 +0000714 if (fRestoreOffsetStack.isEmpty()) {
715 return;
716 }
717
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000718 // The RestoreOffset field is initially filled with a placeholder
719 // value that points to the offset of the previous RestoreOffset
720 // in the current stack level, thus forming a linked list so that
721 // the restore offsets can be filled in when the corresponding
722 // restore command is recorded.
723 int32_t prevOffset = fRestoreOffsetStack.top();
724
reed@google.com45482d12011-08-29 19:02:39 +0000725 if (regionOpExpands(op)) {
726 // Run back through any previous clip ops, and mark their offset to
727 // be 0, disabling their ability to trigger a jump-to-restore, otherwise
728 // they could hide this clips ability to expand the clip (i.e. go from
729 // empty to non-empty).
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000730 fillRestoreOffsetPlaceholdersForCurrentStackLevel(0);
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000731
732 // Reset the pointer back to the previous clip so that subsequent
733 // restores don't overwrite the offsets we just cleared.
734 prevOffset = 0;
reed@google.com45482d12011-08-29 19:02:39 +0000735 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000736
reed@google.com45482d12011-08-29 19:02:39 +0000737 size_t offset = fWriter.size();
fmalita@google.comd0f1a4f2013-08-27 15:50:19 +0000738 addInt(prevOffset);
reed@google.com45482d12011-08-29 19:02:39 +0000739 fRestoreOffsetStack.top() = offset;
740}
741
reed@google.com071eef92011-10-12 11:52:53 +0000742bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.comf9291502013-02-15 15:13:27 +0000743 // id + rect + clip params
744 uint32_t size = 1 * kUInt32Size + sizeof(rect) + 1 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000745 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000746 if (!fRestoreOffsetStack.isEmpty()) {
747 // + restore offset
748 size += kUInt32Size;
749 }
robertphillips@google.com8b169312013-10-15 17:47:36 +0000750 size_t initialOffset = this->addDraw(CLIP_RECT, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751 addRect(rect);
reed@google.com83ab4952011-11-11 21:34:54 +0000752 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000753 recordRestoreOffsetPlaceholder(op);
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000754
robertphillips@google.com8b169312013-10-15 17:47:36 +0000755 this->validate(initialOffset, size);
reed@google.com071eef92011-10-12 11:52:53 +0000756 return this->INHERITED::clipRect(rect, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757}
758
reed@google.com4ed0fb72012-12-12 20:48:18 +0000759bool SkPictureRecord::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
760 if (rrect.isRect()) {
761 return this->SkPictureRecord::clipRect(rrect.getBounds(), op, doAA);
762 }
763
robertphillips@google.comf9291502013-02-15 15:13:27 +0000764 // op + rrect + clip params
765 uint32_t size = 1 * kUInt32Size + SkRRect::kSizeInMemory + 1 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000766 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000767 if (!fRestoreOffsetStack.isEmpty()) {
768 // + restore offset
769 size += kUInt32Size;
770 }
robertphillips@google.com8b169312013-10-15 17:47:36 +0000771 size_t initialOffset = this->addDraw(CLIP_RRECT, &size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000772 addRRect(rrect);
773 addInt(ClipParams_pack(op, doAA));
774 recordRestoreOffsetPlaceholder(op);
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000775
robertphillips@google.com8b169312013-10-15 17:47:36 +0000776 this->validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000777
778 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +0000779 return this->updateClipConservativelyUsingBounds(rrect.getBounds(), op, false);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000780 } else {
781 return this->INHERITED::clipRRect(rrect, op, doAA);
782 }
783}
784
reed@google.com071eef92011-10-12 11:52:53 +0000785bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000786
787 SkRect r;
reed@google.com907ef6c2012-12-10 15:50:37 +0000788 if (!path.isInverseFillType() && path.isRect(&r)) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000789 return this->clipRect(r, op, doAA);
790 }
791
robertphillips@google.comf9291502013-02-15 15:13:27 +0000792 // op + path index + clip params
793 uint32_t size = 3 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000794 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000795 if (!fRestoreOffsetStack.isEmpty()) {
796 // + restore offset
797 size += kUInt32Size;
798 }
robertphillips@google.com8b169312013-10-15 17:47:36 +0000799 size_t initialOffset = this->addDraw(CLIP_PATH, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 addPath(path);
reed@google.com83ab4952011-11-11 21:34:54 +0000801 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000802 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000803
robertphillips@google.com8b169312013-10-15 17:47:36 +0000804 this->validate(initialOffset, size);
reed@google.com82065d62011-02-07 15:30:46 +0000805
reed@android.comae814c82009-02-13 14:56:09 +0000806 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +0000807 return this->updateClipConservativelyUsingBounds(path.getBounds(), op,
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000808 path.isInverseFillType());
reed@android.comae814c82009-02-13 14:56:09 +0000809 } else {
reed@google.com071eef92011-10-12 11:52:53 +0000810 return this->INHERITED::clipPath(path, op, doAA);
reed@android.comae814c82009-02-13 14:56:09 +0000811 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812}
813
814bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
robertphillips@google.comf9291502013-02-15 15:13:27 +0000815 // op + region index + clip params
816 uint32_t size = 3 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000817 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000818 if (!fRestoreOffsetStack.isEmpty()) {
819 // + restore offset
820 size += kUInt32Size;
821 }
robertphillips@google.com8b169312013-10-15 17:47:36 +0000822 size_t initialOffset = this->addDraw(CLIP_REGION, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 addRegion(region);
reed@google.com83ab4952011-11-11 21:34:54 +0000824 addInt(ClipParams_pack(op, false));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000825 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000826
robertphillips@google.com8b169312013-10-15 17:47:36 +0000827 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 return this->INHERITED::clipRegion(region, op);
829}
830
reed@google.com2a981812011-04-14 18:59:28 +0000831void SkPictureRecord::clear(SkColor color) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000832 // op + color
833 uint32_t size = 2 * kUInt32Size;
robertphillips@google.com8b169312013-10-15 17:47:36 +0000834 size_t initialOffset = this->addDraw(DRAW_CLEAR, &size);
reed@google.com2a981812011-04-14 18:59:28 +0000835 addInt(color);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000836 this->validate(initialOffset, size);
reed@google.com2a981812011-04-14 18:59:28 +0000837}
838
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839void SkPictureRecord::drawPaint(const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000840 // op + paint index
841 uint32_t size = 2 * kUInt32Size;
robertphillips@google.com8b169312013-10-15 17:47:36 +0000842 size_t initialOffset = this->addDraw(DRAW_PAINT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000843 SkASSERT(initialOffset+getPaintOffset(DRAW_PAINT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 addPaint(paint);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000845 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846}
847
848void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000849 const SkPaint& paint) {
850 // op + paint index + mode + count + point data
851 uint32_t size = 4 * kUInt32Size + count * sizeof(SkPoint);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000852 size_t initialOffset = this->addDraw(DRAW_POINTS, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000853 SkASSERT(initialOffset+getPaintOffset(DRAW_POINTS, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 addPaint(paint);
855 addInt(mode);
856 addInt(count);
857 fWriter.writeMul4(pts, count * sizeof(SkPoint));
robertphillips@google.com8b169312013-10-15 17:47:36 +0000858 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859}
860
reed@google.com4ed0fb72012-12-12 20:48:18 +0000861void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000862 // op + paint index + rect
863 uint32_t size = 2 * kUInt32Size + sizeof(oval);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000864 size_t initialOffset = this->addDraw(DRAW_OVAL, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000865 SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000866 addPaint(paint);
867 addRect(oval);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000868 this->validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000869}
870
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000872 // op + paint index + rect
873 uint32_t size = 2 * kUInt32Size + sizeof(rect);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000874 size_t initialOffset = this->addDraw(DRAW_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000875 SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 addPaint(paint);
877 addRect(rect);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000878 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879}
880
reed@google.com4ed0fb72012-12-12 20:48:18 +0000881void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
882 if (rrect.isRect()) {
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000883 this->SkPictureRecord::drawRect(rrect.getBounds(), paint);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000884 } else if (rrect.isOval()) {
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000885 this->SkPictureRecord::drawOval(rrect.getBounds(), paint);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000886 } else {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000887 // op + paint index + rrect
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000888 uint32_t initialOffset, size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000889 size = 2 * kUInt32Size + SkRRect::kSizeInMemory;
890 initialOffset = this->addDraw(DRAW_RRECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000891 SkASSERT(initialOffset+getPaintOffset(DRAW_RRECT, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000892 addPaint(paint);
893 addRRect(rrect);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000894 this->validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000895 }
reed@google.com4ed0fb72012-12-12 20:48:18 +0000896}
897
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000899 // op + paint index + path index
900 uint32_t size = 3 * kUInt32Size;
robertphillips@google.com8b169312013-10-15 17:47:36 +0000901 size_t initialOffset = this->addDraw(DRAW_PATH, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000902 SkASSERT(initialOffset+getPaintOffset(DRAW_PATH, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903 addPaint(paint);
904 addPath(path);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000905 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906}
907
908void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
909 const SkPaint* paint = NULL) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000910 // op + paint index + bitmap index + left + top
911 uint32_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000912 size_t initialOffset = this->addDraw(DRAW_BITMAP, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000913 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000914 addPaintPtr(paint);
915 addBitmap(bitmap);
916 addScalar(left);
917 addScalar(top);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000918 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919}
920
reed@google.com71121732012-09-18 15:14:33 +0000921void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +0000922 const SkRect& dst, const SkPaint* paint,
923 DrawBitmapRectFlags flags) {
924 // id + paint index + bitmap index + bool for 'src' + flags
925 uint32_t size = 5 * kUInt32Size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000926 if (NULL != src) {
927 size += sizeof(*src); // + rect
928 }
929 size += sizeof(dst); // + rect
930
robertphillips@google.com8b169312013-10-15 17:47:36 +0000931 size_t initialOffset = this->addDraw(DRAW_BITMAP_RECT_TO_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000932 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_RECT_TO_RECT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 addPaintPtr(paint);
934 addBitmap(bitmap);
reed@google.com71121732012-09-18 15:14:33 +0000935 addRectPtr(src); // may be null
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 addRect(dst);
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +0000937 addInt(flags);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000938 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939}
940
941void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
reed@google.comf0b5e112011-09-07 11:57:34 +0000942 const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000943 // id + paint index + bitmap index + matrix index
944 uint32_t size = 4 * kUInt32Size;
robertphillips@google.com8b169312013-10-15 17:47:36 +0000945 size_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000946 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_MATRIX, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 addPaintPtr(paint);
948 addBitmap(bitmap);
949 addMatrix(matrix);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000950 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951}
952
reed@google.comf0b5e112011-09-07 11:57:34 +0000953void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
954 const SkRect& dst, const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000955 // op + paint index + bitmap id + center + dst rect
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000956 uint32_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000957 size_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000958 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_NINE, size) == fWriter.size());
reed@google.comf0b5e112011-09-07 11:57:34 +0000959 addPaintPtr(paint);
960 addBitmap(bitmap);
961 addIRect(center);
962 addRect(dst);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000963 this->validate(initialOffset, size);
reed@google.comf0b5e112011-09-07 11:57:34 +0000964}
965
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
967 const SkPaint* paint = NULL) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000968 // op + paint index + bitmap index + left + top
969 uint32_t size = 5 * kUInt32Size;
robertphillips@google.com8b169312013-10-15 17:47:36 +0000970 size_t initialOffset = this->addDraw(DRAW_SPRITE, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000971 SkASSERT(initialOffset+getPaintOffset(DRAW_SPRITE, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972 addPaintPtr(paint);
973 addBitmap(bitmap);
974 addInt(left);
975 addInt(top);
robertphillips@google.com8b169312013-10-15 17:47:36 +0000976 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977}
978
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +0000979void SkPictureRecord::ComputeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 SkPaint::FontMetrics metrics;
981 paint.getFontMetrics(&metrics);
982 SkRect bounds;
983 // construct a rect so we can see any adjustments from the paint.
984 // we use 0,1 for left,right, just so the rect isn't empty
reed@google.com45954262012-12-07 17:14:40 +0000985 bounds.set(0, metrics.fTop, SK_Scalar1, metrics.fBottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 (void)paint.computeFastBounds(bounds, &bounds);
reed@google.com45954262012-12-07 17:14:40 +0000987 topbot[0] = bounds.fTop;
988 topbot[1] = bounds.fBottom;
989}
990
junov@chromium.orgf3b12232013-01-22 17:50:47 +0000991void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData& flat,
reed@google.com45954262012-12-07 17:14:40 +0000992 SkScalar minY, SkScalar maxY) {
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +0000993 WriteTopBot(paint, flat);
junov@chromium.org3f5ecd62013-01-22 18:01:26 +0000994 addScalar(flat.topBot()[0] + minY);
995 addScalar(flat.topBot()[1] + maxY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996}
997
reed@google.com82065d62011-02-07 15:30:46 +0000998void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 SkScalar y, const SkPaint& paint) {
reed@google.com2eb5bb12012-04-12 14:27:42 +00001000 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com82065d62011-02-07 15:30:46 +00001001
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001002 // op + paint index + length + 'length' worth of chars + x + y
1003 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * sizeof(SkScalar);
1004 if (fast) {
1005 size += 2 * sizeof(SkScalar); // + top & bottom
1006 }
1007
robertphillips@google.come37ad352013-03-01 19:44:30 +00001008 DrawType op = fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT;
robertphillips@google.com8b169312013-10-15 17:47:36 +00001009 size_t initialOffset = this->addDraw(op, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001010 SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001011 const SkFlatData* flatPaintData = addPaint(paint);
1012 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 addText(text, byteLength);
1014 addScalar(x);
1015 addScalar(y);
1016 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001017 addFontMetricsTopBottom(paint, *flatPaintData, y, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 }
robertphillips@google.com8b169312013-10-15 17:47:36 +00001019 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020}
1021
reed@google.com82065d62011-02-07 15:30:46 +00001022void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023 const SkPoint pos[], const SkPaint& paint) {
1024 size_t points = paint.countText(text, byteLength);
1025 if (0 == points)
1026 return;
1027
1028 bool canUseDrawH = true;
reed@google.com9efd9a02012-01-30 15:41:43 +00001029 SkScalar minY = pos[0].fY;
1030 SkScalar maxY = pos[0].fY;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031 // check if the caller really should have used drawPosTextH()
1032 {
1033 const SkScalar firstY = pos[0].fY;
1034 for (size_t index = 1; index < points; index++) {
1035 if (pos[index].fY != firstY) {
1036 canUseDrawH = false;
reed@google.com9efd9a02012-01-30 15:41:43 +00001037 if (pos[index].fY < minY) {
1038 minY = pos[index].fY;
1039 } else if (pos[index].fY > maxY) {
1040 maxY = pos[index].fY;
1041 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001042 }
1043 }
1044 }
reed@google.com82065d62011-02-07 15:30:46 +00001045
reed@google.com2eb5bb12012-04-12 14:27:42 +00001046 bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com9efd9a02012-01-30 15:41:43 +00001047 bool fast = canUseDrawH && fastBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001049 // op + paint index + length + 'length' worth of data + num points
1050 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1051 if (canUseDrawH) {
1052 if (fast) {
1053 size += 2 * sizeof(SkScalar); // + top & bottom
1054 }
1055 // + y-pos + actual x-point data
1056 size += sizeof(SkScalar) + points * sizeof(SkScalar);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 } else {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001058 // + x&y point data
1059 size += points * sizeof(SkPoint);
1060 if (fastBounds) {
1061 size += 2 * sizeof(SkScalar); // + top & bottom
1062 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001064
1065 DrawType op;
1066 if (fast) {
1067 op = DRAW_POS_TEXT_H_TOP_BOTTOM;
1068 } else if (canUseDrawH) {
1069 op = DRAW_POS_TEXT_H;
1070 } else if (fastBounds) {
1071 op = DRAW_POS_TEXT_TOP_BOTTOM;
1072 } else {
1073 op = DRAW_POS_TEXT;
1074 }
robertphillips@google.com8b169312013-10-15 17:47:36 +00001075 size_t initialOffset = this->addDraw(op, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001076 SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001077 const SkFlatData* flatPaintData = addPaint(paint);
1078 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 addText(text, byteLength);
1080 addInt(points);
1081
1082#ifdef SK_DEBUG_SIZE
1083 size_t start = fWriter.size();
1084#endif
1085 if (canUseDrawH) {
1086 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001087 addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 }
1089 addScalar(pos[0].fY);
1090 SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
reed@google.com82065d62011-02-07 15:30:46 +00001091 for (size_t index = 0; index < points; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 *xptr++ = pos[index].fX;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001093 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 fWriter.writeMul4(pos, points * sizeof(SkPoint));
reed@google.com9efd9a02012-01-30 15:41:43 +00001095 if (fastBounds) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001096 addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY);
reed@google.com9efd9a02012-01-30 15:41:43 +00001097 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 }
1099#ifdef SK_DEBUG_SIZE
1100 fPointBytes += fWriter.size() - start;
1101 fPointWrites += points;
1102#endif
robertphillips@google.com8b169312013-10-15 17:47:36 +00001103 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104}
1105
1106void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
1107 const SkScalar xpos[], SkScalar constY,
1108 const SkPaint& paint) {
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +00001109
1110 const SkFlatData* flatPaintData = this->getFlatPaintData(paint);
1111 drawPosTextHImpl(text, byteLength, xpos, constY, paint, flatPaintData);
1112}
1113
1114void SkPictureRecord::drawPosTextHImpl(const void* text, size_t byteLength,
1115 const SkScalar xpos[], SkScalar constY,
1116 const SkPaint& paint, const SkFlatData* flatPaintData) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 size_t points = paint.countText(text, byteLength);
1118 if (0 == points)
1119 return;
reed@google.com82065d62011-02-07 15:30:46 +00001120
reed@google.com2eb5bb12012-04-12 14:27:42 +00001121 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001123 // op + paint index + length + 'length' worth of data + num points
1124 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1125 if (fast) {
1126 size += 2 * sizeof(SkScalar); // + top & bottom
1127 }
1128 // + y + the actual points
1129 size += 1 * kUInt32Size + points * sizeof(SkScalar);
robertphillips@google.com8b169312013-10-15 17:47:36 +00001130 size_t initialOffset = this->addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H,
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001131 &size);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001132 SkASSERT(flatPaintData);
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +00001133 addFlatPaint(flatPaintData);
1134
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135 addText(text, byteLength);
1136 addInt(points);
reed@google.com82065d62011-02-07 15:30:46 +00001137
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138#ifdef SK_DEBUG_SIZE
1139 size_t start = fWriter.size();
1140#endif
1141 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001142 addFontMetricsTopBottom(paint, *flatPaintData, constY, constY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 }
1144 addScalar(constY);
1145 fWriter.writeMul4(xpos, points * sizeof(SkScalar));
1146#ifdef SK_DEBUG_SIZE
1147 fPointBytes += fWriter.size() - start;
1148 fPointWrites += points;
1149#endif
robertphillips@google.com8b169312013-10-15 17:47:36 +00001150 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151}
1152
reed@google.com82065d62011-02-07 15:30:46 +00001153void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength,
1154 const SkPath& path, const SkMatrix* matrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001156 // op + paint index + length + 'length' worth of data + path index + matrix index
1157 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * kUInt32Size;
robertphillips@google.com8b169312013-10-15 17:47:36 +00001158 size_t initialOffset = this->addDraw(DRAW_TEXT_ON_PATH, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001159 SkASSERT(initialOffset+getPaintOffset(DRAW_TEXT_ON_PATH, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160 addPaint(paint);
1161 addText(text, byteLength);
1162 addPath(path);
1163 addMatrixPtr(matrix);
robertphillips@google.com8b169312013-10-15 17:47:36 +00001164 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165}
1166
1167void SkPictureRecord::drawPicture(SkPicture& picture) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001168 // op + picture index
1169 uint32_t size = 2 * kUInt32Size;
robertphillips@google.com8b169312013-10-15 17:47:36 +00001170 size_t initialOffset = this->addDraw(DRAW_PICTURE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171 addPicture(picture);
robertphillips@google.com8b169312013-10-15 17:47:36 +00001172 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173}
1174
1175void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
1176 const SkPoint vertices[], const SkPoint texs[],
1177 const SkColor colors[], SkXfermode*,
1178 const uint16_t indices[], int indexCount,
1179 const SkPaint& paint) {
1180 uint32_t flags = 0;
1181 if (texs) {
1182 flags |= DRAW_VERTICES_HAS_TEXS;
1183 }
1184 if (colors) {
1185 flags |= DRAW_VERTICES_HAS_COLORS;
1186 }
1187 if (indexCount > 0) {
1188 flags |= DRAW_VERTICES_HAS_INDICES;
1189 }
1190
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001191 // op + paint index + flags + vmode + vCount + vertices
1192 uint32_t size = 5 * kUInt32Size + vertexCount * sizeof(SkPoint);
1193 if (flags & DRAW_VERTICES_HAS_TEXS) {
1194 size += vertexCount * sizeof(SkPoint); // + uvs
1195 }
1196 if (flags & DRAW_VERTICES_HAS_COLORS) {
1197 size += vertexCount * sizeof(SkColor); // + vert colors
1198 }
1199 if (flags & DRAW_VERTICES_HAS_INDICES) {
1200 // + num indices + indices
1201 size += 1 * kUInt32Size + SkAlign4(indexCount * sizeof(uint16_t));
1202 }
1203
robertphillips@google.com8b169312013-10-15 17:47:36 +00001204 size_t initialOffset = this->addDraw(DRAW_VERTICES, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001205 SkASSERT(initialOffset+getPaintOffset(DRAW_VERTICES, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 addPaint(paint);
1207 addInt(flags);
1208 addInt(vmode);
1209 addInt(vertexCount);
1210 addPoints(vertices, vertexCount);
1211 if (flags & DRAW_VERTICES_HAS_TEXS) {
1212 addPoints(texs, vertexCount);
1213 }
1214 if (flags & DRAW_VERTICES_HAS_COLORS) {
1215 fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
1216 }
1217 if (flags & DRAW_VERTICES_HAS_INDICES) {
1218 addInt(indexCount);
1219 fWriter.writePad(indices, indexCount * sizeof(uint16_t));
1220 }
robertphillips@google.com8b169312013-10-15 17:47:36 +00001221 this->validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222}
1223
reed@android.comcb608442009-12-04 21:32:27 +00001224void SkPictureRecord::drawData(const void* data, size_t length) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001225 // op + length + 'length' worth of data
1226 uint32_t size = 2 * kUInt32Size + SkAlign4(length);
robertphillips@google.com8b169312013-10-15 17:47:36 +00001227 size_t initialOffset = this->addDraw(DRAW_DATA, &size);
reed@android.comcb608442009-12-04 21:32:27 +00001228 addInt(length);
1229 fWriter.writePad(data, length);
robertphillips@google.com8b169312013-10-15 17:47:36 +00001230 this->validate(initialOffset, size);
reed@android.comcb608442009-12-04 21:32:27 +00001231}
1232
robertphillips@google.com0a4805e2013-05-29 13:24:23 +00001233void SkPictureRecord::beginCommentGroup(const char* description) {
1234 // op/size + length of string + \0 terminated chars
1235 int length = strlen(description);
1236 uint32_t size = 2 * kUInt32Size + SkAlign4(length + 1);
robertphillips@google.com8b169312013-10-15 17:47:36 +00001237 size_t initialOffset = this->addDraw(BEGIN_COMMENT_GROUP, &size);
robertphillips@google.com0a4805e2013-05-29 13:24:23 +00001238 fWriter.writeString(description, length);
robertphillips@google.com8b169312013-10-15 17:47:36 +00001239 this->validate(initialOffset, size);
robertphillips@google.com0a4805e2013-05-29 13:24:23 +00001240}
1241
1242void SkPictureRecord::addComment(const char* kywd, const char* value) {
1243 // op/size + 2x length of string + 2x \0 terminated chars
1244 int kywdLen = strlen(kywd);
1245 int valueLen = strlen(value);
1246 uint32_t size = 3 * kUInt32Size + SkAlign4(kywdLen + 1) + SkAlign4(valueLen + 1);
robertphillips@google.com8b169312013-10-15 17:47:36 +00001247 size_t initialOffset = this->addDraw(COMMENT, &size);
robertphillips@google.com0a4805e2013-05-29 13:24:23 +00001248 fWriter.writeString(kywd, kywdLen);
1249 fWriter.writeString(value, valueLen);
robertphillips@google.com8b169312013-10-15 17:47:36 +00001250 this->validate(initialOffset, size);
robertphillips@google.com0a4805e2013-05-29 13:24:23 +00001251}
1252
1253void SkPictureRecord::endCommentGroup() {
1254 // op/size
1255 uint32_t size = 1 * kUInt32Size;
robertphillips@google.com8b169312013-10-15 17:47:36 +00001256 size_t initialOffset = this->addDraw(END_COMMENT_GROUP, &size);
1257 this->validate(initialOffset, size);
robertphillips@google.com0a4805e2013-05-29 13:24:23 +00001258}
1259
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260///////////////////////////////////////////////////////////////////////////////
reed@google.com82065d62011-02-07 15:30:46 +00001261
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
scroggo@google.com4b90b112012-12-04 15:08:56 +00001263 const int index = fBitmapHeap->insert(bitmap);
1264 // In debug builds, a bad return value from insert() will crash, allowing for debugging. In
1265 // release builds, the invalid value will be recorded so that the reader will know that there
1266 // was a problem.
1267 SkASSERT(index != SkBitmapHeap::INVALID_SLOT);
1268 addInt(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269}
1270
1271void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
1272 addMatrixPtr(&matrix);
1273}
1274
1275void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
reed@google.com83ca3372012-07-12 15:27:54 +00001276 this->addInt(matrix ? fMatrices.find(*matrix) : 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277}
1278
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +00001279const SkFlatData* SkPictureRecord::getFlatPaintData(const SkPaint& paint) {
1280 return fPaints.findAndReturnFlat(paint);
1281}
1282
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001283const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +00001284 const SkFlatData* data = paint ? getFlatPaintData(*paint) : NULL;
1285 this->addFlatPaint(data);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001286 return data;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287}
1288
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +00001289void SkPictureRecord::addFlatPaint(const SkFlatData* flatPaint) {
1290 int index = flatPaint ? flatPaint->index() : 0;
1291 this->addInt(index);
1292}
1293
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294void SkPictureRecord::addPath(const SkPath& path) {
1295 if (NULL == fPathHeap) {
1296 fPathHeap = SkNEW(SkPathHeap);
1297 }
1298 addInt(fPathHeap->append(path));
1299}
1300
1301void SkPictureRecord::addPicture(SkPicture& picture) {
1302 int index = fPictureRefs.find(&picture);
1303 if (index < 0) { // not found
1304 index = fPictureRefs.count();
1305 *fPictureRefs.append() = &picture;
1306 picture.ref();
1307 }
1308 // follow the convention of recording a 1-based index
1309 addInt(index + 1);
1310}
1311
1312void SkPictureRecord::addPoint(const SkPoint& point) {
1313#ifdef SK_DEBUG_SIZE
1314 size_t start = fWriter.size();
1315#endif
1316 fWriter.writePoint(point);
1317#ifdef SK_DEBUG_SIZE
1318 fPointBytes += fWriter.size() - start;
1319 fPointWrites++;
1320#endif
1321}
reed@google.com82065d62011-02-07 15:30:46 +00001322
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
1324 fWriter.writeMul4(pts, count * sizeof(SkPoint));
1325#ifdef SK_DEBUG_SIZE
1326 fPointBytes += count * sizeof(SkPoint);
1327 fPointWrites++;
1328#endif
1329}
1330
1331void SkPictureRecord::addRect(const SkRect& rect) {
1332#ifdef SK_DEBUG_SIZE
1333 size_t start = fWriter.size();
1334#endif
1335 fWriter.writeRect(rect);
1336#ifdef SK_DEBUG_SIZE
1337 fRectBytes += fWriter.size() - start;
1338 fRectWrites++;
1339#endif
1340}
1341
1342void SkPictureRecord::addRectPtr(const SkRect* rect) {
1343 if (fWriter.writeBool(rect != NULL)) {
1344 fWriter.writeRect(*rect);
1345 }
1346}
1347
reed@google.comf0b5e112011-09-07 11:57:34 +00001348void SkPictureRecord::addIRect(const SkIRect& rect) {
1349 fWriter.write(&rect, sizeof(rect));
1350}
1351
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
1353 if (fWriter.writeBool(rect != NULL)) {
1354 *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
1355 }
1356}
1357
reed@google.com4ed0fb72012-12-12 20:48:18 +00001358void SkPictureRecord::addRRect(const SkRRect& rrect) {
1359 fWriter.writeRRect(rrect);
1360}
1361
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362void SkPictureRecord::addRegion(const SkRegion& region) {
reed@google.com83ca3372012-07-12 15:27:54 +00001363 addInt(fRegions.find(region));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364}
1365
1366void SkPictureRecord::addText(const void* text, size_t byteLength) {
1367#ifdef SK_DEBUG_SIZE
1368 size_t start = fWriter.size();
1369#endif
1370 addInt(byteLength);
1371 fWriter.writePad(text, byteLength);
1372#ifdef SK_DEBUG_SIZE
1373 fTextBytes += fWriter.size() - start;
1374 fTextWrites++;
1375#endif
1376}
1377
1378///////////////////////////////////////////////////////////////////////////////
1379
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380#ifdef SK_DEBUG_SIZE
1381size_t SkPictureRecord::size() const {
1382 size_t result = 0;
1383 size_t sizeData;
1384 bitmaps(&sizeData);
1385 result += sizeData;
1386 matrices(&sizeData);
1387 result += sizeData;
1388 paints(&sizeData);
1389 result += sizeData;
1390 paths(&sizeData);
1391 result += sizeData;
1392 pictures(&sizeData);
1393 result += sizeData;
1394 regions(&sizeData);
1395 result += sizeData;
1396 result += streamlen();
1397 return result;
1398}
1399
1400int SkPictureRecord::bitmaps(size_t* size) const {
1401 size_t result = 0;
1402 int count = fBitmaps.count();
reed@google.com82065d62011-02-07 15:30:46 +00001403 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
1405 *size = result;
1406 return count;
1407}
1408
1409int SkPictureRecord::matrices(size_t* size) const {
1410 int count = fMatrices.count();
1411 *size = sizeof(fMatrices[0]) * count;
1412 return count;
1413}
1414
1415int SkPictureRecord::paints(size_t* size) const {
1416 size_t result = 0;
1417 int count = fPaints.count();
reed@google.com82065d62011-02-07 15:30:46 +00001418 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419 result += sizeof(fPaints[index]) + fPaints[index]->size();
1420 *size = result;
1421 return count;
1422}
1423
1424int SkPictureRecord::paths(size_t* size) const {
1425 size_t result = 0;
1426 int count = fPaths.count();
reed@google.com82065d62011-02-07 15:30:46 +00001427 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 result += sizeof(fPaths[index]) + fPaths[index]->size();
1429 *size = result;
1430 return count;
1431}
1432
1433int SkPictureRecord::regions(size_t* size) const {
1434 size_t result = 0;
1435 int count = fRegions.count();
reed@google.com82065d62011-02-07 15:30:46 +00001436 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437 result += sizeof(fRegions[index]) + fRegions[index]->size();
1438 *size = result;
1439 return count;
1440}
1441
1442size_t SkPictureRecord::streamlen() const {
1443 return fWriter.size();
1444}
1445#endif
1446
1447#ifdef SK_DEBUG_VALIDATE
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001448void SkPictureRecord::validate(uint32_t initialOffset, uint32_t size) const {
1449 SkASSERT(fWriter.size() == initialOffset + size);
1450
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 validateBitmaps();
1452 validateMatrices();
1453 validatePaints();
1454 validatePaths();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 validateRegions();
1456}
1457
1458void SkPictureRecord::validateBitmaps() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001459 int count = fBitmapHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460 SkASSERT((unsigned) count < 0x1000);
1461 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001462 const SkBitmap* bitPtr = fBitmapHeap->getBitmap(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463 SkASSERT(bitPtr);
1464 bitPtr->validate();
1465 }
1466}
1467
1468void SkPictureRecord::validateMatrices() const {
1469 int count = fMatrices.count();
1470 SkASSERT((unsigned) count < 0x1000);
1471 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001472 const SkFlatData* matrix = fMatrices[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473 SkASSERT(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001474// matrix->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475 }
1476}
1477
1478void SkPictureRecord::validatePaints() const {
1479 int count = fPaints.count();
1480 SkASSERT((unsigned) count < 0x1000);
1481 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001482 const SkFlatData* paint = fPaints[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483 SkASSERT(paint);
1484// paint->validate();
1485 }
1486}
1487
1488void SkPictureRecord::validatePaths() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001489 if (NULL == fPathHeap) {
1490 return;
1491 }
1492
1493 int count = fPathHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 SkASSERT((unsigned) count < 0x1000);
1495 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001496 const SkPath& path = (*fPathHeap)[index];
1497 path.validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498 }
1499}
1500
1501void SkPictureRecord::validateRegions() const {
1502 int count = fRegions.count();
1503 SkASSERT((unsigned) count < 0x1000);
1504 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001505 const SkFlatData* region = fRegions[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506 SkASSERT(region);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001507// region->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508 }
1509}
1510#endif