blob: 2c2d334caf02d03dd3ee831550d851c4cccf2d22 [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
reed@google.comd86e7ab2012-09-27 20:31:31 +000031SkPictureRecord::SkPictureRecord(uint32_t flags, SkDevice* device) :
32 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
junov@chromium.org4e6dfa52012-07-16 14:04:59 +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
404 const SkFlatData* data = paintDict->findAndReturnFlat(*dbmPaint);
405 if (NULL == data) {
406 return false;
407 }
408
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);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000413 *ptr = data->index();
414 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.com2ca1aaa2013-02-15 13:47:37 +0000606 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);
613 uint32_t initialOffset = this->addDraw(TRANSLATE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 addScalar(dx);
615 addScalar(dy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000616 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);
623 uint32_t initialOffset = this->addDraw(SCALE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 addScalar(sx);
625 addScalar(sy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000626 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);
633 uint32_t initialOffset = this->addDraw(ROTATE, &size);
reed@google.com82065d62011-02-07 15:30:46 +0000634 addScalar(degrees);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000635 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);
642 uint32_t initialOffset = this->addDraw(SKEW, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 addScalar(sx);
644 addScalar(sy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000645 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.com2ca1aaa2013-02-15 13:47:37 +0000650 validate(fWriter.size(), 0);
651 // op + matrix index
652 uint32_t size = 2 * kUInt32Size;
653 uint32_t initialOffset = this->addDraw(CONCAT, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000655 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.com2ca1aaa2013-02-15 13:47:37 +0000660 validate(fWriter.size(), 0);
661 // op + matrix index
662 uint32_t size = 2 * kUInt32Size;
663 uint32_t initialOffset = this->addDraw(SET_MATRIX, &size);
reed@android.com6e073b92009-01-06 15:03:30 +0000664 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000665 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
reed@google.com45482d12011-08-29 19:02:39 +0000718 if (regionOpExpands(op)) {
719 // Run back through any previous clip ops, and mark their offset to
720 // be 0, disabling their ability to trigger a jump-to-restore, otherwise
721 // they could hide this clips ability to expand the clip (i.e. go from
722 // empty to non-empty).
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000723 fillRestoreOffsetPlaceholdersForCurrentStackLevel(0);
reed@google.com45482d12011-08-29 19:02:39 +0000724 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000725
reed@google.com45482d12011-08-29 19:02:39 +0000726 size_t offset = fWriter.size();
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000727 // The RestoreOffset field is initially filled with a placeholder
728 // value that points to the offset of the previous RestoreOffset
729 // in the current stack level, thus forming a linked list so that
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000730 // the restore offsets can be filled in when the corresponding
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000731 // restore command is recorded.
reed@google.com45482d12011-08-29 19:02:39 +0000732 addInt(fRestoreOffsetStack.top());
733 fRestoreOffsetStack.top() = offset;
734}
735
reed@google.com071eef92011-10-12 11:52:53 +0000736bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.comf9291502013-02-15 15:13:27 +0000737 // id + rect + clip params
738 uint32_t size = 1 * kUInt32Size + sizeof(rect) + 1 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000739 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000740 if (!fRestoreOffsetStack.isEmpty()) {
741 // + restore offset
742 size += kUInt32Size;
743 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000744 uint32_t initialOffset = this->addDraw(CLIP_RECT, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 addRect(rect);
reed@google.com83ab4952011-11-11 21:34:54 +0000746 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000747 recordRestoreOffsetPlaceholder(op);
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000748
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000749 validate(initialOffset, size);
reed@google.com071eef92011-10-12 11:52:53 +0000750 return this->INHERITED::clipRect(rect, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751}
752
reed@google.com4ed0fb72012-12-12 20:48:18 +0000753bool SkPictureRecord::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
754 if (rrect.isRect()) {
755 return this->SkPictureRecord::clipRect(rrect.getBounds(), op, doAA);
756 }
757
robertphillips@google.comf9291502013-02-15 15:13:27 +0000758 // op + rrect + clip params
759 uint32_t size = 1 * kUInt32Size + SkRRect::kSizeInMemory + 1 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000760 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000761 if (!fRestoreOffsetStack.isEmpty()) {
762 // + restore offset
763 size += kUInt32Size;
764 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000765 uint32_t initialOffset = this->addDraw(CLIP_RRECT, &size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000766 addRRect(rrect);
767 addInt(ClipParams_pack(op, doAA));
768 recordRestoreOffsetPlaceholder(op);
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000769
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000770 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000771
772 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +0000773 return this->updateClipConservativelyUsingBounds(rrect.getBounds(), op, false);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000774 } else {
775 return this->INHERITED::clipRRect(rrect, op, doAA);
776 }
777}
778
reed@google.com071eef92011-10-12 11:52:53 +0000779bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000780
781 SkRect r;
reed@google.com907ef6c2012-12-10 15:50:37 +0000782 if (!path.isInverseFillType() && path.isRect(&r)) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000783 return this->clipRect(r, op, doAA);
784 }
785
robertphillips@google.comf9291502013-02-15 15:13:27 +0000786 // op + path index + clip params
787 uint32_t size = 3 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000788 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000789 if (!fRestoreOffsetStack.isEmpty()) {
790 // + restore offset
791 size += kUInt32Size;
792 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000793 uint32_t initialOffset = this->addDraw(CLIP_PATH, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 addPath(path);
reed@google.com83ab4952011-11-11 21:34:54 +0000795 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000796 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000797
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000798 validate(initialOffset, size);
reed@google.com82065d62011-02-07 15:30:46 +0000799
reed@android.comae814c82009-02-13 14:56:09 +0000800 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +0000801 return this->updateClipConservativelyUsingBounds(path.getBounds(), op,
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000802 path.isInverseFillType());
reed@android.comae814c82009-02-13 14:56:09 +0000803 } else {
reed@google.com071eef92011-10-12 11:52:53 +0000804 return this->INHERITED::clipPath(path, op, doAA);
reed@android.comae814c82009-02-13 14:56:09 +0000805 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806}
807
808bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
robertphillips@google.comf9291502013-02-15 15:13:27 +0000809 // op + region index + clip params
810 uint32_t size = 3 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000811 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000812 if (!fRestoreOffsetStack.isEmpty()) {
813 // + restore offset
814 size += kUInt32Size;
815 }
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000816 uint32_t initialOffset = this->addDraw(CLIP_REGION, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 addRegion(region);
reed@google.com83ab4952011-11-11 21:34:54 +0000818 addInt(ClipParams_pack(op, false));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000819 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000820
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000821 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 return this->INHERITED::clipRegion(region, op);
823}
824
reed@google.com2a981812011-04-14 18:59:28 +0000825void SkPictureRecord::clear(SkColor color) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000826 // op + color
827 uint32_t size = 2 * kUInt32Size;
828 uint32_t initialOffset = this->addDraw(DRAW_CLEAR, &size);
reed@google.com2a981812011-04-14 18:59:28 +0000829 addInt(color);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000830 validate(initialOffset, size);
reed@google.com2a981812011-04-14 18:59:28 +0000831}
832
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833void SkPictureRecord::drawPaint(const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000834 // op + paint index
835 uint32_t size = 2 * kUInt32Size;
836 uint32_t initialOffset = this->addDraw(DRAW_PAINT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000837 SkASSERT(initialOffset+getPaintOffset(DRAW_PAINT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 addPaint(paint);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000839 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840}
841
842void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000843 const SkPaint& paint) {
844 // op + paint index + mode + count + point data
845 uint32_t size = 4 * kUInt32Size + count * sizeof(SkPoint);
846 uint32_t initialOffset = this->addDraw(DRAW_POINTS, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000847 SkASSERT(initialOffset+getPaintOffset(DRAW_POINTS, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 addPaint(paint);
849 addInt(mode);
850 addInt(count);
851 fWriter.writeMul4(pts, count * sizeof(SkPoint));
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000852 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853}
854
reed@google.com4ed0fb72012-12-12 20:48:18 +0000855void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000856 // op + paint index + rect
857 uint32_t size = 2 * kUInt32Size + sizeof(oval);
858 uint32_t initialOffset = this->addDraw(DRAW_OVAL, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000859 SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000860 addPaint(paint);
861 addRect(oval);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000862 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000863}
864
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000866 // op + paint index + rect
867 uint32_t size = 2 * kUInt32Size + sizeof(rect);
868 uint32_t initialOffset = this->addDraw(DRAW_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000869 SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 addPaint(paint);
871 addRect(rect);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000872 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873}
874
reed@google.com4ed0fb72012-12-12 20:48:18 +0000875void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
876 if (rrect.isRect()) {
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000877 this->SkPictureRecord::drawRect(rrect.getBounds(), paint);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000878 } else if (rrect.isOval()) {
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000879 this->SkPictureRecord::drawOval(rrect.getBounds(), paint);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000880 } else {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000881 // op + paint index + rrect
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000882 uint32_t initialOffset, size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000883 size = 2 * kUInt32Size + SkRRect::kSizeInMemory;
884 initialOffset = this->addDraw(DRAW_RRECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000885 SkASSERT(initialOffset+getPaintOffset(DRAW_RRECT, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000886 addPaint(paint);
887 addRRect(rrect);
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000888 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000889 }
reed@google.com4ed0fb72012-12-12 20:48:18 +0000890}
891
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000893 // op + paint index + path index
894 uint32_t size = 3 * kUInt32Size;
895 uint32_t initialOffset = this->addDraw(DRAW_PATH, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000896 SkASSERT(initialOffset+getPaintOffset(DRAW_PATH, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 addPaint(paint);
898 addPath(path);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000899 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900}
901
902void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
903 const SkPaint* paint = NULL) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000904 // op + paint index + bitmap index + left + top
905 uint32_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar);
906 uint32_t initialOffset = this->addDraw(DRAW_BITMAP, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000907 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 addPaintPtr(paint);
909 addBitmap(bitmap);
910 addScalar(left);
911 addScalar(top);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000912 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913}
914
reed@google.com71121732012-09-18 15:14:33 +0000915void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 const SkRect& dst, const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000917 // id + paint index + bitmap index + bool for 'src'
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000918 uint32_t size = 4 * kUInt32Size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000919 if (NULL != src) {
920 size += sizeof(*src); // + rect
921 }
922 size += sizeof(dst); // + rect
923
924 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_RECT_TO_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000925 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_RECT_TO_RECT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000926 addPaintPtr(paint);
927 addBitmap(bitmap);
reed@google.com71121732012-09-18 15:14:33 +0000928 addRectPtr(src); // may be null
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 addRect(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000930 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931}
932
933void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
reed@google.comf0b5e112011-09-07 11:57:34 +0000934 const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000935 // id + paint index + bitmap index + matrix index
936 uint32_t size = 4 * kUInt32Size;
937 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000938 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_MATRIX, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939 addPaintPtr(paint);
940 addBitmap(bitmap);
941 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000942 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943}
944
reed@google.comf0b5e112011-09-07 11:57:34 +0000945void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
946 const SkRect& dst, const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000947 // op + paint index + bitmap id + center + dst rect
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000948 uint32_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000949 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000950 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_NINE, size) == fWriter.size());
reed@google.comf0b5e112011-09-07 11:57:34 +0000951 addPaintPtr(paint);
952 addBitmap(bitmap);
953 addIRect(center);
954 addRect(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000955 validate(initialOffset, size);
reed@google.comf0b5e112011-09-07 11:57:34 +0000956}
957
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
959 const SkPaint* paint = NULL) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000960 // op + paint index + bitmap index + left + top
961 uint32_t size = 5 * kUInt32Size;
962 uint32_t initialOffset = this->addDraw(DRAW_SPRITE, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000963 SkASSERT(initialOffset+getPaintOffset(DRAW_SPRITE, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 addPaintPtr(paint);
965 addBitmap(bitmap);
966 addInt(left);
967 addInt(top);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000968 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000969}
970
reed@google.com45954262012-12-07 17:14:40 +0000971// Return fontmetrics.fTop,fBottom in topbot[0,1], after they have been
972// tweaked by paint.computeFastBounds().
973//
974static void computeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 SkPaint::FontMetrics metrics;
976 paint.getFontMetrics(&metrics);
977 SkRect bounds;
978 // construct a rect so we can see any adjustments from the paint.
979 // we use 0,1 for left,right, just so the rect isn't empty
reed@google.com45954262012-12-07 17:14:40 +0000980 bounds.set(0, metrics.fTop, SK_Scalar1, metrics.fBottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 (void)paint.computeFastBounds(bounds, &bounds);
reed@google.com45954262012-12-07 17:14:40 +0000982 topbot[0] = bounds.fTop;
983 topbot[1] = bounds.fBottom;
984}
985
junov@chromium.orgf3b12232013-01-22 17:50:47 +0000986void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData& flat,
reed@google.com45954262012-12-07 17:14:40 +0000987 SkScalar minY, SkScalar maxY) {
junov@chromium.org3f5ecd62013-01-22 18:01:26 +0000988 if (!flat.isTopBotWritten()) {
989 computeFontMetricsTopBottom(paint, flat.writableTopBot());
990 SkASSERT(flat.isTopBotWritten());
reed@google.com45954262012-12-07 17:14:40 +0000991 }
junov@chromium.org3f5ecd62013-01-22 18:01:26 +0000992 addScalar(flat.topBot()[0] + minY);
993 addScalar(flat.topBot()[1] + maxY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994}
995
reed@google.com82065d62011-02-07 15:30:46 +0000996void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 SkScalar y, const SkPaint& paint) {
reed@google.com2eb5bb12012-04-12 14:27:42 +0000998 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com82065d62011-02-07 15:30:46 +0000999
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001000 // op + paint index + length + 'length' worth of chars + x + y
1001 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * sizeof(SkScalar);
1002 if (fast) {
1003 size += 2 * sizeof(SkScalar); // + top & bottom
1004 }
1005
robertphillips@google.come37ad352013-03-01 19:44:30 +00001006 DrawType op = fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT;
1007 uint32_t initialOffset = this->addDraw(op, &size);
1008 SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001009 const SkFlatData* flatPaintData = addPaint(paint);
1010 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011 addText(text, byteLength);
1012 addScalar(x);
1013 addScalar(y);
1014 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001015 addFontMetricsTopBottom(paint, *flatPaintData, y, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001017 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018}
1019
reed@google.com82065d62011-02-07 15:30:46 +00001020void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021 const SkPoint pos[], const SkPaint& paint) {
1022 size_t points = paint.countText(text, byteLength);
1023 if (0 == points)
1024 return;
1025
1026 bool canUseDrawH = true;
reed@google.com9efd9a02012-01-30 15:41:43 +00001027 SkScalar minY = pos[0].fY;
1028 SkScalar maxY = pos[0].fY;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 // check if the caller really should have used drawPosTextH()
1030 {
1031 const SkScalar firstY = pos[0].fY;
1032 for (size_t index = 1; index < points; index++) {
1033 if (pos[index].fY != firstY) {
1034 canUseDrawH = false;
reed@google.com9efd9a02012-01-30 15:41:43 +00001035 if (pos[index].fY < minY) {
1036 minY = pos[index].fY;
1037 } else if (pos[index].fY > maxY) {
1038 maxY = pos[index].fY;
1039 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 }
1041 }
1042 }
reed@google.com82065d62011-02-07 15:30:46 +00001043
reed@google.com2eb5bb12012-04-12 14:27:42 +00001044 bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com9efd9a02012-01-30 15:41:43 +00001045 bool fast = canUseDrawH && fastBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001047 // op + paint index + length + 'length' worth of data + num points
1048 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1049 if (canUseDrawH) {
1050 if (fast) {
1051 size += 2 * sizeof(SkScalar); // + top & bottom
1052 }
1053 // + y-pos + actual x-point data
1054 size += sizeof(SkScalar) + points * sizeof(SkScalar);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 } else {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001056 // + x&y point data
1057 size += points * sizeof(SkPoint);
1058 if (fastBounds) {
1059 size += 2 * sizeof(SkScalar); // + top & bottom
1060 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001062
1063 DrawType op;
1064 if (fast) {
1065 op = DRAW_POS_TEXT_H_TOP_BOTTOM;
1066 } else if (canUseDrawH) {
1067 op = DRAW_POS_TEXT_H;
1068 } else if (fastBounds) {
1069 op = DRAW_POS_TEXT_TOP_BOTTOM;
1070 } else {
1071 op = DRAW_POS_TEXT;
1072 }
1073 uint32_t initialOffset = this->addDraw(op, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001074 SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001075 const SkFlatData* flatPaintData = addPaint(paint);
1076 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 addText(text, byteLength);
1078 addInt(points);
1079
1080#ifdef SK_DEBUG_SIZE
1081 size_t start = fWriter.size();
1082#endif
1083 if (canUseDrawH) {
1084 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001085 addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 }
1087 addScalar(pos[0].fY);
1088 SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
reed@google.com82065d62011-02-07 15:30:46 +00001089 for (size_t index = 0; index < points; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 *xptr++ = pos[index].fX;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001091 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 fWriter.writeMul4(pos, points * sizeof(SkPoint));
reed@google.com9efd9a02012-01-30 15:41:43 +00001093 if (fastBounds) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001094 addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY);
reed@google.com9efd9a02012-01-30 15:41:43 +00001095 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 }
1097#ifdef SK_DEBUG_SIZE
1098 fPointBytes += fWriter.size() - start;
1099 fPointWrites += points;
1100#endif
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001101 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102}
1103
1104void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
1105 const SkScalar xpos[], SkScalar constY,
1106 const SkPaint& paint) {
1107 size_t points = paint.countText(text, byteLength);
1108 if (0 == points)
1109 return;
reed@google.com82065d62011-02-07 15:30:46 +00001110
reed@google.com2eb5bb12012-04-12 14:27:42 +00001111 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001113 // op + paint index + length + 'length' worth of data + num points
1114 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1115 if (fast) {
1116 size += 2 * sizeof(SkScalar); // + top & bottom
1117 }
1118 // + y + the actual points
1119 size += 1 * kUInt32Size + points * sizeof(SkScalar);
1120
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +00001121 uint32_t initialOffset = this->addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H,
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001122 &size);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001123 const SkFlatData* flatPaintData = addPaint(paint);
1124 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 addText(text, byteLength);
1126 addInt(points);
reed@google.com82065d62011-02-07 15:30:46 +00001127
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128#ifdef SK_DEBUG_SIZE
1129 size_t start = fWriter.size();
1130#endif
1131 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001132 addFontMetricsTopBottom(paint, *flatPaintData, constY, constY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 }
1134 addScalar(constY);
1135 fWriter.writeMul4(xpos, points * sizeof(SkScalar));
1136#ifdef SK_DEBUG_SIZE
1137 fPointBytes += fWriter.size() - start;
1138 fPointWrites += points;
1139#endif
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001140 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141}
1142
reed@google.com82065d62011-02-07 15:30:46 +00001143void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength,
1144 const SkPath& path, const SkMatrix* matrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001146 // op + paint index + length + 'length' worth of data + path index + matrix index
1147 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * kUInt32Size;
1148 uint32_t initialOffset = this->addDraw(DRAW_TEXT_ON_PATH, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001149 SkASSERT(initialOffset+getPaintOffset(DRAW_TEXT_ON_PATH, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 addPaint(paint);
1151 addText(text, byteLength);
1152 addPath(path);
1153 addMatrixPtr(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001154 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155}
1156
1157void SkPictureRecord::drawPicture(SkPicture& picture) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001158 // op + picture index
1159 uint32_t size = 2 * kUInt32Size;
1160 uint32_t initialOffset = this->addDraw(DRAW_PICTURE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161 addPicture(picture);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001162 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163}
1164
1165void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
1166 const SkPoint vertices[], const SkPoint texs[],
1167 const SkColor colors[], SkXfermode*,
1168 const uint16_t indices[], int indexCount,
1169 const SkPaint& paint) {
1170 uint32_t flags = 0;
1171 if (texs) {
1172 flags |= DRAW_VERTICES_HAS_TEXS;
1173 }
1174 if (colors) {
1175 flags |= DRAW_VERTICES_HAS_COLORS;
1176 }
1177 if (indexCount > 0) {
1178 flags |= DRAW_VERTICES_HAS_INDICES;
1179 }
1180
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001181 // op + paint index + flags + vmode + vCount + vertices
1182 uint32_t size = 5 * kUInt32Size + vertexCount * sizeof(SkPoint);
1183 if (flags & DRAW_VERTICES_HAS_TEXS) {
1184 size += vertexCount * sizeof(SkPoint); // + uvs
1185 }
1186 if (flags & DRAW_VERTICES_HAS_COLORS) {
1187 size += vertexCount * sizeof(SkColor); // + vert colors
1188 }
1189 if (flags & DRAW_VERTICES_HAS_INDICES) {
1190 // + num indices + indices
1191 size += 1 * kUInt32Size + SkAlign4(indexCount * sizeof(uint16_t));
1192 }
1193
1194 uint32_t initialOffset = this->addDraw(DRAW_VERTICES, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001195 SkASSERT(initialOffset+getPaintOffset(DRAW_VERTICES, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 addPaint(paint);
1197 addInt(flags);
1198 addInt(vmode);
1199 addInt(vertexCount);
1200 addPoints(vertices, vertexCount);
1201 if (flags & DRAW_VERTICES_HAS_TEXS) {
1202 addPoints(texs, vertexCount);
1203 }
1204 if (flags & DRAW_VERTICES_HAS_COLORS) {
1205 fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
1206 }
1207 if (flags & DRAW_VERTICES_HAS_INDICES) {
1208 addInt(indexCount);
1209 fWriter.writePad(indices, indexCount * sizeof(uint16_t));
1210 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001211 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212}
1213
reed@android.comcb608442009-12-04 21:32:27 +00001214void SkPictureRecord::drawData(const void* data, size_t length) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001215 // op + length + 'length' worth of data
1216 uint32_t size = 2 * kUInt32Size + SkAlign4(length);
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +00001217 uint32_t initialOffset = this->addDraw(DRAW_DATA, &size);
reed@android.comcb608442009-12-04 21:32:27 +00001218 addInt(length);
1219 fWriter.writePad(data, length);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001220 validate(initialOffset, size);
reed@android.comcb608442009-12-04 21:32:27 +00001221}
1222
robertphillips@google.com0a4805e2013-05-29 13:24:23 +00001223void SkPictureRecord::beginCommentGroup(const char* description) {
1224 // op/size + length of string + \0 terminated chars
1225 int length = strlen(description);
1226 uint32_t size = 2 * kUInt32Size + SkAlign4(length + 1);
1227 uint32_t initialOffset = this->addDraw(BEGIN_COMMENT_GROUP, &size);
1228 fWriter.writeString(description, length);
1229 validate(initialOffset, size);
1230}
1231
1232void SkPictureRecord::addComment(const char* kywd, const char* value) {
1233 // op/size + 2x length of string + 2x \0 terminated chars
1234 int kywdLen = strlen(kywd);
1235 int valueLen = strlen(value);
1236 uint32_t size = 3 * kUInt32Size + SkAlign4(kywdLen + 1) + SkAlign4(valueLen + 1);
1237 uint32_t initialOffset = this->addDraw(COMMENT, &size);
1238 fWriter.writeString(kywd, kywdLen);
1239 fWriter.writeString(value, valueLen);
1240 validate(initialOffset, size);
1241}
1242
1243void SkPictureRecord::endCommentGroup() {
1244 // op/size
1245 uint32_t size = 1 * kUInt32Size;
1246 uint32_t initialOffset = this->addDraw(END_COMMENT_GROUP, &size);
1247 validate(initialOffset, size);
1248}
1249
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250///////////////////////////////////////////////////////////////////////////////
reed@google.com82065d62011-02-07 15:30:46 +00001251
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
scroggo@google.com4b90b112012-12-04 15:08:56 +00001253 const int index = fBitmapHeap->insert(bitmap);
1254 // In debug builds, a bad return value from insert() will crash, allowing for debugging. In
1255 // release builds, the invalid value will be recorded so that the reader will know that there
1256 // was a problem.
1257 SkASSERT(index != SkBitmapHeap::INVALID_SLOT);
1258 addInt(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259}
1260
1261void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
1262 addMatrixPtr(&matrix);
1263}
1264
1265void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
reed@google.com83ca3372012-07-12 15:27:54 +00001266 this->addInt(matrix ? fMatrices.find(*matrix) : 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267}
1268
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001269const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
1270 const SkFlatData* data = paint ? fPaints.findAndReturnFlat(*paint) : NULL;
1271 int index = data ? data->index() : 0;
reed@google.com45954262012-12-07 17:14:40 +00001272 this->addInt(index);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001273 return data;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274}
1275
1276void SkPictureRecord::addPath(const SkPath& path) {
1277 if (NULL == fPathHeap) {
1278 fPathHeap = SkNEW(SkPathHeap);
1279 }
1280 addInt(fPathHeap->append(path));
1281}
1282
1283void SkPictureRecord::addPicture(SkPicture& picture) {
1284 int index = fPictureRefs.find(&picture);
1285 if (index < 0) { // not found
1286 index = fPictureRefs.count();
1287 *fPictureRefs.append() = &picture;
1288 picture.ref();
1289 }
1290 // follow the convention of recording a 1-based index
1291 addInt(index + 1);
1292}
1293
1294void SkPictureRecord::addPoint(const SkPoint& point) {
1295#ifdef SK_DEBUG_SIZE
1296 size_t start = fWriter.size();
1297#endif
1298 fWriter.writePoint(point);
1299#ifdef SK_DEBUG_SIZE
1300 fPointBytes += fWriter.size() - start;
1301 fPointWrites++;
1302#endif
1303}
reed@google.com82065d62011-02-07 15:30:46 +00001304
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
1306 fWriter.writeMul4(pts, count * sizeof(SkPoint));
1307#ifdef SK_DEBUG_SIZE
1308 fPointBytes += count * sizeof(SkPoint);
1309 fPointWrites++;
1310#endif
1311}
1312
1313void SkPictureRecord::addRect(const SkRect& rect) {
1314#ifdef SK_DEBUG_SIZE
1315 size_t start = fWriter.size();
1316#endif
1317 fWriter.writeRect(rect);
1318#ifdef SK_DEBUG_SIZE
1319 fRectBytes += fWriter.size() - start;
1320 fRectWrites++;
1321#endif
1322}
1323
1324void SkPictureRecord::addRectPtr(const SkRect* rect) {
1325 if (fWriter.writeBool(rect != NULL)) {
1326 fWriter.writeRect(*rect);
1327 }
1328}
1329
reed@google.comf0b5e112011-09-07 11:57:34 +00001330void SkPictureRecord::addIRect(const SkIRect& rect) {
1331 fWriter.write(&rect, sizeof(rect));
1332}
1333
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
1335 if (fWriter.writeBool(rect != NULL)) {
1336 *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
1337 }
1338}
1339
reed@google.com4ed0fb72012-12-12 20:48:18 +00001340void SkPictureRecord::addRRect(const SkRRect& rrect) {
1341 fWriter.writeRRect(rrect);
1342}
1343
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344void SkPictureRecord::addRegion(const SkRegion& region) {
reed@google.com83ca3372012-07-12 15:27:54 +00001345 addInt(fRegions.find(region));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346}
1347
1348void SkPictureRecord::addText(const void* text, size_t byteLength) {
1349#ifdef SK_DEBUG_SIZE
1350 size_t start = fWriter.size();
1351#endif
1352 addInt(byteLength);
1353 fWriter.writePad(text, byteLength);
1354#ifdef SK_DEBUG_SIZE
1355 fTextBytes += fWriter.size() - start;
1356 fTextWrites++;
1357#endif
1358}
1359
1360///////////////////////////////////////////////////////////////////////////////
1361
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362#ifdef SK_DEBUG_SIZE
1363size_t SkPictureRecord::size() const {
1364 size_t result = 0;
1365 size_t sizeData;
1366 bitmaps(&sizeData);
1367 result += sizeData;
1368 matrices(&sizeData);
1369 result += sizeData;
1370 paints(&sizeData);
1371 result += sizeData;
1372 paths(&sizeData);
1373 result += sizeData;
1374 pictures(&sizeData);
1375 result += sizeData;
1376 regions(&sizeData);
1377 result += sizeData;
1378 result += streamlen();
1379 return result;
1380}
1381
1382int SkPictureRecord::bitmaps(size_t* size) const {
1383 size_t result = 0;
1384 int count = fBitmaps.count();
reed@google.com82065d62011-02-07 15:30:46 +00001385 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386 result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
1387 *size = result;
1388 return count;
1389}
1390
1391int SkPictureRecord::matrices(size_t* size) const {
1392 int count = fMatrices.count();
1393 *size = sizeof(fMatrices[0]) * count;
1394 return count;
1395}
1396
1397int SkPictureRecord::paints(size_t* size) const {
1398 size_t result = 0;
1399 int count = fPaints.count();
reed@google.com82065d62011-02-07 15:30:46 +00001400 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401 result += sizeof(fPaints[index]) + fPaints[index]->size();
1402 *size = result;
1403 return count;
1404}
1405
1406int SkPictureRecord::paths(size_t* size) const {
1407 size_t result = 0;
1408 int count = fPaths.count();
reed@google.com82065d62011-02-07 15:30:46 +00001409 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410 result += sizeof(fPaths[index]) + fPaths[index]->size();
1411 *size = result;
1412 return count;
1413}
1414
1415int SkPictureRecord::regions(size_t* size) const {
1416 size_t result = 0;
1417 int count = fRegions.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(fRegions[index]) + fRegions[index]->size();
1420 *size = result;
1421 return count;
1422}
1423
1424size_t SkPictureRecord::streamlen() const {
1425 return fWriter.size();
1426}
1427#endif
1428
1429#ifdef SK_DEBUG_VALIDATE
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001430void SkPictureRecord::validate(uint32_t initialOffset, uint32_t size) const {
1431 SkASSERT(fWriter.size() == initialOffset + size);
1432
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433 validateBitmaps();
1434 validateMatrices();
1435 validatePaints();
1436 validatePaths();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437 validateRegions();
1438}
1439
1440void SkPictureRecord::validateBitmaps() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001441 int count = fBitmapHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442 SkASSERT((unsigned) count < 0x1000);
1443 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001444 const SkBitmap* bitPtr = fBitmapHeap->getBitmap(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 SkASSERT(bitPtr);
1446 bitPtr->validate();
1447 }
1448}
1449
1450void SkPictureRecord::validateMatrices() const {
1451 int count = fMatrices.count();
1452 SkASSERT((unsigned) count < 0x1000);
1453 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001454 const SkFlatData* matrix = fMatrices[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 SkASSERT(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001456// matrix->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457 }
1458}
1459
1460void SkPictureRecord::validatePaints() const {
1461 int count = fPaints.count();
1462 SkASSERT((unsigned) count < 0x1000);
1463 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001464 const SkFlatData* paint = fPaints[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465 SkASSERT(paint);
1466// paint->validate();
1467 }
1468}
1469
1470void SkPictureRecord::validatePaths() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001471 if (NULL == fPathHeap) {
1472 return;
1473 }
1474
1475 int count = fPathHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476 SkASSERT((unsigned) count < 0x1000);
1477 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001478 const SkPath& path = (*fPathHeap)[index];
1479 path.validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480 }
1481}
1482
1483void SkPictureRecord::validateRegions() const {
1484 int count = fRegions.count();
1485 SkASSERT((unsigned) count < 0x1000);
1486 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001487 const SkFlatData* region = fRegions[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488 SkASSERT(region);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001489// region->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490 }
1491}
1492#endif