blob: 0ec9eaf74d0c812b01bea81893d39ff61fb94d80 [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.com9b051a32013-08-20 20:06:40 +000031SkPictureRecord::SkPictureRecord(uint32_t flags, SkDevice* 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.com9b051a32013-08-20 20:06:40 +0000141SkDevice* SkPictureRecord::setDevice(SkDevice* device) {
reed@google.comd86e7ab2012-09-27 20:31:31 +0000142 SkASSERT(!"eeek, don't try to change the device on a recording canvas");
143 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.com2ca1aaa2013-02-15 13:47:37 +0000153 uint32_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.com2ca1aaa2013-02-15 13:47:37 +0000156 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.com2ca1aaa2013-02-15 13:47:37 +0000176 uint32_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.com2ca1aaa2013-02-15 13:47:37 +0000186 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
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000404 const int paintIndex = paintDict->find(*dbmPaint);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000405
406 // kill the saveLayer and alter the DBMR2R's paint to be the modified one
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000407 convert_command_to_noop(writer, saveLayerInfo.fOffset);
408 uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
409 SkASSERT(dbmPaintId == *ptr);
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000410 *ptr = paintIndex;
robertphillips@google.come37ad352013-03-01 19:44:30 +0000411 return true;
412}
413
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000414/*
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000415 * Restore has just been called (but not recorded), look back at the
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000416 * matching save* and see if we are in the configuration:
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000417 * SAVE_LAYER (with NULL == bounds)
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000418 * SAVE
419 * CLIP_RECT
420 * DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
421 * RESTORE
422 * RESTORE
423 * where the saveLayer's color can be moved into the drawBitmap*'s paint
424 */
425static bool remove_save_layer2(SkWriter32* writer, int32_t offset,
426 SkPaintDictionary* paintDict) {
427
428 // back up to the save block
429 // TODO: add a stack to track save*/restore offsets rather than searching backwards
430 while (offset > 0) {
431 offset = *writer->peek32(offset);
432 }
433
434 int pattern[] = { SAVE_LAYER, SAVE, CLIP_RECT, kDRAW_BITMAP_FLAVOR, RESTORE, /* RESTORE */ };
435 CommandInfo result[SK_ARRAY_COUNT(pattern)];
436
437 if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
438 return false;
439 }
440
441 if (kSaveLayerWithBoundsSize == result[0].fSize) {
442 // The saveLayer's bound can offset where the dbm is drawn
443 return false;
444 }
445
446 return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
447 result[0], result[3]);
448}
robertphillips@google.come37ad352013-03-01 19:44:30 +0000449
450/*
451 * Restore has just been called (but not recorded), so look back at the
reed@google.comffacd3c2012-08-30 15:31:23 +0000452 * matching save(), and see if we can eliminate the pair of them, due to no
453 * intervening matrix/clip calls.
454 *
455 * If so, update the writer and return true, in which case we won't even record
456 * the restore() call. If we still need the restore(), return false.
457 */
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000458static bool collapse_save_clip_restore(SkWriter32* writer, int32_t offset,
459 SkPaintDictionary* paintDict) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000460#ifdef TRACK_COLLAPSE_STATS
461 gCollapseCalls += 1;
462#endif
463
464 int32_t restoreOffset = (int32_t)writer->size();
465
466 // back up to the save block
467 while (offset > 0) {
468 offset = *writer->peek32(offset);
469 }
470
471 // now offset points to a save
472 offset = -offset;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000473 uint32_t opSize;
474 DrawType op = peek_op_and_size(writer, offset, &opSize);
475 if (SAVE_LAYER == op) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000476 // not ready to cull these out yet (mrr)
477 return false;
478 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000479 SkASSERT(SAVE == op);
djsollen@google.comd4236572013-08-13 14:29:06 +0000480 SkASSERT(kSaveSize == opSize);
481
482 // get the save flag (last 4-bytes of the space allocated for the opSize)
483 SkCanvas::SaveFlags saveFlags = (SkCanvas::SaveFlags) *writer->peek32(offset+4);
484 if (SkCanvas::kMatrixClip_SaveFlag != saveFlags) {
485 // This function's optimization is only correct for kMatrixClip style saves.
486 // TODO: set checkMatrix & checkClip booleans here and then check for the
487 // offending operations in the following loop.
488 return false;
489 }
reed@google.comffacd3c2012-08-30 15:31:23 +0000490
491 // Walk forward until we get back to either a draw-verb (abort) or we hit
492 // our restore (success).
493 int32_t saveOffset = offset;
494
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000495 offset += opSize;
reed@google.comffacd3c2012-08-30 15:31:23 +0000496 while (offset < restoreOffset) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000497 op = peek_op_and_size(writer, offset, &opSize);
498 if ((op > CONCAT && op < ROTATE) || (SAVE_LAYER == op)) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000499 // drawing verb, abort
500 return false;
501 }
502 offset += opSize;
503 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000504
reed@google.comffacd3c2012-08-30 15:31:23 +0000505#ifdef TRACK_COLLAPSE_STATS
506 gCollapseCount += 1;
507 SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls,
508 (double)gCollapseCount / gCollapseCalls, "%");
509#endif
510
511 writer->rewindToOffset(saveOffset);
512 return true;
513}
514
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000515typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
516 SkPaintDictionary* paintDict);
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000517enum PictureRecordOptType {
518 kRewind_OptType, // Optimization rewinds the command stream
519 kCollapseSaveLayer_OptType, // Optimization eliminates a save/restore pair
520};
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000521
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000522struct PictureRecordOpt {
523 PictureRecordOptProc fProc;
524 PictureRecordOptType fType;
525};
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000526/*
527 * A list of the optimizations that are tried upon seeing a restore
528 * TODO: add a real API for such optimizations
529 * Add the ability to fire optimizations on any op (not just RESTORE)
530 */
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000531static const PictureRecordOpt gPictureRecordOpts[] = {
532 { collapse_save_clip_restore, kRewind_OptType },
533 { remove_save_layer1, kCollapseSaveLayer_OptType },
534 { remove_save_layer2, kCollapseSaveLayer_OptType }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000535};
536
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000537// This is called after an optimization has been applied to the command stream
538// in order to adjust the contents and state of the bounding box hierarchy and
539// state tree to reflect the optimization.
540static void apply_optimization_to_bbh(PictureRecordOptType opt, SkPictureStateTree* stateTree,
541 SkBBoxHierarchy* boundingHierarchy) {
542 switch (opt) {
543 case kCollapseSaveLayer_OptType:
robertphillips@google.com35e15632013-03-15 16:49:34 +0000544 if (NULL != stateTree) {
545 stateTree->saveCollapsed();
546 }
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000547 break;
548 case kRewind_OptType:
549 if (NULL != boundingHierarchy) {
550 boundingHierarchy->rewindInserts();
551 }
552 // Note: No need to touch the state tree for this to work correctly.
553 // Unused branches do not burden the playback, and pruning the tree
554 // would be O(N^2), so it is best to leave it alone.
555 break;
556 default:
557 SkASSERT(0);
558 }
559}
560
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561void SkPictureRecord::restore() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000562 // FIXME: SkDeferredCanvas needs to be refactored to respect
563 // save/restore balancing so that the following test can be
564 // turned on permanently.
565#if 0
566 SkASSERT(fRestoreOffsetStack.count() > 1);
567#endif
568
reed@android.comb4e22d62009-07-09 15:20:25 +0000569 // check for underflow
570 if (fRestoreOffsetStack.count() == 0) {
571 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 }
reed@android.comb4e22d62009-07-09 15:20:25 +0000573
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000574 if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
575 fFirstSavedLayerIndex = kNoSavedLayerIndex;
576 }
577
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000578 uint32_t initialOffset, size;
robertphillips@google.com31d81912013-04-12 15:24:29 +0000579 size_t opt = 0;
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000580 if (!(fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag)) {
581 for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
582 if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
583 // Some optimization fired so don't add the RESTORE
584 size = 0;
585 initialOffset = fWriter.size();
586 apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
587 fStateTree, fBoundingHierarchy);
588 break;
589 }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000590 }
skia.committer@gmail.com4bb50b22013-04-13 07:01:15 +0000591 }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000592
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000593 if ((fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag) ||
594 SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000595 // No optimization fired so add the RESTORE
reed@google.comffacd3c2012-08-30 15:31:23 +0000596 fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000597 size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000598 initialOffset = this->addDraw(RESTORE, &size);
reed@google.comffacd3c2012-08-30 15:31:23 +0000599 }
600
reed@android.comb4e22d62009-07-09 15:20:25 +0000601 fRestoreOffsetStack.pop();
reed@android.com32a42492009-07-10 03:33:52 +0000602
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000603 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 return this->INHERITED::restore();
605}
606
607bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000608 // op + dx + dy
609 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
610 uint32_t initialOffset = this->addDraw(TRANSLATE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 addScalar(dx);
612 addScalar(dy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000613 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 return this->INHERITED::translate(dx, dy);
615}
616
617bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000618 // op + sx + sy
619 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
620 uint32_t initialOffset = this->addDraw(SCALE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 addScalar(sx);
622 addScalar(sy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000623 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 return this->INHERITED::scale(sx, sy);
625}
626
627bool SkPictureRecord::rotate(SkScalar degrees) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000628 // op + degrees
629 uint32_t size = 1 * kUInt32Size + sizeof(SkScalar);
630 uint32_t initialOffset = this->addDraw(ROTATE, &size);
reed@google.com82065d62011-02-07 15:30:46 +0000631 addScalar(degrees);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000632 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 return this->INHERITED::rotate(degrees);
634}
635
636bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000637 // op + sx + sy
638 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
639 uint32_t initialOffset = this->addDraw(SKEW, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 addScalar(sx);
641 addScalar(sy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000642 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 return this->INHERITED::skew(sx, sy);
644}
645
646bool SkPictureRecord::concat(const SkMatrix& matrix) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000647 validate(fWriter.size(), 0);
648 // op + matrix index
649 uint32_t size = 2 * kUInt32Size;
650 uint32_t initialOffset = this->addDraw(CONCAT, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000652 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 return this->INHERITED::concat(matrix);
654}
655
reed@android.com6e073b92009-01-06 15:03:30 +0000656void SkPictureRecord::setMatrix(const SkMatrix& matrix) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000657 validate(fWriter.size(), 0);
658 // op + matrix index
659 uint32_t size = 2 * kUInt32Size;
660 uint32_t initialOffset = this->addDraw(SET_MATRIX, &size);
reed@android.com6e073b92009-01-06 15:03:30 +0000661 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000662 validate(initialOffset, size);
reed@android.com6e073b92009-01-06 15:03:30 +0000663 this->INHERITED::setMatrix(matrix);
664}
665
reed@google.com45482d12011-08-29 19:02:39 +0000666static bool regionOpExpands(SkRegion::Op op) {
667 switch (op) {
668 case SkRegion::kUnion_Op:
669 case SkRegion::kXOR_Op:
670 case SkRegion::kReverseDifference_Op:
671 case SkRegion::kReplace_Op:
672 return true;
673 case SkRegion::kIntersect_Op:
674 case SkRegion::kDifference_Op:
675 return false;
676 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000677 SkDEBUGFAIL("unknown region op");
reed@google.com45482d12011-08-29 19:02:39 +0000678 return false;
679 }
680}
681
robertphillips@google.come37ad352013-03-01 19:44:30 +0000682void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000683 int32_t offset = fRestoreOffsetStack.top();
684 while (offset > 0) {
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000685 uint32_t* peek = fWriter.peek32(offset);
686 offset = *peek;
687 *peek = restoreOffset;
688 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000689
reed@google.comffacd3c2012-08-30 15:31:23 +0000690#ifdef SK_DEBUG
691 // assert that the final offset value points to a save verb
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000692 uint32_t opSize;
693 DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize);
reed@google.comffacd3c2012-08-30 15:31:23 +0000694 SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
695#endif
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000696}
697
reed@google.comd86e7ab2012-09-27 20:31:31 +0000698void SkPictureRecord::beginRecording() {
699 // we have to call this *after* our constructor, to ensure that it gets
700 // recorded. This is balanced by restoreToCount() call from endRecording,
701 // which in-turn calls our overridden restore(), so those get recorded too.
702 fInitialSaveCount = this->save(kMatrixClip_SaveFlag);
703}
704
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000705void SkPictureRecord::endRecording() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000706 SkASSERT(kNoInitialSave != fInitialSaveCount);
707 this->restoreToCount(fInitialSaveCount);
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000708}
709
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000710void SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
reed@google.com21b519d2012-10-02 17:42:15 +0000711 if (fRestoreOffsetStack.isEmpty()) {
712 return;
713 }
714
reed@google.com45482d12011-08-29 19:02:39 +0000715 if (regionOpExpands(op)) {
716 // Run back through any previous clip ops, and mark their offset to
717 // be 0, disabling their ability to trigger a jump-to-restore, otherwise
718 // they could hide this clips ability to expand the clip (i.e. go from
719 // empty to non-empty).
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000720 fillRestoreOffsetPlaceholdersForCurrentStackLevel(0);
reed@google.com45482d12011-08-29 19:02:39 +0000721 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000722
reed@google.com45482d12011-08-29 19:02:39 +0000723 size_t offset = fWriter.size();
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000724 // The RestoreOffset field is initially filled with a placeholder
725 // value that points to the offset of the previous RestoreOffset
726 // in the current stack level, thus forming a linked list so that
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000727 // the restore offsets can be filled in when the corresponding
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000728 // restore command is recorded.
reed@google.com45482d12011-08-29 19:02:39 +0000729 addInt(fRestoreOffsetStack.top());
730 fRestoreOffsetStack.top() = offset;
731}
732
reed@google.com071eef92011-10-12 11:52:53 +0000733bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.comf9291502013-02-15 15:13:27 +0000734 // id + rect + clip params
735 uint32_t size = 1 * kUInt32Size + sizeof(rect) + 1 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000736 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000737 if (!fRestoreOffsetStack.isEmpty()) {
738 // + restore offset
739 size += kUInt32Size;
740 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000741 uint32_t initialOffset = this->addDraw(CLIP_RECT, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742 addRect(rect);
reed@google.com83ab4952011-11-11 21:34:54 +0000743 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000744 recordRestoreOffsetPlaceholder(op);
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000745
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000746 validate(initialOffset, size);
reed@google.com071eef92011-10-12 11:52:53 +0000747 return this->INHERITED::clipRect(rect, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748}
749
reed@google.com4ed0fb72012-12-12 20:48:18 +0000750bool SkPictureRecord::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
751 if (rrect.isRect()) {
752 return this->SkPictureRecord::clipRect(rrect.getBounds(), op, doAA);
753 }
754
robertphillips@google.comf9291502013-02-15 15:13:27 +0000755 // op + rrect + clip params
756 uint32_t size = 1 * kUInt32Size + SkRRect::kSizeInMemory + 1 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000757 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000758 if (!fRestoreOffsetStack.isEmpty()) {
759 // + restore offset
760 size += kUInt32Size;
761 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000762 uint32_t initialOffset = this->addDraw(CLIP_RRECT, &size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000763 addRRect(rrect);
764 addInt(ClipParams_pack(op, doAA));
765 recordRestoreOffsetPlaceholder(op);
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000766
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000767 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000768
769 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +0000770 return this->updateClipConservativelyUsingBounds(rrect.getBounds(), op, false);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000771 } else {
772 return this->INHERITED::clipRRect(rrect, op, doAA);
773 }
774}
775
reed@google.com071eef92011-10-12 11:52:53 +0000776bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000777
778 SkRect r;
reed@google.com907ef6c2012-12-10 15:50:37 +0000779 if (!path.isInverseFillType() && path.isRect(&r)) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000780 return this->clipRect(r, op, doAA);
781 }
782
robertphillips@google.comf9291502013-02-15 15:13:27 +0000783 // op + path index + clip params
784 uint32_t size = 3 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000785 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000786 if (!fRestoreOffsetStack.isEmpty()) {
787 // + restore offset
788 size += kUInt32Size;
789 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000790 uint32_t initialOffset = this->addDraw(CLIP_PATH, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 addPath(path);
reed@google.com83ab4952011-11-11 21:34:54 +0000792 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000793 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000794
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000795 validate(initialOffset, size);
reed@google.com82065d62011-02-07 15:30:46 +0000796
reed@android.comae814c82009-02-13 14:56:09 +0000797 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +0000798 return this->updateClipConservativelyUsingBounds(path.getBounds(), op,
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000799 path.isInverseFillType());
reed@android.comae814c82009-02-13 14:56:09 +0000800 } else {
reed@google.com071eef92011-10-12 11:52:53 +0000801 return this->INHERITED::clipPath(path, op, doAA);
reed@android.comae814c82009-02-13 14:56:09 +0000802 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803}
804
805bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
robertphillips@google.comf9291502013-02-15 15:13:27 +0000806 // op + region index + clip params
807 uint32_t size = 3 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000808 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000809 if (!fRestoreOffsetStack.isEmpty()) {
810 // + restore offset
811 size += kUInt32Size;
812 }
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000813 uint32_t initialOffset = this->addDraw(CLIP_REGION, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 addRegion(region);
reed@google.com83ab4952011-11-11 21:34:54 +0000815 addInt(ClipParams_pack(op, false));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000816 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000817
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000818 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 return this->INHERITED::clipRegion(region, op);
820}
821
reed@google.com2a981812011-04-14 18:59:28 +0000822void SkPictureRecord::clear(SkColor color) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000823 // op + color
824 uint32_t size = 2 * kUInt32Size;
825 uint32_t initialOffset = this->addDraw(DRAW_CLEAR, &size);
reed@google.com2a981812011-04-14 18:59:28 +0000826 addInt(color);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000827 validate(initialOffset, size);
reed@google.com2a981812011-04-14 18:59:28 +0000828}
829
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830void SkPictureRecord::drawPaint(const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000831 // op + paint index
832 uint32_t size = 2 * kUInt32Size;
833 uint32_t initialOffset = this->addDraw(DRAW_PAINT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000834 SkASSERT(initialOffset+getPaintOffset(DRAW_PAINT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 addPaint(paint);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000836 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837}
838
839void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000840 const SkPaint& paint) {
841 // op + paint index + mode + count + point data
842 uint32_t size = 4 * kUInt32Size + count * sizeof(SkPoint);
843 uint32_t initialOffset = this->addDraw(DRAW_POINTS, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000844 SkASSERT(initialOffset+getPaintOffset(DRAW_POINTS, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 addPaint(paint);
846 addInt(mode);
847 addInt(count);
848 fWriter.writeMul4(pts, count * sizeof(SkPoint));
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000849 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850}
851
reed@google.com4ed0fb72012-12-12 20:48:18 +0000852void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000853 // op + paint index + rect
854 uint32_t size = 2 * kUInt32Size + sizeof(oval);
855 uint32_t initialOffset = this->addDraw(DRAW_OVAL, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000856 SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000857 addPaint(paint);
858 addRect(oval);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000859 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000860}
861
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000863 // op + paint index + rect
864 uint32_t size = 2 * kUInt32Size + sizeof(rect);
865 uint32_t initialOffset = this->addDraw(DRAW_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000866 SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 addPaint(paint);
868 addRect(rect);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000869 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870}
871
reed@google.com4ed0fb72012-12-12 20:48:18 +0000872void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
873 if (rrect.isRect()) {
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000874 this->SkPictureRecord::drawRect(rrect.getBounds(), paint);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000875 } else if (rrect.isOval()) {
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000876 this->SkPictureRecord::drawOval(rrect.getBounds(), paint);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000877 } else {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000878 // op + paint index + rrect
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000879 uint32_t initialOffset, size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000880 size = 2 * kUInt32Size + SkRRect::kSizeInMemory;
881 initialOffset = this->addDraw(DRAW_RRECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000882 SkASSERT(initialOffset+getPaintOffset(DRAW_RRECT, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000883 addPaint(paint);
884 addRRect(rrect);
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000885 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000886 }
reed@google.com4ed0fb72012-12-12 20:48:18 +0000887}
888
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000890 // op + paint index + path index
891 uint32_t size = 3 * kUInt32Size;
892 uint32_t initialOffset = this->addDraw(DRAW_PATH, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000893 SkASSERT(initialOffset+getPaintOffset(DRAW_PATH, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 addPaint(paint);
895 addPath(path);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000896 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897}
898
899void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
900 const SkPaint* paint = NULL) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000901 // op + paint index + bitmap index + left + top
902 uint32_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar);
903 uint32_t initialOffset = this->addDraw(DRAW_BITMAP, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000904 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905 addPaintPtr(paint);
906 addBitmap(bitmap);
907 addScalar(left);
908 addScalar(top);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000909 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910}
911
reed@google.com71121732012-09-18 15:14:33 +0000912void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +0000913 const SkRect& dst, const SkPaint* paint,
914 DrawBitmapRectFlags flags) {
915 // id + paint index + bitmap index + bool for 'src' + flags
916 uint32_t size = 5 * kUInt32Size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000917 if (NULL != src) {
918 size += sizeof(*src); // + rect
919 }
920 size += sizeof(dst); // + rect
921
922 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_RECT_TO_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000923 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_RECT_TO_RECT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 addPaintPtr(paint);
925 addBitmap(bitmap);
reed@google.com71121732012-09-18 15:14:33 +0000926 addRectPtr(src); // may be null
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 addRect(dst);
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +0000928 addInt(flags);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000929 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930}
931
932void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
reed@google.comf0b5e112011-09-07 11:57:34 +0000933 const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000934 // id + paint index + bitmap index + matrix index
935 uint32_t size = 4 * kUInt32Size;
936 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000937 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_MATRIX, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 addPaintPtr(paint);
939 addBitmap(bitmap);
940 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000941 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942}
943
reed@google.comf0b5e112011-09-07 11:57:34 +0000944void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
945 const SkRect& dst, const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000946 // op + paint index + bitmap id + center + dst rect
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000947 uint32_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000948 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000949 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_NINE, size) == fWriter.size());
reed@google.comf0b5e112011-09-07 11:57:34 +0000950 addPaintPtr(paint);
951 addBitmap(bitmap);
952 addIRect(center);
953 addRect(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000954 validate(initialOffset, size);
reed@google.comf0b5e112011-09-07 11:57:34 +0000955}
956
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
958 const SkPaint* paint = NULL) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000959 // op + paint index + bitmap index + left + top
960 uint32_t size = 5 * kUInt32Size;
961 uint32_t initialOffset = this->addDraw(DRAW_SPRITE, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000962 SkASSERT(initialOffset+getPaintOffset(DRAW_SPRITE, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963 addPaintPtr(paint);
964 addBitmap(bitmap);
965 addInt(left);
966 addInt(top);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000967 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968}
969
reed@google.com45954262012-12-07 17:14:40 +0000970// Return fontmetrics.fTop,fBottom in topbot[0,1], after they have been
971// tweaked by paint.computeFastBounds().
972//
973static void computeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974 SkPaint::FontMetrics metrics;
975 paint.getFontMetrics(&metrics);
976 SkRect bounds;
977 // construct a rect so we can see any adjustments from the paint.
978 // we use 0,1 for left,right, just so the rect isn't empty
reed@google.com45954262012-12-07 17:14:40 +0000979 bounds.set(0, metrics.fTop, SK_Scalar1, metrics.fBottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 (void)paint.computeFastBounds(bounds, &bounds);
reed@google.com45954262012-12-07 17:14:40 +0000981 topbot[0] = bounds.fTop;
982 topbot[1] = bounds.fBottom;
983}
984
junov@chromium.orgf3b12232013-01-22 17:50:47 +0000985void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData& flat,
reed@google.com45954262012-12-07 17:14:40 +0000986 SkScalar minY, SkScalar maxY) {
junov@chromium.org3f5ecd62013-01-22 18:01:26 +0000987 if (!flat.isTopBotWritten()) {
988 computeFontMetricsTopBottom(paint, flat.writableTopBot());
989 SkASSERT(flat.isTopBotWritten());
reed@google.com45954262012-12-07 17:14:40 +0000990 }
junov@chromium.org3f5ecd62013-01-22 18:01:26 +0000991 addScalar(flat.topBot()[0] + minY);
992 addScalar(flat.topBot()[1] + maxY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993}
994
reed@google.com82065d62011-02-07 15:30:46 +0000995void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 SkScalar y, const SkPaint& paint) {
reed@google.com2eb5bb12012-04-12 14:27:42 +0000997 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com82065d62011-02-07 15:30:46 +0000998
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000999 // op + paint index + length + 'length' worth of chars + x + y
1000 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * sizeof(SkScalar);
1001 if (fast) {
1002 size += 2 * sizeof(SkScalar); // + top & bottom
1003 }
1004
robertphillips@google.come37ad352013-03-01 19:44:30 +00001005 DrawType op = fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT;
1006 uint32_t initialOffset = this->addDraw(op, &size);
1007 SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001008 const SkFlatData* flatPaintData = addPaint(paint);
1009 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 addText(text, byteLength);
1011 addScalar(x);
1012 addScalar(y);
1013 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001014 addFontMetricsTopBottom(paint, *flatPaintData, y, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001016 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017}
1018
reed@google.com82065d62011-02-07 15:30:46 +00001019void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020 const SkPoint pos[], const SkPaint& paint) {
1021 size_t points = paint.countText(text, byteLength);
1022 if (0 == points)
1023 return;
1024
1025 bool canUseDrawH = true;
reed@google.com9efd9a02012-01-30 15:41:43 +00001026 SkScalar minY = pos[0].fY;
1027 SkScalar maxY = pos[0].fY;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028 // check if the caller really should have used drawPosTextH()
1029 {
1030 const SkScalar firstY = pos[0].fY;
1031 for (size_t index = 1; index < points; index++) {
1032 if (pos[index].fY != firstY) {
1033 canUseDrawH = false;
reed@google.com9efd9a02012-01-30 15:41:43 +00001034 if (pos[index].fY < minY) {
1035 minY = pos[index].fY;
1036 } else if (pos[index].fY > maxY) {
1037 maxY = pos[index].fY;
1038 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039 }
1040 }
1041 }
reed@google.com82065d62011-02-07 15:30:46 +00001042
reed@google.com2eb5bb12012-04-12 14:27:42 +00001043 bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com9efd9a02012-01-30 15:41:43 +00001044 bool fast = canUseDrawH && fastBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001046 // op + paint index + length + 'length' worth of data + num points
1047 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1048 if (canUseDrawH) {
1049 if (fast) {
1050 size += 2 * sizeof(SkScalar); // + top & bottom
1051 }
1052 // + y-pos + actual x-point data
1053 size += sizeof(SkScalar) + points * sizeof(SkScalar);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054 } else {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001055 // + x&y point data
1056 size += points * sizeof(SkPoint);
1057 if (fastBounds) {
1058 size += 2 * sizeof(SkScalar); // + top & bottom
1059 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001061
1062 DrawType op;
1063 if (fast) {
1064 op = DRAW_POS_TEXT_H_TOP_BOTTOM;
1065 } else if (canUseDrawH) {
1066 op = DRAW_POS_TEXT_H;
1067 } else if (fastBounds) {
1068 op = DRAW_POS_TEXT_TOP_BOTTOM;
1069 } else {
1070 op = DRAW_POS_TEXT;
1071 }
1072 uint32_t initialOffset = this->addDraw(op, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001073 SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001074 const SkFlatData* flatPaintData = addPaint(paint);
1075 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 addText(text, byteLength);
1077 addInt(points);
1078
1079#ifdef SK_DEBUG_SIZE
1080 size_t start = fWriter.size();
1081#endif
1082 if (canUseDrawH) {
1083 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001084 addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 }
1086 addScalar(pos[0].fY);
1087 SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
reed@google.com82065d62011-02-07 15:30:46 +00001088 for (size_t index = 0; index < points; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 *xptr++ = pos[index].fX;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001090 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 fWriter.writeMul4(pos, points * sizeof(SkPoint));
reed@google.com9efd9a02012-01-30 15:41:43 +00001092 if (fastBounds) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001093 addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY);
reed@google.com9efd9a02012-01-30 15:41:43 +00001094 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 }
1096#ifdef SK_DEBUG_SIZE
1097 fPointBytes += fWriter.size() - start;
1098 fPointWrites += points;
1099#endif
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001100 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101}
1102
1103void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
1104 const SkScalar xpos[], SkScalar constY,
1105 const SkPaint& paint) {
1106 size_t points = paint.countText(text, byteLength);
1107 if (0 == points)
1108 return;
reed@google.com82065d62011-02-07 15:30:46 +00001109
reed@google.com2eb5bb12012-04-12 14:27:42 +00001110 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001112 // op + paint index + length + 'length' worth of data + num points
1113 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1114 if (fast) {
1115 size += 2 * sizeof(SkScalar); // + top & bottom
1116 }
1117 // + y + the actual points
1118 size += 1 * kUInt32Size + points * sizeof(SkScalar);
1119
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +00001120 uint32_t initialOffset = this->addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H,
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001121 &size);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001122 const SkFlatData* flatPaintData = addPaint(paint);
1123 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 addText(text, byteLength);
1125 addInt(points);
reed@google.com82065d62011-02-07 15:30:46 +00001126
reed@android.com8a1c16f2008-12-17 15:59:43 +00001127#ifdef SK_DEBUG_SIZE
1128 size_t start = fWriter.size();
1129#endif
1130 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001131 addFontMetricsTopBottom(paint, *flatPaintData, constY, constY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 }
1133 addScalar(constY);
1134 fWriter.writeMul4(xpos, points * sizeof(SkScalar));
1135#ifdef SK_DEBUG_SIZE
1136 fPointBytes += fWriter.size() - start;
1137 fPointWrites += points;
1138#endif
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001139 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140}
1141
reed@google.com82065d62011-02-07 15:30:46 +00001142void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength,
1143 const SkPath& path, const SkMatrix* matrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144 const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001145 // op + paint index + length + 'length' worth of data + path index + matrix index
1146 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * kUInt32Size;
1147 uint32_t initialOffset = this->addDraw(DRAW_TEXT_ON_PATH, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001148 SkASSERT(initialOffset+getPaintOffset(DRAW_TEXT_ON_PATH, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 addPaint(paint);
1150 addText(text, byteLength);
1151 addPath(path);
1152 addMatrixPtr(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001153 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154}
1155
1156void SkPictureRecord::drawPicture(SkPicture& picture) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001157 // op + picture index
1158 uint32_t size = 2 * kUInt32Size;
1159 uint32_t initialOffset = this->addDraw(DRAW_PICTURE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160 addPicture(picture);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001161 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162}
1163
1164void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
1165 const SkPoint vertices[], const SkPoint texs[],
1166 const SkColor colors[], SkXfermode*,
1167 const uint16_t indices[], int indexCount,
1168 const SkPaint& paint) {
1169 uint32_t flags = 0;
1170 if (texs) {
1171 flags |= DRAW_VERTICES_HAS_TEXS;
1172 }
1173 if (colors) {
1174 flags |= DRAW_VERTICES_HAS_COLORS;
1175 }
1176 if (indexCount > 0) {
1177 flags |= DRAW_VERTICES_HAS_INDICES;
1178 }
1179
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001180 // op + paint index + flags + vmode + vCount + vertices
1181 uint32_t size = 5 * kUInt32Size + vertexCount * sizeof(SkPoint);
1182 if (flags & DRAW_VERTICES_HAS_TEXS) {
1183 size += vertexCount * sizeof(SkPoint); // + uvs
1184 }
1185 if (flags & DRAW_VERTICES_HAS_COLORS) {
1186 size += vertexCount * sizeof(SkColor); // + vert colors
1187 }
1188 if (flags & DRAW_VERTICES_HAS_INDICES) {
1189 // + num indices + indices
1190 size += 1 * kUInt32Size + SkAlign4(indexCount * sizeof(uint16_t));
1191 }
1192
1193 uint32_t initialOffset = this->addDraw(DRAW_VERTICES, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001194 SkASSERT(initialOffset+getPaintOffset(DRAW_VERTICES, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 addPaint(paint);
1196 addInt(flags);
1197 addInt(vmode);
1198 addInt(vertexCount);
1199 addPoints(vertices, vertexCount);
1200 if (flags & DRAW_VERTICES_HAS_TEXS) {
1201 addPoints(texs, vertexCount);
1202 }
1203 if (flags & DRAW_VERTICES_HAS_COLORS) {
1204 fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
1205 }
1206 if (flags & DRAW_VERTICES_HAS_INDICES) {
1207 addInt(indexCount);
1208 fWriter.writePad(indices, indexCount * sizeof(uint16_t));
1209 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001210 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211}
1212
reed@android.comcb608442009-12-04 21:32:27 +00001213void SkPictureRecord::drawData(const void* data, size_t length) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001214 // op + length + 'length' worth of data
1215 uint32_t size = 2 * kUInt32Size + SkAlign4(length);
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +00001216 uint32_t initialOffset = this->addDraw(DRAW_DATA, &size);
reed@android.comcb608442009-12-04 21:32:27 +00001217 addInt(length);
1218 fWriter.writePad(data, length);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001219 validate(initialOffset, size);
reed@android.comcb608442009-12-04 21:32:27 +00001220}
1221
robertphillips@google.com0a4805e2013-05-29 13:24:23 +00001222void SkPictureRecord::beginCommentGroup(const char* description) {
1223 // op/size + length of string + \0 terminated chars
1224 int length = strlen(description);
1225 uint32_t size = 2 * kUInt32Size + SkAlign4(length + 1);
1226 uint32_t initialOffset = this->addDraw(BEGIN_COMMENT_GROUP, &size);
1227 fWriter.writeString(description, length);
1228 validate(initialOffset, size);
1229}
1230
1231void SkPictureRecord::addComment(const char* kywd, const char* value) {
1232 // op/size + 2x length of string + 2x \0 terminated chars
1233 int kywdLen = strlen(kywd);
1234 int valueLen = strlen(value);
1235 uint32_t size = 3 * kUInt32Size + SkAlign4(kywdLen + 1) + SkAlign4(valueLen + 1);
1236 uint32_t initialOffset = this->addDraw(COMMENT, &size);
1237 fWriter.writeString(kywd, kywdLen);
1238 fWriter.writeString(value, valueLen);
1239 validate(initialOffset, size);
1240}
1241
1242void SkPictureRecord::endCommentGroup() {
1243 // op/size
1244 uint32_t size = 1 * kUInt32Size;
1245 uint32_t initialOffset = this->addDraw(END_COMMENT_GROUP, &size);
1246 validate(initialOffset, size);
1247}
1248
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249///////////////////////////////////////////////////////////////////////////////
reed@google.com82065d62011-02-07 15:30:46 +00001250
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
scroggo@google.com4b90b112012-12-04 15:08:56 +00001252 const int index = fBitmapHeap->insert(bitmap);
1253 // In debug builds, a bad return value from insert() will crash, allowing for debugging. In
1254 // release builds, the invalid value will be recorded so that the reader will know that there
1255 // was a problem.
1256 SkASSERT(index != SkBitmapHeap::INVALID_SLOT);
1257 addInt(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258}
1259
1260void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
1261 addMatrixPtr(&matrix);
1262}
1263
1264void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
reed@google.com83ca3372012-07-12 15:27:54 +00001265 this->addInt(matrix ? fMatrices.find(*matrix) : 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266}
1267
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001268const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
1269 const SkFlatData* data = paint ? fPaints.findAndReturnFlat(*paint) : NULL;
1270 int index = data ? data->index() : 0;
reed@google.com45954262012-12-07 17:14:40 +00001271 this->addInt(index);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001272 return data;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273}
1274
1275void SkPictureRecord::addPath(const SkPath& path) {
1276 if (NULL == fPathHeap) {
1277 fPathHeap = SkNEW(SkPathHeap);
1278 }
1279 addInt(fPathHeap->append(path));
1280}
1281
1282void SkPictureRecord::addPicture(SkPicture& picture) {
1283 int index = fPictureRefs.find(&picture);
1284 if (index < 0) { // not found
1285 index = fPictureRefs.count();
1286 *fPictureRefs.append() = &picture;
1287 picture.ref();
1288 }
1289 // follow the convention of recording a 1-based index
1290 addInt(index + 1);
1291}
1292
1293void SkPictureRecord::addPoint(const SkPoint& point) {
1294#ifdef SK_DEBUG_SIZE
1295 size_t start = fWriter.size();
1296#endif
1297 fWriter.writePoint(point);
1298#ifdef SK_DEBUG_SIZE
1299 fPointBytes += fWriter.size() - start;
1300 fPointWrites++;
1301#endif
1302}
reed@google.com82065d62011-02-07 15:30:46 +00001303
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
1305 fWriter.writeMul4(pts, count * sizeof(SkPoint));
1306#ifdef SK_DEBUG_SIZE
1307 fPointBytes += count * sizeof(SkPoint);
1308 fPointWrites++;
1309#endif
1310}
1311
1312void SkPictureRecord::addRect(const SkRect& rect) {
1313#ifdef SK_DEBUG_SIZE
1314 size_t start = fWriter.size();
1315#endif
1316 fWriter.writeRect(rect);
1317#ifdef SK_DEBUG_SIZE
1318 fRectBytes += fWriter.size() - start;
1319 fRectWrites++;
1320#endif
1321}
1322
1323void SkPictureRecord::addRectPtr(const SkRect* rect) {
1324 if (fWriter.writeBool(rect != NULL)) {
1325 fWriter.writeRect(*rect);
1326 }
1327}
1328
reed@google.comf0b5e112011-09-07 11:57:34 +00001329void SkPictureRecord::addIRect(const SkIRect& rect) {
1330 fWriter.write(&rect, sizeof(rect));
1331}
1332
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
1334 if (fWriter.writeBool(rect != NULL)) {
1335 *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
1336 }
1337}
1338
reed@google.com4ed0fb72012-12-12 20:48:18 +00001339void SkPictureRecord::addRRect(const SkRRect& rrect) {
1340 fWriter.writeRRect(rrect);
1341}
1342
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343void SkPictureRecord::addRegion(const SkRegion& region) {
reed@google.com83ca3372012-07-12 15:27:54 +00001344 addInt(fRegions.find(region));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345}
1346
1347void SkPictureRecord::addText(const void* text, size_t byteLength) {
1348#ifdef SK_DEBUG_SIZE
1349 size_t start = fWriter.size();
1350#endif
1351 addInt(byteLength);
1352 fWriter.writePad(text, byteLength);
1353#ifdef SK_DEBUG_SIZE
1354 fTextBytes += fWriter.size() - start;
1355 fTextWrites++;
1356#endif
1357}
1358
1359///////////////////////////////////////////////////////////////////////////////
1360
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361#ifdef SK_DEBUG_SIZE
1362size_t SkPictureRecord::size() const {
1363 size_t result = 0;
1364 size_t sizeData;
1365 bitmaps(&sizeData);
1366 result += sizeData;
1367 matrices(&sizeData);
1368 result += sizeData;
1369 paints(&sizeData);
1370 result += sizeData;
1371 paths(&sizeData);
1372 result += sizeData;
1373 pictures(&sizeData);
1374 result += sizeData;
1375 regions(&sizeData);
1376 result += sizeData;
1377 result += streamlen();
1378 return result;
1379}
1380
1381int SkPictureRecord::bitmaps(size_t* size) const {
1382 size_t result = 0;
1383 int count = fBitmaps.count();
reed@google.com82065d62011-02-07 15:30:46 +00001384 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001385 result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
1386 *size = result;
1387 return count;
1388}
1389
1390int SkPictureRecord::matrices(size_t* size) const {
1391 int count = fMatrices.count();
1392 *size = sizeof(fMatrices[0]) * count;
1393 return count;
1394}
1395
1396int SkPictureRecord::paints(size_t* size) const {
1397 size_t result = 0;
1398 int count = fPaints.count();
reed@google.com82065d62011-02-07 15:30:46 +00001399 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 result += sizeof(fPaints[index]) + fPaints[index]->size();
1401 *size = result;
1402 return count;
1403}
1404
1405int SkPictureRecord::paths(size_t* size) const {
1406 size_t result = 0;
1407 int count = fPaths.count();
reed@google.com82065d62011-02-07 15:30:46 +00001408 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 result += sizeof(fPaths[index]) + fPaths[index]->size();
1410 *size = result;
1411 return count;
1412}
1413
1414int SkPictureRecord::regions(size_t* size) const {
1415 size_t result = 0;
1416 int count = fRegions.count();
reed@google.com82065d62011-02-07 15:30:46 +00001417 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418 result += sizeof(fRegions[index]) + fRegions[index]->size();
1419 *size = result;
1420 return count;
1421}
1422
1423size_t SkPictureRecord::streamlen() const {
1424 return fWriter.size();
1425}
1426#endif
1427
1428#ifdef SK_DEBUG_VALIDATE
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001429void SkPictureRecord::validate(uint32_t initialOffset, uint32_t size) const {
1430 SkASSERT(fWriter.size() == initialOffset + size);
1431
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 validateBitmaps();
1433 validateMatrices();
1434 validatePaints();
1435 validatePaths();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436 validateRegions();
1437}
1438
1439void SkPictureRecord::validateBitmaps() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001440 int count = fBitmapHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441 SkASSERT((unsigned) count < 0x1000);
1442 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001443 const SkBitmap* bitPtr = fBitmapHeap->getBitmap(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 SkASSERT(bitPtr);
1445 bitPtr->validate();
1446 }
1447}
1448
1449void SkPictureRecord::validateMatrices() const {
1450 int count = fMatrices.count();
1451 SkASSERT((unsigned) count < 0x1000);
1452 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001453 const SkFlatData* matrix = fMatrices[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 SkASSERT(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001455// matrix->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456 }
1457}
1458
1459void SkPictureRecord::validatePaints() const {
1460 int count = fPaints.count();
1461 SkASSERT((unsigned) count < 0x1000);
1462 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001463 const SkFlatData* paint = fPaints[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464 SkASSERT(paint);
1465// paint->validate();
1466 }
1467}
1468
1469void SkPictureRecord::validatePaths() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001470 if (NULL == fPathHeap) {
1471 return;
1472 }
1473
1474 int count = fPathHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475 SkASSERT((unsigned) count < 0x1000);
1476 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001477 const SkPath& path = (*fPathHeap)[index];
1478 path.validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479 }
1480}
1481
1482void SkPictureRecord::validateRegions() const {
1483 int count = fRegions.count();
1484 SkASSERT((unsigned) count < 0x1000);
1485 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001486 const SkFlatData* region = fRegions[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001487 SkASSERT(region);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001488// region->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489 }
1490}
1491#endif