blob: 01478bb844828bae09214e457ed4dada66d251cd [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) {
mtklein@google.com330313a2013-08-22 15:37:26 +0000142 SkDEBUGFAIL("eeek, don't try to change the device on a recording canvas");
reed@google.comd86e7ab2012-09-27 20:31:31 +0000143 return this->INHERITED::setDevice(device);
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000144}
145
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146int SkPictureRecord::save(SaveFlags flags) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000147 // record the offset to us, making it non-positive to distinguish a save
148 // from a clip entry.
149 fRestoreOffsetStack.push(-(int32_t)fWriter.size());
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000150
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000151 // op + flags
djsollen@google.comd4236572013-08-13 14:29:06 +0000152 uint32_t size = kSaveSize;
robertphillips@google.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
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +0000404 const SkFlatData* data = paintDict->findAndReturnFlat(*dbmPaint);
405 if (NULL == data) {
406 return false;
407 }
robertphillips@google.come37ad352013-03-01 19:44:30 +0000408
409 // kill the saveLayer and alter the DBMR2R's paint to be the modified one
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000410 convert_command_to_noop(writer, saveLayerInfo.fOffset);
411 uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
412 SkASSERT(dbmPaintId == *ptr);
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +0000413 *ptr = data->index();
robertphillips@google.come37ad352013-03-01 19:44:30 +0000414 return true;
415}
416
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000417/*
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000418 * Restore has just been called (but not recorded), look back at the
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000419 * matching save* and see if we are in the configuration:
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000420 * SAVE_LAYER (with NULL == bounds)
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000421 * SAVE
422 * CLIP_RECT
423 * DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
424 * RESTORE
425 * RESTORE
426 * where the saveLayer's color can be moved into the drawBitmap*'s paint
427 */
428static bool remove_save_layer2(SkWriter32* writer, int32_t offset,
429 SkPaintDictionary* paintDict) {
430
431 // back up to the save block
432 // TODO: add a stack to track save*/restore offsets rather than searching backwards
433 while (offset > 0) {
434 offset = *writer->peek32(offset);
435 }
436
437 int pattern[] = { SAVE_LAYER, SAVE, CLIP_RECT, kDRAW_BITMAP_FLAVOR, RESTORE, /* RESTORE */ };
438 CommandInfo result[SK_ARRAY_COUNT(pattern)];
439
440 if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
441 return false;
442 }
443
444 if (kSaveLayerWithBoundsSize == result[0].fSize) {
445 // The saveLayer's bound can offset where the dbm is drawn
446 return false;
447 }
448
449 return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
450 result[0], result[3]);
451}
robertphillips@google.come37ad352013-03-01 19:44:30 +0000452
453/*
454 * Restore has just been called (but not recorded), so look back at the
reed@google.comffacd3c2012-08-30 15:31:23 +0000455 * matching save(), and see if we can eliminate the pair of them, due to no
456 * intervening matrix/clip calls.
457 *
458 * If so, update the writer and return true, in which case we won't even record
459 * the restore() call. If we still need the restore(), return false.
460 */
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000461static bool collapse_save_clip_restore(SkWriter32* writer, int32_t offset,
462 SkPaintDictionary* paintDict) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000463#ifdef TRACK_COLLAPSE_STATS
464 gCollapseCalls += 1;
465#endif
466
467 int32_t restoreOffset = (int32_t)writer->size();
468
469 // back up to the save block
470 while (offset > 0) {
471 offset = *writer->peek32(offset);
472 }
473
474 // now offset points to a save
475 offset = -offset;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000476 uint32_t opSize;
477 DrawType op = peek_op_and_size(writer, offset, &opSize);
478 if (SAVE_LAYER == op) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000479 // not ready to cull these out yet (mrr)
480 return false;
481 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000482 SkASSERT(SAVE == op);
djsollen@google.comd4236572013-08-13 14:29:06 +0000483 SkASSERT(kSaveSize == opSize);
484
485 // get the save flag (last 4-bytes of the space allocated for the opSize)
486 SkCanvas::SaveFlags saveFlags = (SkCanvas::SaveFlags) *writer->peek32(offset+4);
487 if (SkCanvas::kMatrixClip_SaveFlag != saveFlags) {
488 // This function's optimization is only correct for kMatrixClip style saves.
489 // TODO: set checkMatrix & checkClip booleans here and then check for the
490 // offending operations in the following loop.
491 return false;
492 }
reed@google.comffacd3c2012-08-30 15:31:23 +0000493
494 // Walk forward until we get back to either a draw-verb (abort) or we hit
495 // our restore (success).
496 int32_t saveOffset = offset;
497
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000498 offset += opSize;
reed@google.comffacd3c2012-08-30 15:31:23 +0000499 while (offset < restoreOffset) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000500 op = peek_op_and_size(writer, offset, &opSize);
501 if ((op > CONCAT && op < ROTATE) || (SAVE_LAYER == op)) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000502 // drawing verb, abort
503 return false;
504 }
505 offset += opSize;
506 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000507
reed@google.comffacd3c2012-08-30 15:31:23 +0000508#ifdef TRACK_COLLAPSE_STATS
509 gCollapseCount += 1;
510 SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls,
511 (double)gCollapseCount / gCollapseCalls, "%");
512#endif
513
514 writer->rewindToOffset(saveOffset);
515 return true;
516}
517
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000518typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
519 SkPaintDictionary* paintDict);
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000520enum PictureRecordOptType {
521 kRewind_OptType, // Optimization rewinds the command stream
522 kCollapseSaveLayer_OptType, // Optimization eliminates a save/restore pair
523};
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000524
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000525struct PictureRecordOpt {
526 PictureRecordOptProc fProc;
527 PictureRecordOptType fType;
528};
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000529/*
530 * A list of the optimizations that are tried upon seeing a restore
531 * TODO: add a real API for such optimizations
532 * Add the ability to fire optimizations on any op (not just RESTORE)
533 */
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000534static const PictureRecordOpt gPictureRecordOpts[] = {
535 { collapse_save_clip_restore, kRewind_OptType },
536 { remove_save_layer1, kCollapseSaveLayer_OptType },
537 { remove_save_layer2, kCollapseSaveLayer_OptType }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000538};
539
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000540// This is called after an optimization has been applied to the command stream
541// in order to adjust the contents and state of the bounding box hierarchy and
542// state tree to reflect the optimization.
543static void apply_optimization_to_bbh(PictureRecordOptType opt, SkPictureStateTree* stateTree,
544 SkBBoxHierarchy* boundingHierarchy) {
545 switch (opt) {
546 case kCollapseSaveLayer_OptType:
robertphillips@google.com35e15632013-03-15 16:49:34 +0000547 if (NULL != stateTree) {
548 stateTree->saveCollapsed();
549 }
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000550 break;
551 case kRewind_OptType:
552 if (NULL != boundingHierarchy) {
553 boundingHierarchy->rewindInserts();
554 }
555 // Note: No need to touch the state tree for this to work correctly.
556 // Unused branches do not burden the playback, and pruning the tree
557 // would be O(N^2), so it is best to leave it alone.
558 break;
559 default:
560 SkASSERT(0);
561 }
562}
563
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564void SkPictureRecord::restore() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000565 // FIXME: SkDeferredCanvas needs to be refactored to respect
566 // save/restore balancing so that the following test can be
567 // turned on permanently.
568#if 0
569 SkASSERT(fRestoreOffsetStack.count() > 1);
570#endif
571
reed@android.comb4e22d62009-07-09 15:20:25 +0000572 // check for underflow
573 if (fRestoreOffsetStack.count() == 0) {
574 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 }
reed@android.comb4e22d62009-07-09 15:20:25 +0000576
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000577 if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
578 fFirstSavedLayerIndex = kNoSavedLayerIndex;
579 }
580
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000581 uint32_t initialOffset, size;
robertphillips@google.com31d81912013-04-12 15:24:29 +0000582 size_t opt = 0;
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000583 if (!(fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag)) {
584 for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
585 if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
586 // Some optimization fired so don't add the RESTORE
587 size = 0;
588 initialOffset = fWriter.size();
589 apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
590 fStateTree, fBoundingHierarchy);
591 break;
592 }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000593 }
skia.committer@gmail.com4bb50b22013-04-13 07:01:15 +0000594 }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000595
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000596 if ((fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag) ||
597 SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000598 // No optimization fired so add the RESTORE
reed@google.comffacd3c2012-08-30 15:31:23 +0000599 fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000600 size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000601 initialOffset = this->addDraw(RESTORE, &size);
reed@google.comffacd3c2012-08-30 15:31:23 +0000602 }
603
reed@android.comb4e22d62009-07-09 15:20:25 +0000604 fRestoreOffsetStack.pop();
reed@android.com32a42492009-07-10 03:33:52 +0000605
robertphillips@google.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,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +0000916 const SkRect& dst, const SkPaint* paint,
917 DrawBitmapRectFlags flags) {
918 // id + paint index + bitmap index + bool for 'src' + flags
919 uint32_t size = 5 * kUInt32Size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000920 if (NULL != src) {
921 size += sizeof(*src); // + rect
922 }
923 size += sizeof(dst); // + rect
924
925 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_RECT_TO_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000926 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_RECT_TO_RECT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 addPaintPtr(paint);
928 addBitmap(bitmap);
reed@google.com71121732012-09-18 15:14:33 +0000929 addRectPtr(src); // may be null
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 addRect(dst);
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +0000931 addInt(flags);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000932 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933}
934
935void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
reed@google.comf0b5e112011-09-07 11:57:34 +0000936 const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000937 // id + paint index + bitmap index + matrix index
938 uint32_t size = 4 * kUInt32Size;
939 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000940 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_MATRIX, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 addPaintPtr(paint);
942 addBitmap(bitmap);
943 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000944 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945}
946
reed@google.comf0b5e112011-09-07 11:57:34 +0000947void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
948 const SkRect& dst, const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000949 // op + paint index + bitmap id + center + dst rect
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000950 uint32_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000951 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000952 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_NINE, size) == fWriter.size());
reed@google.comf0b5e112011-09-07 11:57:34 +0000953 addPaintPtr(paint);
954 addBitmap(bitmap);
955 addIRect(center);
956 addRect(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000957 validate(initialOffset, size);
reed@google.comf0b5e112011-09-07 11:57:34 +0000958}
959
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
961 const SkPaint* paint = NULL) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000962 // op + paint index + bitmap index + left + top
963 uint32_t size = 5 * kUInt32Size;
964 uint32_t initialOffset = this->addDraw(DRAW_SPRITE, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000965 SkASSERT(initialOffset+getPaintOffset(DRAW_SPRITE, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 addPaintPtr(paint);
967 addBitmap(bitmap);
968 addInt(left);
969 addInt(top);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000970 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971}
972
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +0000973void SkPictureRecord::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) {
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +0000987 WriteTopBot(paint, flat);
junov@chromium.org3f5ecd62013-01-22 18:01:26 +0000988 addScalar(flat.topBot()[0] + minY);
989 addScalar(flat.topBot()[1] + maxY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990}
991
reed@google.com82065d62011-02-07 15:30:46 +0000992void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 SkScalar y, const SkPaint& paint) {
reed@google.com2eb5bb12012-04-12 14:27:42 +0000994 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com82065d62011-02-07 15:30:46 +0000995
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000996 // op + paint index + length + 'length' worth of chars + x + y
997 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * sizeof(SkScalar);
998 if (fast) {
999 size += 2 * sizeof(SkScalar); // + top & bottom
1000 }
1001
robertphillips@google.come37ad352013-03-01 19:44:30 +00001002 DrawType op = fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT;
1003 uint32_t initialOffset = this->addDraw(op, &size);
1004 SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001005 const SkFlatData* flatPaintData = addPaint(paint);
1006 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007 addText(text, byteLength);
1008 addScalar(x);
1009 addScalar(y);
1010 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001011 addFontMetricsTopBottom(paint, *flatPaintData, y, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001013 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014}
1015
reed@google.com82065d62011-02-07 15:30:46 +00001016void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017 const SkPoint pos[], const SkPaint& paint) {
1018 size_t points = paint.countText(text, byteLength);
1019 if (0 == points)
1020 return;
1021
1022 bool canUseDrawH = true;
reed@google.com9efd9a02012-01-30 15:41:43 +00001023 SkScalar minY = pos[0].fY;
1024 SkScalar maxY = pos[0].fY;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 // check if the caller really should have used drawPosTextH()
1026 {
1027 const SkScalar firstY = pos[0].fY;
1028 for (size_t index = 1; index < points; index++) {
1029 if (pos[index].fY != firstY) {
1030 canUseDrawH = false;
reed@google.com9efd9a02012-01-30 15:41:43 +00001031 if (pos[index].fY < minY) {
1032 minY = pos[index].fY;
1033 } else if (pos[index].fY > maxY) {
1034 maxY = pos[index].fY;
1035 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036 }
1037 }
1038 }
reed@google.com82065d62011-02-07 15:30:46 +00001039
reed@google.com2eb5bb12012-04-12 14:27:42 +00001040 bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com9efd9a02012-01-30 15:41:43 +00001041 bool fast = canUseDrawH && fastBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001042
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001043 // op + paint index + length + 'length' worth of data + num points
1044 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1045 if (canUseDrawH) {
1046 if (fast) {
1047 size += 2 * sizeof(SkScalar); // + top & bottom
1048 }
1049 // + y-pos + actual x-point data
1050 size += sizeof(SkScalar) + points * sizeof(SkScalar);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 } else {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001052 // + x&y point data
1053 size += points * sizeof(SkPoint);
1054 if (fastBounds) {
1055 size += 2 * sizeof(SkScalar); // + top & bottom
1056 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001058
1059 DrawType op;
1060 if (fast) {
1061 op = DRAW_POS_TEXT_H_TOP_BOTTOM;
1062 } else if (canUseDrawH) {
1063 op = DRAW_POS_TEXT_H;
1064 } else if (fastBounds) {
1065 op = DRAW_POS_TEXT_TOP_BOTTOM;
1066 } else {
1067 op = DRAW_POS_TEXT;
1068 }
1069 uint32_t initialOffset = this->addDraw(op, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001070 SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001071 const SkFlatData* flatPaintData = addPaint(paint);
1072 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 addText(text, byteLength);
1074 addInt(points);
1075
1076#ifdef SK_DEBUG_SIZE
1077 size_t start = fWriter.size();
1078#endif
1079 if (canUseDrawH) {
1080 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001081 addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082 }
1083 addScalar(pos[0].fY);
1084 SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
reed@google.com82065d62011-02-07 15:30:46 +00001085 for (size_t index = 0; index < points; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 *xptr++ = pos[index].fX;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001087 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 fWriter.writeMul4(pos, points * sizeof(SkPoint));
reed@google.com9efd9a02012-01-30 15:41:43 +00001089 if (fastBounds) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001090 addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY);
reed@google.com9efd9a02012-01-30 15:41:43 +00001091 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 }
1093#ifdef SK_DEBUG_SIZE
1094 fPointBytes += fWriter.size() - start;
1095 fPointWrites += points;
1096#endif
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001097 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098}
1099
1100void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
1101 const SkScalar xpos[], SkScalar constY,
1102 const SkPaint& paint) {
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +00001103
1104 const SkFlatData* flatPaintData = this->getFlatPaintData(paint);
1105 drawPosTextHImpl(text, byteLength, xpos, constY, paint, flatPaintData);
1106}
1107
1108void SkPictureRecord::drawPosTextHImpl(const void* text, size_t byteLength,
1109 const SkScalar xpos[], SkScalar constY,
1110 const SkPaint& paint, const SkFlatData* flatPaintData) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 size_t points = paint.countText(text, byteLength);
1112 if (0 == points)
1113 return;
reed@google.com82065d62011-02-07 15:30:46 +00001114
reed@google.com2eb5bb12012-04-12 14:27:42 +00001115 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001116
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001117 // op + paint index + length + 'length' worth of data + num points
1118 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1119 if (fast) {
1120 size += 2 * sizeof(SkScalar); // + top & bottom
1121 }
1122 // + y + the actual points
1123 size += 1 * kUInt32Size + points * sizeof(SkScalar);
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +00001124 uint32_t initialOffset = this->addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H,
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001125 &size);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001126 SkASSERT(flatPaintData);
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +00001127 addFlatPaint(flatPaintData);
1128
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 addText(text, byteLength);
1130 addInt(points);
reed@google.com82065d62011-02-07 15:30:46 +00001131
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132#ifdef SK_DEBUG_SIZE
1133 size_t start = fWriter.size();
1134#endif
1135 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001136 addFontMetricsTopBottom(paint, *flatPaintData, constY, constY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 }
1138 addScalar(constY);
1139 fWriter.writeMul4(xpos, points * sizeof(SkScalar));
1140#ifdef SK_DEBUG_SIZE
1141 fPointBytes += fWriter.size() - start;
1142 fPointWrites += points;
1143#endif
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001144 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145}
1146
reed@google.com82065d62011-02-07 15:30:46 +00001147void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength,
1148 const SkPath& path, const SkMatrix* matrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001150 // op + paint index + length + 'length' worth of data + path index + matrix index
1151 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * kUInt32Size;
1152 uint32_t initialOffset = this->addDraw(DRAW_TEXT_ON_PATH, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001153 SkASSERT(initialOffset+getPaintOffset(DRAW_TEXT_ON_PATH, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154 addPaint(paint);
1155 addText(text, byteLength);
1156 addPath(path);
1157 addMatrixPtr(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001158 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001159}
1160
1161void SkPictureRecord::drawPicture(SkPicture& picture) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001162 // op + picture index
1163 uint32_t size = 2 * kUInt32Size;
1164 uint32_t initialOffset = this->addDraw(DRAW_PICTURE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 addPicture(picture);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001166 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167}
1168
1169void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
1170 const SkPoint vertices[], const SkPoint texs[],
1171 const SkColor colors[], SkXfermode*,
1172 const uint16_t indices[], int indexCount,
1173 const SkPaint& paint) {
1174 uint32_t flags = 0;
1175 if (texs) {
1176 flags |= DRAW_VERTICES_HAS_TEXS;
1177 }
1178 if (colors) {
1179 flags |= DRAW_VERTICES_HAS_COLORS;
1180 }
1181 if (indexCount > 0) {
1182 flags |= DRAW_VERTICES_HAS_INDICES;
1183 }
1184
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001185 // op + paint index + flags + vmode + vCount + vertices
1186 uint32_t size = 5 * kUInt32Size + vertexCount * sizeof(SkPoint);
1187 if (flags & DRAW_VERTICES_HAS_TEXS) {
1188 size += vertexCount * sizeof(SkPoint); // + uvs
1189 }
1190 if (flags & DRAW_VERTICES_HAS_COLORS) {
1191 size += vertexCount * sizeof(SkColor); // + vert colors
1192 }
1193 if (flags & DRAW_VERTICES_HAS_INDICES) {
1194 // + num indices + indices
1195 size += 1 * kUInt32Size + SkAlign4(indexCount * sizeof(uint16_t));
1196 }
1197
1198 uint32_t initialOffset = this->addDraw(DRAW_VERTICES, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001199 SkASSERT(initialOffset+getPaintOffset(DRAW_VERTICES, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 addPaint(paint);
1201 addInt(flags);
1202 addInt(vmode);
1203 addInt(vertexCount);
1204 addPoints(vertices, vertexCount);
1205 if (flags & DRAW_VERTICES_HAS_TEXS) {
1206 addPoints(texs, vertexCount);
1207 }
1208 if (flags & DRAW_VERTICES_HAS_COLORS) {
1209 fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
1210 }
1211 if (flags & DRAW_VERTICES_HAS_INDICES) {
1212 addInt(indexCount);
1213 fWriter.writePad(indices, indexCount * sizeof(uint16_t));
1214 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001215 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001216}
1217
reed@android.comcb608442009-12-04 21:32:27 +00001218void SkPictureRecord::drawData(const void* data, size_t length) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001219 // op + length + 'length' worth of data
1220 uint32_t size = 2 * kUInt32Size + SkAlign4(length);
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +00001221 uint32_t initialOffset = this->addDraw(DRAW_DATA, &size);
reed@android.comcb608442009-12-04 21:32:27 +00001222 addInt(length);
1223 fWriter.writePad(data, length);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001224 validate(initialOffset, size);
reed@android.comcb608442009-12-04 21:32:27 +00001225}
1226
robertphillips@google.com0a4805e2013-05-29 13:24:23 +00001227void SkPictureRecord::beginCommentGroup(const char* description) {
1228 // op/size + length of string + \0 terminated chars
1229 int length = strlen(description);
1230 uint32_t size = 2 * kUInt32Size + SkAlign4(length + 1);
1231 uint32_t initialOffset = this->addDraw(BEGIN_COMMENT_GROUP, &size);
1232 fWriter.writeString(description, length);
1233 validate(initialOffset, size);
1234}
1235
1236void SkPictureRecord::addComment(const char* kywd, const char* value) {
1237 // op/size + 2x length of string + 2x \0 terminated chars
1238 int kywdLen = strlen(kywd);
1239 int valueLen = strlen(value);
1240 uint32_t size = 3 * kUInt32Size + SkAlign4(kywdLen + 1) + SkAlign4(valueLen + 1);
1241 uint32_t initialOffset = this->addDraw(COMMENT, &size);
1242 fWriter.writeString(kywd, kywdLen);
1243 fWriter.writeString(value, valueLen);
1244 validate(initialOffset, size);
1245}
1246
1247void SkPictureRecord::endCommentGroup() {
1248 // op/size
1249 uint32_t size = 1 * kUInt32Size;
1250 uint32_t initialOffset = this->addDraw(END_COMMENT_GROUP, &size);
1251 validate(initialOffset, size);
1252}
1253
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254///////////////////////////////////////////////////////////////////////////////
reed@google.com82065d62011-02-07 15:30:46 +00001255
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
scroggo@google.com4b90b112012-12-04 15:08:56 +00001257 const int index = fBitmapHeap->insert(bitmap);
1258 // In debug builds, a bad return value from insert() will crash, allowing for debugging. In
1259 // release builds, the invalid value will be recorded so that the reader will know that there
1260 // was a problem.
1261 SkASSERT(index != SkBitmapHeap::INVALID_SLOT);
1262 addInt(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263}
1264
1265void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
1266 addMatrixPtr(&matrix);
1267}
1268
1269void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
reed@google.com83ca3372012-07-12 15:27:54 +00001270 this->addInt(matrix ? fMatrices.find(*matrix) : 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271}
1272
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +00001273const SkFlatData* SkPictureRecord::getFlatPaintData(const SkPaint& paint) {
1274 return fPaints.findAndReturnFlat(paint);
1275}
1276
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001277const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +00001278 const SkFlatData* data = paint ? getFlatPaintData(*paint) : NULL;
1279 this->addFlatPaint(data);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001280 return data;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281}
1282
commit-bot@chromium.orgcf7be952013-08-22 17:19:52 +00001283void SkPictureRecord::addFlatPaint(const SkFlatData* flatPaint) {
1284 int index = flatPaint ? flatPaint->index() : 0;
1285 this->addInt(index);
1286}
1287
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288void SkPictureRecord::addPath(const SkPath& path) {
1289 if (NULL == fPathHeap) {
1290 fPathHeap = SkNEW(SkPathHeap);
1291 }
1292 addInt(fPathHeap->append(path));
1293}
1294
1295void SkPictureRecord::addPicture(SkPicture& picture) {
1296 int index = fPictureRefs.find(&picture);
1297 if (index < 0) { // not found
1298 index = fPictureRefs.count();
1299 *fPictureRefs.append() = &picture;
1300 picture.ref();
1301 }
1302 // follow the convention of recording a 1-based index
1303 addInt(index + 1);
1304}
1305
1306void SkPictureRecord::addPoint(const SkPoint& point) {
1307#ifdef SK_DEBUG_SIZE
1308 size_t start = fWriter.size();
1309#endif
1310 fWriter.writePoint(point);
1311#ifdef SK_DEBUG_SIZE
1312 fPointBytes += fWriter.size() - start;
1313 fPointWrites++;
1314#endif
1315}
reed@google.com82065d62011-02-07 15:30:46 +00001316
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
1318 fWriter.writeMul4(pts, count * sizeof(SkPoint));
1319#ifdef SK_DEBUG_SIZE
1320 fPointBytes += count * sizeof(SkPoint);
1321 fPointWrites++;
1322#endif
1323}
1324
1325void SkPictureRecord::addRect(const SkRect& rect) {
1326#ifdef SK_DEBUG_SIZE
1327 size_t start = fWriter.size();
1328#endif
1329 fWriter.writeRect(rect);
1330#ifdef SK_DEBUG_SIZE
1331 fRectBytes += fWriter.size() - start;
1332 fRectWrites++;
1333#endif
1334}
1335
1336void SkPictureRecord::addRectPtr(const SkRect* rect) {
1337 if (fWriter.writeBool(rect != NULL)) {
1338 fWriter.writeRect(*rect);
1339 }
1340}
1341
reed@google.comf0b5e112011-09-07 11:57:34 +00001342void SkPictureRecord::addIRect(const SkIRect& rect) {
1343 fWriter.write(&rect, sizeof(rect));
1344}
1345
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
1347 if (fWriter.writeBool(rect != NULL)) {
1348 *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
1349 }
1350}
1351
reed@google.com4ed0fb72012-12-12 20:48:18 +00001352void SkPictureRecord::addRRect(const SkRRect& rrect) {
1353 fWriter.writeRRect(rrect);
1354}
1355
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356void SkPictureRecord::addRegion(const SkRegion& region) {
reed@google.com83ca3372012-07-12 15:27:54 +00001357 addInt(fRegions.find(region));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358}
1359
1360void SkPictureRecord::addText(const void* text, size_t byteLength) {
1361#ifdef SK_DEBUG_SIZE
1362 size_t start = fWriter.size();
1363#endif
1364 addInt(byteLength);
1365 fWriter.writePad(text, byteLength);
1366#ifdef SK_DEBUG_SIZE
1367 fTextBytes += fWriter.size() - start;
1368 fTextWrites++;
1369#endif
1370}
1371
1372///////////////////////////////////////////////////////////////////////////////
1373
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374#ifdef SK_DEBUG_SIZE
1375size_t SkPictureRecord::size() const {
1376 size_t result = 0;
1377 size_t sizeData;
1378 bitmaps(&sizeData);
1379 result += sizeData;
1380 matrices(&sizeData);
1381 result += sizeData;
1382 paints(&sizeData);
1383 result += sizeData;
1384 paths(&sizeData);
1385 result += sizeData;
1386 pictures(&sizeData);
1387 result += sizeData;
1388 regions(&sizeData);
1389 result += sizeData;
1390 result += streamlen();
1391 return result;
1392}
1393
1394int SkPictureRecord::bitmaps(size_t* size) const {
1395 size_t result = 0;
1396 int count = fBitmaps.count();
reed@google.com82065d62011-02-07 15:30:46 +00001397 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398 result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
1399 *size = result;
1400 return count;
1401}
1402
1403int SkPictureRecord::matrices(size_t* size) const {
1404 int count = fMatrices.count();
1405 *size = sizeof(fMatrices[0]) * count;
1406 return count;
1407}
1408
1409int SkPictureRecord::paints(size_t* size) const {
1410 size_t result = 0;
1411 int count = fPaints.count();
reed@google.com82065d62011-02-07 15:30:46 +00001412 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413 result += sizeof(fPaints[index]) + fPaints[index]->size();
1414 *size = result;
1415 return count;
1416}
1417
1418int SkPictureRecord::paths(size_t* size) const {
1419 size_t result = 0;
1420 int count = fPaths.count();
reed@google.com82065d62011-02-07 15:30:46 +00001421 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 result += sizeof(fPaths[index]) + fPaths[index]->size();
1423 *size = result;
1424 return count;
1425}
1426
1427int SkPictureRecord::regions(size_t* size) const {
1428 size_t result = 0;
1429 int count = fRegions.count();
reed@google.com82065d62011-02-07 15:30:46 +00001430 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 result += sizeof(fRegions[index]) + fRegions[index]->size();
1432 *size = result;
1433 return count;
1434}
1435
1436size_t SkPictureRecord::streamlen() const {
1437 return fWriter.size();
1438}
1439#endif
1440
1441#ifdef SK_DEBUG_VALIDATE
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001442void SkPictureRecord::validate(uint32_t initialOffset, uint32_t size) const {
1443 SkASSERT(fWriter.size() == initialOffset + size);
1444
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 validateBitmaps();
1446 validateMatrices();
1447 validatePaints();
1448 validatePaths();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 validateRegions();
1450}
1451
1452void SkPictureRecord::validateBitmaps() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001453 int count = fBitmapHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 SkASSERT((unsigned) count < 0x1000);
1455 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001456 const SkBitmap* bitPtr = fBitmapHeap->getBitmap(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457 SkASSERT(bitPtr);
1458 bitPtr->validate();
1459 }
1460}
1461
1462void SkPictureRecord::validateMatrices() const {
1463 int count = fMatrices.count();
1464 SkASSERT((unsigned) count < 0x1000);
1465 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001466 const SkFlatData* matrix = fMatrices[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467 SkASSERT(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001468// matrix->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 }
1470}
1471
1472void SkPictureRecord::validatePaints() const {
1473 int count = fPaints.count();
1474 SkASSERT((unsigned) count < 0x1000);
1475 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001476 const SkFlatData* paint = fPaints[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477 SkASSERT(paint);
1478// paint->validate();
1479 }
1480}
1481
1482void SkPictureRecord::validatePaths() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001483 if (NULL == fPathHeap) {
1484 return;
1485 }
1486
1487 int count = fPathHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488 SkASSERT((unsigned) count < 0x1000);
1489 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001490 const SkPath& path = (*fPathHeap)[index];
1491 path.validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001492 }
1493}
1494
1495void SkPictureRecord::validateRegions() const {
1496 int count = fRegions.count();
1497 SkASSERT((unsigned) count < 0x1000);
1498 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001499 const SkFlatData* region = fRegions[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500 SkASSERT(region);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001501// region->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001502 }
1503}
1504#endif