blob: 38bbe33be56012621fda8c3dabc11b58a0f77843 [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"
13#include "SkPictureStateTree.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014
15#define MIN_WRITER_SIZE 16384
16#define HEAP_BLOCK_SIZE 4096
17
junov@chromium.org4e6dfa52012-07-16 14:04:59 +000018enum {
reed@google.comd86e7ab2012-09-27 20:31:31 +000019 // just need a value that save or getSaveCount would never return
junov@chromium.org4e6dfa52012-07-16 14:04:59 +000020 kNoInitialSave = -1,
21};
22
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +000023// A lot of basic types get stored as a uint32_t: bools, ints, paint indices, etc.
24static int const kUInt32Size = 4;
25
robertphillips@google.come37ad352013-03-01 19:44:30 +000026static const uint32_t kSaveLayerNoBoundsSize = 4 * kUInt32Size;
27static const uint32_t kSaveLayerWithBoundsSize = 4 * kUInt32Size + sizeof(SkRect);
28
reed@google.comd86e7ab2012-09-27 20:31:31 +000029SkPictureRecord::SkPictureRecord(uint32_t flags, SkDevice* device) :
30 INHERITED(device),
robertphillips@google.com178a2672012-09-13 13:25:30 +000031 fBoundingHierarchy(NULL),
32 fStateTree(NULL),
djsollen@google.com21830d92012-08-07 19:49:41 +000033 fFlattenableHeap(HEAP_BLOCK_SIZE),
34 fMatrices(&fFlattenableHeap),
35 fPaints(&fFlattenableHeap),
36 fRegions(&fFlattenableHeap),
djsollen@google.comd2700ee2012-05-30 16:54:13 +000037 fWriter(MIN_WRITER_SIZE),
38 fRecordFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000039#ifdef SK_DEBUG_SIZE
40 fPointBytes = fRectBytes = fTextBytes = 0;
41 fPointWrites = fRectWrites = fTextWrites = 0;
42#endif
43
44 fRestoreOffsetStack.setReserve(32);
reed@google.com82065d62011-02-07 15:30:46 +000045
djsollen@google.comc9ab9872012-08-29 18:52:07 +000046 fBitmapHeap = SkNEW(SkBitmapHeap);
47 fFlattenableHeap.setBitmapStorage(fBitmapHeap);
reed@android.com8a1c16f2008-12-17 15:59:43 +000048 fPathHeap = NULL; // lazy allocate
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +000049 fFirstSavedLayerIndex = kNoSavedLayerIndex;
reed@google.comd86e7ab2012-09-27 20:31:31 +000050
51 fInitialSaveCount = kNoInitialSave;
reed@android.com8a1c16f2008-12-17 15:59:43 +000052}
53
54SkPictureRecord::~SkPictureRecord() {
djsollen@google.comc9ab9872012-08-29 18:52:07 +000055 SkSafeUnref(fBitmapHeap);
djsollen@google.com21830d92012-08-07 19:49:41 +000056 SkSafeUnref(fPathHeap);
rileya@google.com9f5898d2012-09-11 20:21:44 +000057 SkSafeUnref(fBoundingHierarchy);
58 SkSafeUnref(fStateTree);
djsollen@google.com21830d92012-08-07 19:49:41 +000059 fFlattenableHeap.setBitmapStorage(NULL);
60 fPictureRefs.unrefAll();
reed@android.com8a1c16f2008-12-17 15:59:43 +000061}
62
63///////////////////////////////////////////////////////////////////////////////
64
robertphillips@google.come37ad352013-03-01 19:44:30 +000065// Return the offset of the paint inside a given op's byte stream. A zero
66// return value means there is no paint (and you really shouldn't be calling
67// this method)
68static inline uint32_t getPaintOffset(DrawType op, uint32_t opSize) {
69 // These offsets are where the paint would be if the op size doesn't overflow
skia.committer@gmail.comf140f182013-03-02 07:01:56 +000070 static const uint8_t gPaintOffsets[LAST_DRAWTYPE_ENUM + 1] = {
robertphillips@google.come37ad352013-03-01 19:44:30 +000071 0, // UNUSED - no paint
72 0, // CLIP_PATH - no paint
73 0, // CLIP_REGION - no paint
74 0, // CLIP_RECT - no paint
75 0, // CLIP_RRECT - no paint
76 0, // CONCAT - no paint
77 1, // DRAW_BITMAP - right after op code
78 1, // DRAW_BITMAP_MATRIX - right after op code
79 1, // DRAW_BITMAP_NINE - right after op code
80 1, // DRAW_BITMAP_RECT_TO_RECT - right after op code
81 0, // DRAW_CLEAR - no paint
82 0, // DRAW_DATA - no paint
83 1, // DRAW_OVAL - right after op code
84 1, // DRAW_PAINT - right after op code
85 1, // DRAW_PATH - right after op code
86 0, // DRAW_PICTURE - no paint
87 1, // DRAW_POINTS - right after op code
88 1, // DRAW_POS_TEXT - right after op code
89 1, // DRAW_POS_TEXT_TOP_BOTTOM - right after op code
90 1, // DRAW_POS_TEXT_H - right after op code
91 1, // DRAW_POS_TEXT_H_TOP_BOTTOM - right after op code
92 1, // DRAW_RECT - right after op code
93 1, // DRAW_RRECT - right after op code
94 1, // DRAW_SPRITE - right after op code
95 1, // DRAW_TEXT - right after op code
96 1, // DRAW_TEXT_ON_PATH - right after op code
97 1, // DRAW_TEXT_TOP_BOTTOM - right after op code
98 1, // DRAW_VERTICES - right after op code
99 0, // RESTORE - no paint
100 0, // ROTATE - no paint
101 0, // SAVE - no paint
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000102 0, // SAVE_LAYER - see below - this paint's location varies
robertphillips@google.come37ad352013-03-01 19:44:30 +0000103 0, // SCALE - no paint
104 0, // SET_MATRIX - no paint
105 0, // SKEW - no paint
106 0, // TRANSLATE - no paint
107 0, // NOOP - no paint
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000108 };
robertphillips@google.come37ad352013-03-01 19:44:30 +0000109
110 SkASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1);
111 SkASSERT((unsigned)op <= (unsigned)LAST_DRAWTYPE_ENUM);
112
113 int overflow = 0;
114 if (0 != (opSize & ~MASK_24) || opSize == MASK_24) {
115 // This op's size overflows so an extra uint32_t will be written
116 // after the op code
117 overflow = sizeof(uint32_t);
118 }
119
120 if (SAVE_LAYER == op) {
121 static const uint32_t kSaveLayerNoBoundsPaintOffset = 2 * kUInt32Size;
122 static const uint32_t kSaveLayerWithBoundsPaintOffset = 2 * kUInt32Size + sizeof(SkRect);
123
124 if (kSaveLayerNoBoundsSize == opSize) {
125 return kSaveLayerNoBoundsPaintOffset + overflow;
126 } else {
127 SkASSERT(kSaveLayerWithBoundsSize == opSize);
128 return kSaveLayerWithBoundsPaintOffset + overflow;
129 }
130 }
131
132 SkASSERT(0 != gPaintOffsets[op]); // really shouldn't be calling this method
133 return gPaintOffsets[op] * sizeof(uint32_t) + overflow;
134}
135
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000136SkDevice* SkPictureRecord::setDevice(SkDevice* device) {
reed@google.comd86e7ab2012-09-27 20:31:31 +0000137 SkASSERT(!"eeek, don't try to change the device on a recording canvas");
138 return this->INHERITED::setDevice(device);
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000139}
140
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141int SkPictureRecord::save(SaveFlags flags) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000142 // record the offset to us, making it non-positive to distinguish a save
143 // from a clip entry.
144 fRestoreOffsetStack.push(-(int32_t)fWriter.size());
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000145
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000146 // op + flags
147 uint32_t size = 2 * kUInt32Size;
148 uint32_t initialOffset = this->addDraw(SAVE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149 addInt(flags);
reed@google.com82065d62011-02-07 15:30:46 +0000150
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000151 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 return this->INHERITED::save(flags);
153}
154
155int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
156 SaveFlags flags) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000157 // record the offset to us, making it non-positive to distinguish a save
158 // from a clip entry.
159 fRestoreOffsetStack.push(-(int32_t)fWriter.size());
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000160
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000161 // op + bool for 'bounds'
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000162 uint32_t size = 2 * kUInt32Size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000163 if (NULL != bounds) {
164 size += sizeof(*bounds); // + rect
165 }
166 // + paint index + flags
167 size += 2 * kUInt32Size;
168
robertphillips@google.come37ad352013-03-01 19:44:30 +0000169 SkASSERT(kSaveLayerNoBoundsSize == size || kSaveLayerWithBoundsSize == size);
170
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000171 uint32_t initialOffset = this->addDraw(SAVE_LAYER, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172 addRectPtr(bounds);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000173 SkASSERT(initialOffset+getPaintOffset(SAVE_LAYER, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 addPaintPtr(paint);
175 addInt(flags);
176
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000177 if (kNoSavedLayerIndex == fFirstSavedLayerIndex) {
178 fFirstSavedLayerIndex = fRestoreOffsetStack.count();
179 }
180
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000181 validate(initialOffset, size);
reed@android.com261ae4d2009-10-02 16:37:46 +0000182 /* Don't actually call saveLayer, because that will try to allocate an
183 offscreen device (potentially very big) which we don't actually need
184 at this time (and may not be able to afford since during record our
185 clip starts out the size of the picture, which is often much larger
186 than the size of the actual device we'll use during playback).
187 */
junov@chromium.orga907ac32012-02-24 21:54:07 +0000188 int count = this->INHERITED::save(flags);
189 this->clipRectBounds(bounds, flags, NULL);
190 return count;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191}
192
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000193bool SkPictureRecord::isDrawingToLayer() const {
194 return fFirstSavedLayerIndex != kNoSavedLayerIndex;
195}
196
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000197/*
198 * Read the op code from 'offset' in 'writer' and extract the size too.
199 */
200static DrawType peek_op_and_size(SkWriter32* writer, int32_t offset, uint32_t* size) {
201 uint32_t* peek = writer->peek32(offset);
reed@google.comffacd3c2012-08-30 15:31:23 +0000202
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000203 uint32_t op;
204 UNPACK_8_24(*peek, op, *size);
205 if (MASK_24 == *size) {
206 // size required its own slot right after the op code
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000207 *size = *writer->peek32(offset+kUInt32Size);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000208 }
209 return (DrawType) op;
reed@google.comffacd3c2012-08-30 15:31:23 +0000210}
211
212#ifdef TRACK_COLLAPSE_STATS
213 static int gCollapseCount, gCollapseCalls;
214#endif
215
robertphillips@google.come37ad352013-03-01 19:44:30 +0000216// Is the supplied paint simply a color?
217static bool is_simple(const SkPaint& p) {
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000218 intptr_t orAccum = (intptr_t)p.getPathEffect() |
robertphillips@google.come37ad352013-03-01 19:44:30 +0000219 (intptr_t)p.getShader() |
220 (intptr_t)p.getXfermode() |
221 (intptr_t)p.getMaskFilter() |
222 (intptr_t)p.getColorFilter() |
223 (intptr_t)p.getRasterizer() |
224 (intptr_t)p.getLooper() |
225 (intptr_t)p.getImageFilter();
226 return 0 == orAccum;
227}
228
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000229// CommandInfos are fed to the 'match' method and filled in with command
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000230// information.
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000231struct CommandInfo {
232 DrawType fActualOp;
233 uint32_t fOffset;
234 uint32_t fSize;
235};
236
reed@google.comffacd3c2012-08-30 15:31:23 +0000237/*
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000238 * Attempt to match the provided pattern of commands starting at 'offset'
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000239 * in the byte stream and stopping at the end of the stream. Upon success,
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000240 * return true with all the pattern information filled out in the result
241 * array (i.e., actual ops, offsets and sizes).
242 * Note this method skips any NOOPs seen in the stream
243 */
244static bool match(SkWriter32* writer, uint32_t offset,
245 int* pattern, CommandInfo* result, int numCommands) {
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000246 SkASSERT(offset < writer->size());
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000247
248 uint32_t curOffset = offset;
249 uint32_t curSize = 0;
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000250 int numMatched;
251 for (numMatched = 0; numMatched < numCommands && curOffset < writer->size(); ++numMatched) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000252 DrawType op = peek_op_and_size(writer, curOffset, &curSize);
253 while (NOOP == op && curOffset < writer->size()) {
254 curOffset += curSize;
255 op = peek_op_and_size(writer, curOffset, &curSize);
256 }
257
258 if (curOffset >= writer->size()) {
259 return false; // ran out of byte stream
260 }
261
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000262 if (kDRAW_BITMAP_FLAVOR == pattern[numMatched]) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000263 if (DRAW_BITMAP != op && DRAW_BITMAP_MATRIX != op &&
264 DRAW_BITMAP_NINE != op && DRAW_BITMAP_RECT_TO_RECT != op) {
265 return false;
266 }
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000267 } else if (op != pattern[numMatched]) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000268 return false;
269 }
270
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000271 result[numMatched].fActualOp = op;
272 result[numMatched].fOffset = curOffset;
273 result[numMatched].fSize = curSize;
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000274
275 curOffset += curSize;
276 }
277
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000278 if (numMatched != numCommands) {
279 return false;
280 }
281
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000282 curOffset += curSize;
283 if (curOffset < writer->size()) {
284 // Something else between the last command and the end of the stream
285 return false;
286 }
287
288 return true;
289}
290
291// temporarily here to make code review easier
292static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
293 SkPaintDictionary* paintDict,
294 const CommandInfo& saveLayerInfo,
295 const CommandInfo& dbmInfo);
296
297/*
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000298 * Restore has just been called (but not recorded), look back at the
robertphillips@google.come37ad352013-03-01 19:44:30 +0000299 * matching save* and see if we are in the configuration:
300 * SAVE_LAYER
301 * DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
302 * RESTORE
303 * where the saveLayer's color can be moved into the drawBitmap*'s paint
304 */
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000305static bool remove_save_layer1(SkWriter32* writer, int32_t offset,
robertphillips@google.come37ad352013-03-01 19:44:30 +0000306 SkPaintDictionary* paintDict) {
robertphillips@google.come37ad352013-03-01 19:44:30 +0000307 // back up to the save block
308 // TODO: add a stack to track save*/restore offsets rather than searching backwards
309 while (offset > 0) {
310 offset = *writer->peek32(offset);
311 }
312
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000313 int pattern[] = { SAVE_LAYER, kDRAW_BITMAP_FLAVOR, /* RESTORE */ };
314 CommandInfo result[SK_ARRAY_COUNT(pattern)];
315
316 if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
317 return false;
robertphillips@google.come37ad352013-03-01 19:44:30 +0000318 }
319
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000320 if (kSaveLayerWithBoundsSize == result[0].fSize) {
robertphillips@google.come37ad352013-03-01 19:44:30 +0000321 // The saveLayer's bound can offset where the dbm is drawn
322 return false;
323 }
324
robertphillips@google.come37ad352013-03-01 19:44:30 +0000325
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000326 return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
327 result[0], result[1]);
328}
robertphillips@google.come37ad352013-03-01 19:44:30 +0000329
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000330/*
331 * Convert the command code located at 'offset' to a NOOP. Leave the size
332 * field alone so the NOOP can be skipped later.
333 */
334static void convert_command_to_noop(SkWriter32* writer, uint32_t offset) {
335 uint32_t* ptr = writer->peek32(offset);
336 *ptr = (*ptr & MASK_24) | (NOOP << 24);
337}
robertphillips@google.come37ad352013-03-01 19:44:30 +0000338
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000339/*
340 * Attempt to merge the saveLayer's paint into the drawBitmap*'s paint.
341 * Return true on success; false otherwise.
342 */
343static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
344 SkPaintDictionary* paintDict,
345 const CommandInfo& saveLayerInfo,
346 const CommandInfo& dbmInfo) {
347 SkASSERT(SAVE_LAYER == saveLayerInfo.fActualOp);
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000348 SkASSERT(DRAW_BITMAP == dbmInfo.fActualOp ||
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000349 DRAW_BITMAP_MATRIX == dbmInfo.fActualOp ||
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000350 DRAW_BITMAP_NINE == dbmInfo.fActualOp ||
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000351 DRAW_BITMAP_RECT_TO_RECT == dbmInfo.fActualOp);
352
353 uint32_t dbmPaintOffset = getPaintOffset(dbmInfo.fActualOp, dbmInfo.fSize);
354 uint32_t slPaintOffset = getPaintOffset(SAVE_LAYER, saveLayerInfo.fSize);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000355
356 // we have a match, now we need to get the paints involved
robertphillips@google.com5ba0d902013-03-12 16:05:14 +0000357 uint32_t dbmPaintId = *writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
358 uint32_t saveLayerPaintId = *writer->peek32(saveLayerInfo.fOffset+slPaintOffset);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000359
360 if (0 == saveLayerPaintId) {
361 // In this case the saveLayer/restore isn't needed at all - just kill the saveLayer
362 // and signal the caller (by returning true) to not add the RESTORE op
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000363 convert_command_to_noop(writer, saveLayerInfo.fOffset);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000364 return true;
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000365 }
366
robertphillips@google.come37ad352013-03-01 19:44:30 +0000367 if (0 == dbmPaintId) {
368 // In this case just make the DBM* use the saveLayer's paint, kill the saveLayer
369 // and signal the caller (by returning true) to not add the RESTORE op
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000370 convert_command_to_noop(writer, saveLayerInfo.fOffset);
371 uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000372 SkASSERT(0 == *ptr);
373 *ptr = saveLayerPaintId;
374 return true;
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000375 }
robertphillips@google.come37ad352013-03-01 19:44:30 +0000376
377 SkAutoTDelete<SkPaint> saveLayerPaint(paintDict->unflatten(saveLayerPaintId));
378 if (NULL == saveLayerPaint.get() || !is_simple(*saveLayerPaint)) {
379 return false;
380 }
381
382 // For this optimization we only fold the saveLayer and drawBitmapRect
383 // together if the saveLayer's draw is simple (i.e., no fancy effects) and
384 // and the only difference in the colors is that the saveLayer's can have
385 // an alpha while the drawBitmapRect's is opaque.
386 // TODO: it should be possible to fold them together even if they both
387 // have different non-255 alphas
388 SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque
389
390 SkAutoTDelete<SkPaint> dbmPaint(paintDict->unflatten(dbmPaintId));
391 if (NULL == dbmPaint.get() || dbmPaint->getColor() != layerColor) {
392 return false;
393 }
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000394
robertphillips@google.come37ad352013-03-01 19:44:30 +0000395 SkColor newColor = SkColorSetA(dbmPaint->getColor(),
396 SkColorGetA(saveLayerPaint->getColor()));
397 dbmPaint->setColor(newColor);
398
399 const SkFlatData* data = paintDict->findAndReturnFlat(*dbmPaint);
400 if (NULL == data) {
401 return false;
402 }
403
404 // kill the saveLayer and alter the DBMR2R's paint to be the modified one
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000405 convert_command_to_noop(writer, saveLayerInfo.fOffset);
406 uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
407 SkASSERT(dbmPaintId == *ptr);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000408 *ptr = data->index();
409 return true;
410}
411
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000412/*
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000413 * Restore has just been called (but not recorded), look back at the
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000414 * matching save* and see if we are in the configuration:
415 * SAVE_LAYER
416 * SAVE
417 * CLIP_RECT
418 * DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
419 * RESTORE
420 * RESTORE
421 * where the saveLayer's color can be moved into the drawBitmap*'s paint
422 */
423static bool remove_save_layer2(SkWriter32* writer, int32_t offset,
424 SkPaintDictionary* paintDict) {
425
426 // back up to the save block
427 // TODO: add a stack to track save*/restore offsets rather than searching backwards
428 while (offset > 0) {
429 offset = *writer->peek32(offset);
430 }
431
432 int pattern[] = { SAVE_LAYER, SAVE, CLIP_RECT, kDRAW_BITMAP_FLAVOR, RESTORE, /* RESTORE */ };
433 CommandInfo result[SK_ARRAY_COUNT(pattern)];
434
435 if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
436 return false;
437 }
438
439 if (kSaveLayerWithBoundsSize == result[0].fSize) {
440 // The saveLayer's bound can offset where the dbm is drawn
441 return false;
442 }
443
444 return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
445 result[0], result[3]);
446}
robertphillips@google.come37ad352013-03-01 19:44:30 +0000447
448/*
449 * Restore has just been called (but not recorded), so look back at the
reed@google.comffacd3c2012-08-30 15:31:23 +0000450 * matching save(), and see if we can eliminate the pair of them, due to no
451 * intervening matrix/clip calls.
452 *
453 * If so, update the writer and return true, in which case we won't even record
454 * the restore() call. If we still need the restore(), return false.
455 */
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000456static bool collapse_save_clip_restore(SkWriter32* writer, int32_t offset,
457 SkPaintDictionary* paintDict) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000458#ifdef TRACK_COLLAPSE_STATS
459 gCollapseCalls += 1;
460#endif
461
462 int32_t restoreOffset = (int32_t)writer->size();
463
464 // back up to the save block
465 while (offset > 0) {
466 offset = *writer->peek32(offset);
467 }
468
469 // now offset points to a save
470 offset = -offset;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000471 uint32_t opSize;
472 DrawType op = peek_op_and_size(writer, offset, &opSize);
473 if (SAVE_LAYER == op) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000474 // not ready to cull these out yet (mrr)
475 return false;
476 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000477 SkASSERT(SAVE == op);
reed@google.comffacd3c2012-08-30 15:31:23 +0000478
479 // Walk forward until we get back to either a draw-verb (abort) or we hit
480 // our restore (success).
481 int32_t saveOffset = offset;
482
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000483 offset += opSize;
reed@google.comffacd3c2012-08-30 15:31:23 +0000484 while (offset < restoreOffset) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000485 op = peek_op_and_size(writer, offset, &opSize);
486 if ((op > CONCAT && op < ROTATE) || (SAVE_LAYER == op)) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000487 // drawing verb, abort
488 return false;
489 }
490 offset += opSize;
491 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000492
reed@google.comffacd3c2012-08-30 15:31:23 +0000493#ifdef TRACK_COLLAPSE_STATS
494 gCollapseCount += 1;
495 SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls,
496 (double)gCollapseCount / gCollapseCalls, "%");
497#endif
498
499 writer->rewindToOffset(saveOffset);
500 return true;
501}
502
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000503typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
504 SkPaintDictionary* paintDict);
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000505enum PictureRecordOptType {
506 kRewind_OptType, // Optimization rewinds the command stream
507 kCollapseSaveLayer_OptType, // Optimization eliminates a save/restore pair
508};
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000509
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000510struct PictureRecordOpt {
511 PictureRecordOptProc fProc;
512 PictureRecordOptType fType;
513};
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000514/*
515 * A list of the optimizations that are tried upon seeing a restore
516 * TODO: add a real API for such optimizations
517 * Add the ability to fire optimizations on any op (not just RESTORE)
518 */
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000519static const PictureRecordOpt gPictureRecordOpts[] = {
520 { collapse_save_clip_restore, kRewind_OptType },
521 { remove_save_layer1, kCollapseSaveLayer_OptType },
522 { remove_save_layer2, kCollapseSaveLayer_OptType }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000523};
524
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000525// This is called after an optimization has been applied to the command stream
526// in order to adjust the contents and state of the bounding box hierarchy and
527// state tree to reflect the optimization.
528static void apply_optimization_to_bbh(PictureRecordOptType opt, SkPictureStateTree* stateTree,
529 SkBBoxHierarchy* boundingHierarchy) {
530 switch (opt) {
531 case kCollapseSaveLayer_OptType:
532 stateTree->saveCollapsed();
533 break;
534 case kRewind_OptType:
535 if (NULL != boundingHierarchy) {
536 boundingHierarchy->rewindInserts();
537 }
538 // Note: No need to touch the state tree for this to work correctly.
539 // Unused branches do not burden the playback, and pruning the tree
540 // would be O(N^2), so it is best to leave it alone.
541 break;
542 default:
543 SkASSERT(0);
544 }
545}
546
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547void SkPictureRecord::restore() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000548 // FIXME: SkDeferredCanvas needs to be refactored to respect
549 // save/restore balancing so that the following test can be
550 // turned on permanently.
551#if 0
552 SkASSERT(fRestoreOffsetStack.count() > 1);
553#endif
554
reed@android.comb4e22d62009-07-09 15:20:25 +0000555 // check for underflow
556 if (fRestoreOffsetStack.count() == 0) {
557 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 }
reed@android.comb4e22d62009-07-09 15:20:25 +0000559
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000560 if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
561 fFirstSavedLayerIndex = kNoSavedLayerIndex;
562 }
563
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000564 uint32_t initialOffset, size;
reed@google.com7ed15342013-03-12 15:55:47 +0000565 size_t opt;
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000566 for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000567 if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000568 // Some optimization fired so don't add the RESTORE
569 size = 0;
570 initialOffset = fWriter.size();
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000571 apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
572 fStateTree, fBoundingHierarchy);
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000573 break;
574 }
575 }
576
577 if (SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
578 // No optimization fired so add the RESTORE
reed@google.comffacd3c2012-08-30 15:31:23 +0000579 fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000580 size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000581 initialOffset = this->addDraw(RESTORE, &size);
reed@google.comffacd3c2012-08-30 15:31:23 +0000582 }
583
reed@android.comb4e22d62009-07-09 15:20:25 +0000584 fRestoreOffsetStack.pop();
reed@android.com32a42492009-07-10 03:33:52 +0000585
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000586 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 return this->INHERITED::restore();
588}
589
590bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000591 // op + dx + dy
592 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
593 uint32_t initialOffset = this->addDraw(TRANSLATE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 addScalar(dx);
595 addScalar(dy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000596 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 return this->INHERITED::translate(dx, dy);
598}
599
600bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000601 // op + sx + sy
602 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
603 uint32_t initialOffset = this->addDraw(SCALE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 addScalar(sx);
605 addScalar(sy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000606 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 return this->INHERITED::scale(sx, sy);
608}
609
610bool SkPictureRecord::rotate(SkScalar degrees) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000611 // op + degrees
612 uint32_t size = 1 * kUInt32Size + sizeof(SkScalar);
613 uint32_t initialOffset = this->addDraw(ROTATE, &size);
reed@google.com82065d62011-02-07 15:30:46 +0000614 addScalar(degrees);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000615 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 return this->INHERITED::rotate(degrees);
617}
618
619bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000620 // op + sx + sy
621 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
622 uint32_t initialOffset = this->addDraw(SKEW, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 addScalar(sx);
624 addScalar(sy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000625 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 return this->INHERITED::skew(sx, sy);
627}
628
629bool SkPictureRecord::concat(const SkMatrix& matrix) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000630 validate(fWriter.size(), 0);
631 // op + matrix index
632 uint32_t size = 2 * kUInt32Size;
633 uint32_t initialOffset = this->addDraw(CONCAT, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000635 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 return this->INHERITED::concat(matrix);
637}
638
reed@android.com6e073b92009-01-06 15:03:30 +0000639void SkPictureRecord::setMatrix(const SkMatrix& matrix) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000640 validate(fWriter.size(), 0);
641 // op + matrix index
642 uint32_t size = 2 * kUInt32Size;
643 uint32_t initialOffset = this->addDraw(SET_MATRIX, &size);
reed@android.com6e073b92009-01-06 15:03:30 +0000644 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000645 validate(initialOffset, size);
reed@android.com6e073b92009-01-06 15:03:30 +0000646 this->INHERITED::setMatrix(matrix);
647}
648
reed@google.com45482d12011-08-29 19:02:39 +0000649static bool regionOpExpands(SkRegion::Op op) {
650 switch (op) {
651 case SkRegion::kUnion_Op:
652 case SkRegion::kXOR_Op:
653 case SkRegion::kReverseDifference_Op:
654 case SkRegion::kReplace_Op:
655 return true;
656 case SkRegion::kIntersect_Op:
657 case SkRegion::kDifference_Op:
658 return false;
659 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000660 SkDEBUGFAIL("unknown region op");
reed@google.com45482d12011-08-29 19:02:39 +0000661 return false;
662 }
663}
664
robertphillips@google.come37ad352013-03-01 19:44:30 +0000665void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000666 int32_t offset = fRestoreOffsetStack.top();
667 while (offset > 0) {
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000668 uint32_t* peek = fWriter.peek32(offset);
669 offset = *peek;
670 *peek = restoreOffset;
671 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000672
reed@google.comffacd3c2012-08-30 15:31:23 +0000673#ifdef SK_DEBUG
674 // assert that the final offset value points to a save verb
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000675 uint32_t opSize;
676 DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize);
reed@google.comffacd3c2012-08-30 15:31:23 +0000677 SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
678#endif
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000679}
680
reed@google.comd86e7ab2012-09-27 20:31:31 +0000681void SkPictureRecord::beginRecording() {
682 // we have to call this *after* our constructor, to ensure that it gets
683 // recorded. This is balanced by restoreToCount() call from endRecording,
684 // which in-turn calls our overridden restore(), so those get recorded too.
685 fInitialSaveCount = this->save(kMatrixClip_SaveFlag);
686}
687
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000688void SkPictureRecord::endRecording() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000689 SkASSERT(kNoInitialSave != fInitialSaveCount);
690 this->restoreToCount(fInitialSaveCount);
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000691}
692
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000693void SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
reed@google.com21b519d2012-10-02 17:42:15 +0000694 if (fRestoreOffsetStack.isEmpty()) {
695 return;
696 }
697
reed@google.com45482d12011-08-29 19:02:39 +0000698 if (regionOpExpands(op)) {
699 // Run back through any previous clip ops, and mark their offset to
700 // be 0, disabling their ability to trigger a jump-to-restore, otherwise
701 // they could hide this clips ability to expand the clip (i.e. go from
702 // empty to non-empty).
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000703 fillRestoreOffsetPlaceholdersForCurrentStackLevel(0);
reed@google.com45482d12011-08-29 19:02:39 +0000704 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000705
reed@google.com45482d12011-08-29 19:02:39 +0000706 size_t offset = fWriter.size();
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000707 // The RestoreOffset field is initially filled with a placeholder
708 // value that points to the offset of the previous RestoreOffset
709 // in the current stack level, thus forming a linked list so that
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000710 // the restore offsets can be filled in when the corresponding
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000711 // restore command is recorded.
reed@google.com45482d12011-08-29 19:02:39 +0000712 addInt(fRestoreOffsetStack.top());
713 fRestoreOffsetStack.top() = offset;
714}
715
reed@google.com071eef92011-10-12 11:52:53 +0000716bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.comf9291502013-02-15 15:13:27 +0000717 // id + rect + clip params
718 uint32_t size = 1 * kUInt32Size + sizeof(rect) + 1 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000719 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000720 if (!fRestoreOffsetStack.isEmpty()) {
721 // + restore offset
722 size += kUInt32Size;
723 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000724 uint32_t initialOffset = this->addDraw(CLIP_RECT, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 addRect(rect);
reed@google.com83ab4952011-11-11 21:34:54 +0000726 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000727 recordRestoreOffsetPlaceholder(op);
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000728
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000729 validate(initialOffset, size);
reed@google.com071eef92011-10-12 11:52:53 +0000730 return this->INHERITED::clipRect(rect, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731}
732
reed@google.com4ed0fb72012-12-12 20:48:18 +0000733bool SkPictureRecord::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
734 if (rrect.isRect()) {
735 return this->SkPictureRecord::clipRect(rrect.getBounds(), op, doAA);
736 }
737
robertphillips@google.comf9291502013-02-15 15:13:27 +0000738 // op + rrect + clip params
739 uint32_t size = 1 * kUInt32Size + SkRRect::kSizeInMemory + 1 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000740 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000741 if (!fRestoreOffsetStack.isEmpty()) {
742 // + restore offset
743 size += kUInt32Size;
744 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000745 uint32_t initialOffset = this->addDraw(CLIP_RRECT, &size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000746 addRRect(rrect);
747 addInt(ClipParams_pack(op, doAA));
748 recordRestoreOffsetPlaceholder(op);
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000749
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000750 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000751
752 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
753 return this->INHERITED::clipRect(rrect.getBounds(), op, doAA);
754 } else {
755 return this->INHERITED::clipRRect(rrect, op, doAA);
756 }
757}
758
reed@google.com071eef92011-10-12 11:52:53 +0000759bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000760
761 SkRect r;
reed@google.com907ef6c2012-12-10 15:50:37 +0000762 if (!path.isInverseFillType() && path.isRect(&r)) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000763 return this->clipRect(r, op, doAA);
764 }
765
robertphillips@google.comf9291502013-02-15 15:13:27 +0000766 // op + path index + clip params
767 uint32_t size = 3 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000768 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000769 if (!fRestoreOffsetStack.isEmpty()) {
770 // + restore offset
771 size += kUInt32Size;
772 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000773 uint32_t initialOffset = this->addDraw(CLIP_PATH, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 addPath(path);
reed@google.com83ab4952011-11-11 21:34:54 +0000775 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000776 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000777
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000778 validate(initialOffset, size);
reed@google.com82065d62011-02-07 15:30:46 +0000779
reed@android.comae814c82009-02-13 14:56:09 +0000780 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
reed@google.com071eef92011-10-12 11:52:53 +0000781 return this->INHERITED::clipRect(path.getBounds(), op, doAA);
reed@android.comae814c82009-02-13 14:56:09 +0000782 } else {
reed@google.com071eef92011-10-12 11:52:53 +0000783 return this->INHERITED::clipPath(path, op, doAA);
reed@android.comae814c82009-02-13 14:56:09 +0000784 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785}
786
787bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
robertphillips@google.comf9291502013-02-15 15:13:27 +0000788 // op + region index + clip params
789 uint32_t size = 3 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000790 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000791 if (!fRestoreOffsetStack.isEmpty()) {
792 // + restore offset
793 size += kUInt32Size;
794 }
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000795 uint32_t initialOffset = this->addDraw(CLIP_REGION, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 addRegion(region);
reed@google.com83ab4952011-11-11 21:34:54 +0000797 addInt(ClipParams_pack(op, false));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000798 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000799
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000800 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 return this->INHERITED::clipRegion(region, op);
802}
803
reed@google.com2a981812011-04-14 18:59:28 +0000804void SkPictureRecord::clear(SkColor color) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000805 // op + color
806 uint32_t size = 2 * kUInt32Size;
807 uint32_t initialOffset = this->addDraw(DRAW_CLEAR, &size);
reed@google.com2a981812011-04-14 18:59:28 +0000808 addInt(color);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000809 validate(initialOffset, size);
reed@google.com2a981812011-04-14 18:59:28 +0000810}
811
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812void SkPictureRecord::drawPaint(const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000813 // op + paint index
814 uint32_t size = 2 * kUInt32Size;
815 uint32_t initialOffset = this->addDraw(DRAW_PAINT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000816 SkASSERT(initialOffset+getPaintOffset(DRAW_PAINT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 addPaint(paint);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000818 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819}
820
821void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000822 const SkPaint& paint) {
823 // op + paint index + mode + count + point data
824 uint32_t size = 4 * kUInt32Size + count * sizeof(SkPoint);
825 uint32_t initialOffset = this->addDraw(DRAW_POINTS, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000826 SkASSERT(initialOffset+getPaintOffset(DRAW_POINTS, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 addPaint(paint);
828 addInt(mode);
829 addInt(count);
830 fWriter.writeMul4(pts, count * sizeof(SkPoint));
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000831 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832}
833
reed@google.com4ed0fb72012-12-12 20:48:18 +0000834void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000835 // op + paint index + rect
836 uint32_t size = 2 * kUInt32Size + sizeof(oval);
837 uint32_t initialOffset = this->addDraw(DRAW_OVAL, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000838 SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000839 addPaint(paint);
840 addRect(oval);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000841 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000842}
843
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000845 // op + paint index + rect
846 uint32_t size = 2 * kUInt32Size + sizeof(rect);
847 uint32_t initialOffset = this->addDraw(DRAW_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000848 SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 addPaint(paint);
850 addRect(rect);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000851 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852}
853
reed@google.com4ed0fb72012-12-12 20:48:18 +0000854void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000855 uint32_t initialOffset, size;
reed@google.com4ed0fb72012-12-12 20:48:18 +0000856 if (rrect.isRect()) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000857 // op + paint index + rect
858 size = 2 * kUInt32Size + sizeof(SkRect);
859 initialOffset = this->addDraw(DRAW_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000860 SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000861 addPaint(paint);
862 addRect(rrect.getBounds());
863 } else if (rrect.isOval()) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000864 // op + paint index + rect
865 size = 2 * kUInt32Size + sizeof(SkRect);
866 initialOffset = this->addDraw(DRAW_OVAL, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000867 SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000868 addPaint(paint);
869 addRect(rrect.getBounds());
870 } else {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000871 // op + paint index + rrect
872 size = 2 * kUInt32Size + SkRRect::kSizeInMemory;
873 initialOffset = this->addDraw(DRAW_RRECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000874 SkASSERT(initialOffset+getPaintOffset(DRAW_RRECT, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000875 addPaint(paint);
876 addRRect(rrect);
877 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000878 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000879}
880
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000882 // op + paint index + path index
883 uint32_t size = 3 * kUInt32Size;
884 uint32_t initialOffset = this->addDraw(DRAW_PATH, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000885 SkASSERT(initialOffset+getPaintOffset(DRAW_PATH, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886 addPaint(paint);
887 addPath(path);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000888 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889}
890
891void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
892 const SkPaint* paint = NULL) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000893 // op + paint index + bitmap index + left + top
894 uint32_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar);
895 uint32_t initialOffset = this->addDraw(DRAW_BITMAP, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000896 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 addPaintPtr(paint);
898 addBitmap(bitmap);
899 addScalar(left);
900 addScalar(top);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000901 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902}
903
reed@google.com71121732012-09-18 15:14:33 +0000904void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905 const SkRect& dst, const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000906 // id + paint index + bitmap index + bool for 'src'
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000907 uint32_t size = 4 * kUInt32Size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000908 if (NULL != src) {
909 size += sizeof(*src); // + rect
910 }
911 size += sizeof(dst); // + rect
912
913 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_RECT_TO_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000914 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_RECT_TO_RECT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915 addPaintPtr(paint);
916 addBitmap(bitmap);
reed@google.com71121732012-09-18 15:14:33 +0000917 addRectPtr(src); // may be null
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918 addRect(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000919 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920}
921
922void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
reed@google.comf0b5e112011-09-07 11:57:34 +0000923 const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000924 // id + paint index + bitmap index + matrix index
925 uint32_t size = 4 * kUInt32Size;
926 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000927 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_MATRIX, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000928 addPaintPtr(paint);
929 addBitmap(bitmap);
930 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000931 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932}
933
reed@google.comf0b5e112011-09-07 11:57:34 +0000934void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
935 const SkRect& dst, const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000936 // op + paint index + bitmap id + center + dst rect
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000937 uint32_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000938 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000939 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_NINE, size) == fWriter.size());
reed@google.comf0b5e112011-09-07 11:57:34 +0000940 addPaintPtr(paint);
941 addBitmap(bitmap);
942 addIRect(center);
943 addRect(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000944 validate(initialOffset, size);
reed@google.comf0b5e112011-09-07 11:57:34 +0000945}
946
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
948 const SkPaint* paint = NULL) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000949 // op + paint index + bitmap index + left + top
950 uint32_t size = 5 * kUInt32Size;
951 uint32_t initialOffset = this->addDraw(DRAW_SPRITE, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000952 SkASSERT(initialOffset+getPaintOffset(DRAW_SPRITE, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953 addPaintPtr(paint);
954 addBitmap(bitmap);
955 addInt(left);
956 addInt(top);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000957 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958}
959
reed@google.com45954262012-12-07 17:14:40 +0000960// Return fontmetrics.fTop,fBottom in topbot[0,1], after they have been
961// tweaked by paint.computeFastBounds().
962//
963static void computeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 SkPaint::FontMetrics metrics;
965 paint.getFontMetrics(&metrics);
966 SkRect bounds;
967 // construct a rect so we can see any adjustments from the paint.
968 // we use 0,1 for left,right, just so the rect isn't empty
reed@google.com45954262012-12-07 17:14:40 +0000969 bounds.set(0, metrics.fTop, SK_Scalar1, metrics.fBottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 (void)paint.computeFastBounds(bounds, &bounds);
reed@google.com45954262012-12-07 17:14:40 +0000971 topbot[0] = bounds.fTop;
972 topbot[1] = bounds.fBottom;
973}
974
junov@chromium.orgf3b12232013-01-22 17:50:47 +0000975void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData& flat,
reed@google.com45954262012-12-07 17:14:40 +0000976 SkScalar minY, SkScalar maxY) {
junov@chromium.org3f5ecd62013-01-22 18:01:26 +0000977 if (!flat.isTopBotWritten()) {
978 computeFontMetricsTopBottom(paint, flat.writableTopBot());
979 SkASSERT(flat.isTopBotWritten());
reed@google.com45954262012-12-07 17:14:40 +0000980 }
junov@chromium.org3f5ecd62013-01-22 18:01:26 +0000981 addScalar(flat.topBot()[0] + minY);
982 addScalar(flat.topBot()[1] + maxY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983}
984
reed@google.com82065d62011-02-07 15:30:46 +0000985void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 SkScalar y, const SkPaint& paint) {
reed@google.com2eb5bb12012-04-12 14:27:42 +0000987 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com82065d62011-02-07 15:30:46 +0000988
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000989 // op + paint index + length + 'length' worth of chars + x + y
990 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * sizeof(SkScalar);
991 if (fast) {
992 size += 2 * sizeof(SkScalar); // + top & bottom
993 }
994
robertphillips@google.come37ad352013-03-01 19:44:30 +0000995 DrawType op = fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT;
996 uint32_t initialOffset = this->addDraw(op, &size);
997 SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
junov@chromium.orgf3b12232013-01-22 17:50:47 +0000998 const SkFlatData* flatPaintData = addPaint(paint);
999 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 addText(text, byteLength);
1001 addScalar(x);
1002 addScalar(y);
1003 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001004 addFontMetricsTopBottom(paint, *flatPaintData, y, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001006 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007}
1008
reed@google.com82065d62011-02-07 15:30:46 +00001009void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 const SkPoint pos[], const SkPaint& paint) {
1011 size_t points = paint.countText(text, byteLength);
1012 if (0 == points)
1013 return;
1014
1015 bool canUseDrawH = true;
reed@google.com9efd9a02012-01-30 15:41:43 +00001016 SkScalar minY = pos[0].fY;
1017 SkScalar maxY = pos[0].fY;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 // check if the caller really should have used drawPosTextH()
1019 {
1020 const SkScalar firstY = pos[0].fY;
1021 for (size_t index = 1; index < points; index++) {
1022 if (pos[index].fY != firstY) {
1023 canUseDrawH = false;
reed@google.com9efd9a02012-01-30 15:41:43 +00001024 if (pos[index].fY < minY) {
1025 minY = pos[index].fY;
1026 } else if (pos[index].fY > maxY) {
1027 maxY = pos[index].fY;
1028 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 }
1030 }
1031 }
reed@google.com82065d62011-02-07 15:30:46 +00001032
reed@google.com2eb5bb12012-04-12 14:27:42 +00001033 bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com9efd9a02012-01-30 15:41:43 +00001034 bool fast = canUseDrawH && fastBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001036 // op + paint index + length + 'length' worth of data + num points
1037 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1038 if (canUseDrawH) {
1039 if (fast) {
1040 size += 2 * sizeof(SkScalar); // + top & bottom
1041 }
1042 // + y-pos + actual x-point data
1043 size += sizeof(SkScalar) + points * sizeof(SkScalar);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 } else {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001045 // + x&y point data
1046 size += points * sizeof(SkPoint);
1047 if (fastBounds) {
1048 size += 2 * sizeof(SkScalar); // + top & bottom
1049 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001051
1052 DrawType op;
1053 if (fast) {
1054 op = DRAW_POS_TEXT_H_TOP_BOTTOM;
1055 } else if (canUseDrawH) {
1056 op = DRAW_POS_TEXT_H;
1057 } else if (fastBounds) {
1058 op = DRAW_POS_TEXT_TOP_BOTTOM;
1059 } else {
1060 op = DRAW_POS_TEXT;
1061 }
1062 uint32_t initialOffset = this->addDraw(op, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001063 SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001064 const SkFlatData* flatPaintData = addPaint(paint);
1065 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 addText(text, byteLength);
1067 addInt(points);
1068
1069#ifdef SK_DEBUG_SIZE
1070 size_t start = fWriter.size();
1071#endif
1072 if (canUseDrawH) {
1073 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001074 addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 }
1076 addScalar(pos[0].fY);
1077 SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
reed@google.com82065d62011-02-07 15:30:46 +00001078 for (size_t index = 0; index < points; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079 *xptr++ = pos[index].fX;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001080 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081 fWriter.writeMul4(pos, points * sizeof(SkPoint));
reed@google.com9efd9a02012-01-30 15:41:43 +00001082 if (fastBounds) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001083 addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY);
reed@google.com9efd9a02012-01-30 15:41:43 +00001084 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 }
1086#ifdef SK_DEBUG_SIZE
1087 fPointBytes += fWriter.size() - start;
1088 fPointWrites += points;
1089#endif
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001090 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091}
1092
1093void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
1094 const SkScalar xpos[], SkScalar constY,
1095 const SkPaint& paint) {
1096 size_t points = paint.countText(text, byteLength);
1097 if (0 == points)
1098 return;
reed@google.com82065d62011-02-07 15:30:46 +00001099
reed@google.com2eb5bb12012-04-12 14:27:42 +00001100 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001102 // op + paint index + length + 'length' worth of data + num points
1103 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1104 if (fast) {
1105 size += 2 * sizeof(SkScalar); // + top & bottom
1106 }
1107 // + y + the actual points
1108 size += 1 * kUInt32Size + points * sizeof(SkScalar);
1109
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +00001110 uint32_t initialOffset = this->addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H,
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001111 &size);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001112 const SkFlatData* flatPaintData = addPaint(paint);
1113 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114 addText(text, byteLength);
1115 addInt(points);
reed@google.com82065d62011-02-07 15:30:46 +00001116
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117#ifdef SK_DEBUG_SIZE
1118 size_t start = fWriter.size();
1119#endif
1120 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001121 addFontMetricsTopBottom(paint, *flatPaintData, constY, constY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122 }
1123 addScalar(constY);
1124 fWriter.writeMul4(xpos, points * sizeof(SkScalar));
1125#ifdef SK_DEBUG_SIZE
1126 fPointBytes += fWriter.size() - start;
1127 fPointWrites += points;
1128#endif
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001129 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130}
1131
reed@google.com82065d62011-02-07 15:30:46 +00001132void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength,
1133 const SkPath& path, const SkMatrix* matrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001134 const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001135 // op + paint index + length + 'length' worth of data + path index + matrix index
1136 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * kUInt32Size;
1137 uint32_t initialOffset = this->addDraw(DRAW_TEXT_ON_PATH, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001138 SkASSERT(initialOffset+getPaintOffset(DRAW_TEXT_ON_PATH, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 addPaint(paint);
1140 addText(text, byteLength);
1141 addPath(path);
1142 addMatrixPtr(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001143 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144}
1145
1146void SkPictureRecord::drawPicture(SkPicture& picture) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001147 // op + picture index
1148 uint32_t size = 2 * kUInt32Size;
1149 uint32_t initialOffset = this->addDraw(DRAW_PICTURE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 addPicture(picture);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001151 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152}
1153
1154void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
1155 const SkPoint vertices[], const SkPoint texs[],
1156 const SkColor colors[], SkXfermode*,
1157 const uint16_t indices[], int indexCount,
1158 const SkPaint& paint) {
1159 uint32_t flags = 0;
1160 if (texs) {
1161 flags |= DRAW_VERTICES_HAS_TEXS;
1162 }
1163 if (colors) {
1164 flags |= DRAW_VERTICES_HAS_COLORS;
1165 }
1166 if (indexCount > 0) {
1167 flags |= DRAW_VERTICES_HAS_INDICES;
1168 }
1169
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001170 // op + paint index + flags + vmode + vCount + vertices
1171 uint32_t size = 5 * kUInt32Size + vertexCount * sizeof(SkPoint);
1172 if (flags & DRAW_VERTICES_HAS_TEXS) {
1173 size += vertexCount * sizeof(SkPoint); // + uvs
1174 }
1175 if (flags & DRAW_VERTICES_HAS_COLORS) {
1176 size += vertexCount * sizeof(SkColor); // + vert colors
1177 }
1178 if (flags & DRAW_VERTICES_HAS_INDICES) {
1179 // + num indices + indices
1180 size += 1 * kUInt32Size + SkAlign4(indexCount * sizeof(uint16_t));
1181 }
1182
1183 uint32_t initialOffset = this->addDraw(DRAW_VERTICES, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001184 SkASSERT(initialOffset+getPaintOffset(DRAW_VERTICES, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 addPaint(paint);
1186 addInt(flags);
1187 addInt(vmode);
1188 addInt(vertexCount);
1189 addPoints(vertices, vertexCount);
1190 if (flags & DRAW_VERTICES_HAS_TEXS) {
1191 addPoints(texs, vertexCount);
1192 }
1193 if (flags & DRAW_VERTICES_HAS_COLORS) {
1194 fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
1195 }
1196 if (flags & DRAW_VERTICES_HAS_INDICES) {
1197 addInt(indexCount);
1198 fWriter.writePad(indices, indexCount * sizeof(uint16_t));
1199 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001200 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201}
1202
reed@android.comcb608442009-12-04 21:32:27 +00001203void SkPictureRecord::drawData(const void* data, size_t length) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001204 // op + length + 'length' worth of data
1205 uint32_t size = 2 * kUInt32Size + SkAlign4(length);
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +00001206 uint32_t initialOffset = this->addDraw(DRAW_DATA, &size);
reed@android.comcb608442009-12-04 21:32:27 +00001207 addInt(length);
1208 fWriter.writePad(data, length);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001209 validate(initialOffset, size);
reed@android.comcb608442009-12-04 21:32:27 +00001210}
1211
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212///////////////////////////////////////////////////////////////////////////////
reed@google.com82065d62011-02-07 15:30:46 +00001213
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
scroggo@google.com4b90b112012-12-04 15:08:56 +00001215 const int index = fBitmapHeap->insert(bitmap);
1216 // In debug builds, a bad return value from insert() will crash, allowing for debugging. In
1217 // release builds, the invalid value will be recorded so that the reader will know that there
1218 // was a problem.
1219 SkASSERT(index != SkBitmapHeap::INVALID_SLOT);
1220 addInt(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221}
1222
1223void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
1224 addMatrixPtr(&matrix);
1225}
1226
1227void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
reed@google.com83ca3372012-07-12 15:27:54 +00001228 this->addInt(matrix ? fMatrices.find(*matrix) : 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229}
1230
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001231const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
1232 const SkFlatData* data = paint ? fPaints.findAndReturnFlat(*paint) : NULL;
1233 int index = data ? data->index() : 0;
reed@google.com45954262012-12-07 17:14:40 +00001234 this->addInt(index);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001235 return data;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236}
1237
1238void SkPictureRecord::addPath(const SkPath& path) {
1239 if (NULL == fPathHeap) {
1240 fPathHeap = SkNEW(SkPathHeap);
1241 }
1242 addInt(fPathHeap->append(path));
1243}
1244
1245void SkPictureRecord::addPicture(SkPicture& picture) {
1246 int index = fPictureRefs.find(&picture);
1247 if (index < 0) { // not found
1248 index = fPictureRefs.count();
1249 *fPictureRefs.append() = &picture;
1250 picture.ref();
1251 }
1252 // follow the convention of recording a 1-based index
1253 addInt(index + 1);
1254}
1255
1256void SkPictureRecord::addPoint(const SkPoint& point) {
1257#ifdef SK_DEBUG_SIZE
1258 size_t start = fWriter.size();
1259#endif
1260 fWriter.writePoint(point);
1261#ifdef SK_DEBUG_SIZE
1262 fPointBytes += fWriter.size() - start;
1263 fPointWrites++;
1264#endif
1265}
reed@google.com82065d62011-02-07 15:30:46 +00001266
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
1268 fWriter.writeMul4(pts, count * sizeof(SkPoint));
1269#ifdef SK_DEBUG_SIZE
1270 fPointBytes += count * sizeof(SkPoint);
1271 fPointWrites++;
1272#endif
1273}
1274
1275void SkPictureRecord::addRect(const SkRect& rect) {
1276#ifdef SK_DEBUG_SIZE
1277 size_t start = fWriter.size();
1278#endif
1279 fWriter.writeRect(rect);
1280#ifdef SK_DEBUG_SIZE
1281 fRectBytes += fWriter.size() - start;
1282 fRectWrites++;
1283#endif
1284}
1285
1286void SkPictureRecord::addRectPtr(const SkRect* rect) {
1287 if (fWriter.writeBool(rect != NULL)) {
1288 fWriter.writeRect(*rect);
1289 }
1290}
1291
reed@google.comf0b5e112011-09-07 11:57:34 +00001292void SkPictureRecord::addIRect(const SkIRect& rect) {
1293 fWriter.write(&rect, sizeof(rect));
1294}
1295
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
1297 if (fWriter.writeBool(rect != NULL)) {
1298 *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
1299 }
1300}
1301
reed@google.com4ed0fb72012-12-12 20:48:18 +00001302void SkPictureRecord::addRRect(const SkRRect& rrect) {
1303 fWriter.writeRRect(rrect);
1304}
1305
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306void SkPictureRecord::addRegion(const SkRegion& region) {
reed@google.com83ca3372012-07-12 15:27:54 +00001307 addInt(fRegions.find(region));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308}
1309
1310void SkPictureRecord::addText(const void* text, size_t byteLength) {
1311#ifdef SK_DEBUG_SIZE
1312 size_t start = fWriter.size();
1313#endif
1314 addInt(byteLength);
1315 fWriter.writePad(text, byteLength);
1316#ifdef SK_DEBUG_SIZE
1317 fTextBytes += fWriter.size() - start;
1318 fTextWrites++;
1319#endif
1320}
1321
1322///////////////////////////////////////////////////////////////////////////////
1323
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324#ifdef SK_DEBUG_SIZE
1325size_t SkPictureRecord::size() const {
1326 size_t result = 0;
1327 size_t sizeData;
1328 bitmaps(&sizeData);
1329 result += sizeData;
1330 matrices(&sizeData);
1331 result += sizeData;
1332 paints(&sizeData);
1333 result += sizeData;
1334 paths(&sizeData);
1335 result += sizeData;
1336 pictures(&sizeData);
1337 result += sizeData;
1338 regions(&sizeData);
1339 result += sizeData;
1340 result += streamlen();
1341 return result;
1342}
1343
1344int SkPictureRecord::bitmaps(size_t* size) const {
1345 size_t result = 0;
1346 int count = fBitmaps.count();
reed@google.com82065d62011-02-07 15:30:46 +00001347 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
1349 *size = result;
1350 return count;
1351}
1352
1353int SkPictureRecord::matrices(size_t* size) const {
1354 int count = fMatrices.count();
1355 *size = sizeof(fMatrices[0]) * count;
1356 return count;
1357}
1358
1359int SkPictureRecord::paints(size_t* size) const {
1360 size_t result = 0;
1361 int count = fPaints.count();
reed@google.com82065d62011-02-07 15:30:46 +00001362 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 result += sizeof(fPaints[index]) + fPaints[index]->size();
1364 *size = result;
1365 return count;
1366}
1367
1368int SkPictureRecord::paths(size_t* size) const {
1369 size_t result = 0;
1370 int count = fPaths.count();
reed@google.com82065d62011-02-07 15:30:46 +00001371 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372 result += sizeof(fPaths[index]) + fPaths[index]->size();
1373 *size = result;
1374 return count;
1375}
1376
1377int SkPictureRecord::regions(size_t* size) const {
1378 size_t result = 0;
1379 int count = fRegions.count();
reed@google.com82065d62011-02-07 15:30:46 +00001380 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381 result += sizeof(fRegions[index]) + fRegions[index]->size();
1382 *size = result;
1383 return count;
1384}
1385
1386size_t SkPictureRecord::streamlen() const {
1387 return fWriter.size();
1388}
1389#endif
1390
1391#ifdef SK_DEBUG_VALIDATE
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001392void SkPictureRecord::validate(uint32_t initialOffset, uint32_t size) const {
1393 SkASSERT(fWriter.size() == initialOffset + size);
1394
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395 validateBitmaps();
1396 validateMatrices();
1397 validatePaints();
1398 validatePaths();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 validateRegions();
1400}
1401
1402void SkPictureRecord::validateBitmaps() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001403 int count = fBitmapHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 SkASSERT((unsigned) count < 0x1000);
1405 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001406 const SkBitmap* bitPtr = fBitmapHeap->getBitmap(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 SkASSERT(bitPtr);
1408 bitPtr->validate();
1409 }
1410}
1411
1412void SkPictureRecord::validateMatrices() const {
1413 int count = fMatrices.count();
1414 SkASSERT((unsigned) count < 0x1000);
1415 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001416 const SkFlatData* matrix = fMatrices[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417 SkASSERT(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001418// matrix->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419 }
1420}
1421
1422void SkPictureRecord::validatePaints() const {
1423 int count = fPaints.count();
1424 SkASSERT((unsigned) count < 0x1000);
1425 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001426 const SkFlatData* paint = fPaints[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 SkASSERT(paint);
1428// paint->validate();
1429 }
1430}
1431
1432void SkPictureRecord::validatePaths() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001433 if (NULL == fPathHeap) {
1434 return;
1435 }
1436
1437 int count = fPathHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438 SkASSERT((unsigned) count < 0x1000);
1439 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001440 const SkPath& path = (*fPathHeap)[index];
1441 path.validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442 }
1443}
1444
1445void SkPictureRecord::validateRegions() const {
1446 int count = fRegions.count();
1447 SkASSERT((unsigned) count < 0x1000);
1448 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001449 const SkFlatData* region = fRegions[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450 SkASSERT(region);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001451// region->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452 }
1453}
1454#endif