blob: 421c2bd9608a43d1d864babaef97e3bd96fc3d54 [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
230// information.
231struct 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'
239 * in the byte stream and stopping at the end of the stream. Upon success,
240 * 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/*
298 * 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);
348 SkASSERT(DRAW_BITMAP == dbmInfo.fActualOp ||
349 DRAW_BITMAP_MATRIX == dbmInfo.fActualOp ||
350 DRAW_BITMAP_NINE == dbmInfo.fActualOp ||
351 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/*
413 * Restore has just been called (but not recorded), look back at the
414 * 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);
505
506/*
507 * A list of the optimizations that are tried upon seeing a restore
508 * TODO: add a real API for such optimizations
509 * Add the ability to fire optimizations on any op (not just RESTORE)
510 */
511static const PictureRecordOptProc gPictureRecordOpts[] = {
512 collapse_save_clip_restore,
513#ifndef SK_IGNORE_PICTURE_RECORD_SAVE_LAYER_OPT
514 remove_save_layer1,
515 remove_save_layer2,
516#endif
517};
518
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519void SkPictureRecord::restore() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000520 // FIXME: SkDeferredCanvas needs to be refactored to respect
521 // save/restore balancing so that the following test can be
522 // turned on permanently.
523#if 0
524 SkASSERT(fRestoreOffsetStack.count() > 1);
525#endif
526
reed@android.comb4e22d62009-07-09 15:20:25 +0000527 // check for underflow
528 if (fRestoreOffsetStack.count() == 0) {
529 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 }
reed@android.comb4e22d62009-07-09 15:20:25 +0000531
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000532 if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
533 fFirstSavedLayerIndex = kNoSavedLayerIndex;
534 }
535
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000536 uint32_t initialOffset, size;
reed@google.com7ed15342013-03-12 15:55:47 +0000537 size_t opt;
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000538 for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
539 if ((*gPictureRecordOpts[opt])(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
540 // Some optimization fired so don't add the RESTORE
541 size = 0;
542 initialOffset = fWriter.size();
543 break;
544 }
545 }
546
547 if (SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
548 // No optimization fired so add the RESTORE
reed@google.comffacd3c2012-08-30 15:31:23 +0000549 fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000550 size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000551 initialOffset = this->addDraw(RESTORE, &size);
reed@google.comffacd3c2012-08-30 15:31:23 +0000552 }
553
reed@android.comb4e22d62009-07-09 15:20:25 +0000554 fRestoreOffsetStack.pop();
reed@android.com32a42492009-07-10 03:33:52 +0000555
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000556 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 return this->INHERITED::restore();
558}
559
560bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000561 // op + dx + dy
562 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
563 uint32_t initialOffset = this->addDraw(TRANSLATE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 addScalar(dx);
565 addScalar(dy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000566 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 return this->INHERITED::translate(dx, dy);
568}
569
570bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000571 // op + sx + sy
572 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
573 uint32_t initialOffset = this->addDraw(SCALE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574 addScalar(sx);
575 addScalar(sy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000576 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 return this->INHERITED::scale(sx, sy);
578}
579
580bool SkPictureRecord::rotate(SkScalar degrees) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000581 // op + degrees
582 uint32_t size = 1 * kUInt32Size + sizeof(SkScalar);
583 uint32_t initialOffset = this->addDraw(ROTATE, &size);
reed@google.com82065d62011-02-07 15:30:46 +0000584 addScalar(degrees);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000585 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586 return this->INHERITED::rotate(degrees);
587}
588
589bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000590 // op + sx + sy
591 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
592 uint32_t initialOffset = this->addDraw(SKEW, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 addScalar(sx);
594 addScalar(sy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000595 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 return this->INHERITED::skew(sx, sy);
597}
598
599bool SkPictureRecord::concat(const SkMatrix& matrix) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000600 validate(fWriter.size(), 0);
601 // op + matrix index
602 uint32_t size = 2 * kUInt32Size;
603 uint32_t initialOffset = this->addDraw(CONCAT, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000605 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 return this->INHERITED::concat(matrix);
607}
608
reed@android.com6e073b92009-01-06 15:03:30 +0000609void SkPictureRecord::setMatrix(const SkMatrix& matrix) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000610 validate(fWriter.size(), 0);
611 // op + matrix index
612 uint32_t size = 2 * kUInt32Size;
613 uint32_t initialOffset = this->addDraw(SET_MATRIX, &size);
reed@android.com6e073b92009-01-06 15:03:30 +0000614 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000615 validate(initialOffset, size);
reed@android.com6e073b92009-01-06 15:03:30 +0000616 this->INHERITED::setMatrix(matrix);
617}
618
reed@google.com45482d12011-08-29 19:02:39 +0000619static bool regionOpExpands(SkRegion::Op op) {
620 switch (op) {
621 case SkRegion::kUnion_Op:
622 case SkRegion::kXOR_Op:
623 case SkRegion::kReverseDifference_Op:
624 case SkRegion::kReplace_Op:
625 return true;
626 case SkRegion::kIntersect_Op:
627 case SkRegion::kDifference_Op:
628 return false;
629 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000630 SkDEBUGFAIL("unknown region op");
reed@google.com45482d12011-08-29 19:02:39 +0000631 return false;
632 }
633}
634
robertphillips@google.come37ad352013-03-01 19:44:30 +0000635void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000636 int32_t offset = fRestoreOffsetStack.top();
637 while (offset > 0) {
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000638 uint32_t* peek = fWriter.peek32(offset);
639 offset = *peek;
640 *peek = restoreOffset;
641 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000642
reed@google.comffacd3c2012-08-30 15:31:23 +0000643#ifdef SK_DEBUG
644 // assert that the final offset value points to a save verb
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000645 uint32_t opSize;
646 DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize);
reed@google.comffacd3c2012-08-30 15:31:23 +0000647 SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
648#endif
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000649}
650
reed@google.comd86e7ab2012-09-27 20:31:31 +0000651void SkPictureRecord::beginRecording() {
652 // we have to call this *after* our constructor, to ensure that it gets
653 // recorded. This is balanced by restoreToCount() call from endRecording,
654 // which in-turn calls our overridden restore(), so those get recorded too.
655 fInitialSaveCount = this->save(kMatrixClip_SaveFlag);
656}
657
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000658void SkPictureRecord::endRecording() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000659 SkASSERT(kNoInitialSave != fInitialSaveCount);
660 this->restoreToCount(fInitialSaveCount);
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000661}
662
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000663void SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
reed@google.com21b519d2012-10-02 17:42:15 +0000664 if (fRestoreOffsetStack.isEmpty()) {
665 return;
666 }
667
reed@google.com45482d12011-08-29 19:02:39 +0000668 if (regionOpExpands(op)) {
669 // Run back through any previous clip ops, and mark their offset to
670 // be 0, disabling their ability to trigger a jump-to-restore, otherwise
671 // they could hide this clips ability to expand the clip (i.e. go from
672 // empty to non-empty).
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000673 fillRestoreOffsetPlaceholdersForCurrentStackLevel(0);
reed@google.com45482d12011-08-29 19:02:39 +0000674 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000675
reed@google.com45482d12011-08-29 19:02:39 +0000676 size_t offset = fWriter.size();
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000677 // The RestoreOffset field is initially filled with a placeholder
678 // value that points to the offset of the previous RestoreOffset
679 // in the current stack level, thus forming a linked list so that
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000680 // the restore offsets can be filled in when the corresponding
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000681 // restore command is recorded.
reed@google.com45482d12011-08-29 19:02:39 +0000682 addInt(fRestoreOffsetStack.top());
683 fRestoreOffsetStack.top() = offset;
684}
685
reed@google.com071eef92011-10-12 11:52:53 +0000686bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.comf9291502013-02-15 15:13:27 +0000687 // id + rect + clip params
688 uint32_t size = 1 * kUInt32Size + sizeof(rect) + 1 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000689 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000690 if (!fRestoreOffsetStack.isEmpty()) {
691 // + restore offset
692 size += kUInt32Size;
693 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000694 uint32_t initialOffset = this->addDraw(CLIP_RECT, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 addRect(rect);
reed@google.com83ab4952011-11-11 21:34:54 +0000696 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000697 recordRestoreOffsetPlaceholder(op);
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000698
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000699 validate(initialOffset, size);
reed@google.com071eef92011-10-12 11:52:53 +0000700 return this->INHERITED::clipRect(rect, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701}
702
reed@google.com4ed0fb72012-12-12 20:48:18 +0000703bool SkPictureRecord::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
704 if (rrect.isRect()) {
705 return this->SkPictureRecord::clipRect(rrect.getBounds(), op, doAA);
706 }
707
robertphillips@google.comf9291502013-02-15 15:13:27 +0000708 // op + rrect + clip params
709 uint32_t size = 1 * kUInt32Size + SkRRect::kSizeInMemory + 1 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000710 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000711 if (!fRestoreOffsetStack.isEmpty()) {
712 // + restore offset
713 size += kUInt32Size;
714 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000715 uint32_t initialOffset = this->addDraw(CLIP_RRECT, &size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000716 addRRect(rrect);
717 addInt(ClipParams_pack(op, doAA));
718 recordRestoreOffsetPlaceholder(op);
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000719
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000720 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000721
722 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
723 return this->INHERITED::clipRect(rrect.getBounds(), op, doAA);
724 } else {
725 return this->INHERITED::clipRRect(rrect, op, doAA);
726 }
727}
728
reed@google.com071eef92011-10-12 11:52:53 +0000729bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000730
731 SkRect r;
reed@google.com907ef6c2012-12-10 15:50:37 +0000732 if (!path.isInverseFillType() && path.isRect(&r)) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000733 return this->clipRect(r, op, doAA);
734 }
735
robertphillips@google.comf9291502013-02-15 15:13:27 +0000736 // op + path index + clip params
737 uint32_t size = 3 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000738 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000739 if (!fRestoreOffsetStack.isEmpty()) {
740 // + restore offset
741 size += kUInt32Size;
742 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000743 uint32_t initialOffset = this->addDraw(CLIP_PATH, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 addPath(path);
reed@google.com83ab4952011-11-11 21:34:54 +0000745 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000746 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000747
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000748 validate(initialOffset, size);
reed@google.com82065d62011-02-07 15:30:46 +0000749
reed@android.comae814c82009-02-13 14:56:09 +0000750 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
reed@google.com071eef92011-10-12 11:52:53 +0000751 return this->INHERITED::clipRect(path.getBounds(), op, doAA);
reed@android.comae814c82009-02-13 14:56:09 +0000752 } else {
reed@google.com071eef92011-10-12 11:52:53 +0000753 return this->INHERITED::clipPath(path, op, doAA);
reed@android.comae814c82009-02-13 14:56:09 +0000754 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755}
756
757bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
robertphillips@google.comf9291502013-02-15 15:13:27 +0000758 // op + region index + clip params
759 uint32_t size = 3 * 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 }
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000765 uint32_t initialOffset = this->addDraw(CLIP_REGION, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 addRegion(region);
reed@google.com83ab4952011-11-11 21:34:54 +0000767 addInt(ClipParams_pack(op, false));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000768 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000769
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000770 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771 return this->INHERITED::clipRegion(region, op);
772}
773
reed@google.com2a981812011-04-14 18:59:28 +0000774void SkPictureRecord::clear(SkColor color) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000775 // op + color
776 uint32_t size = 2 * kUInt32Size;
777 uint32_t initialOffset = this->addDraw(DRAW_CLEAR, &size);
reed@google.com2a981812011-04-14 18:59:28 +0000778 addInt(color);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000779 validate(initialOffset, size);
reed@google.com2a981812011-04-14 18:59:28 +0000780}
781
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782void SkPictureRecord::drawPaint(const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000783 // op + paint index
784 uint32_t size = 2 * kUInt32Size;
785 uint32_t initialOffset = this->addDraw(DRAW_PAINT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000786 SkASSERT(initialOffset+getPaintOffset(DRAW_PAINT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 addPaint(paint);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000788 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789}
790
791void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000792 const SkPaint& paint) {
793 // op + paint index + mode + count + point data
794 uint32_t size = 4 * kUInt32Size + count * sizeof(SkPoint);
795 uint32_t initialOffset = this->addDraw(DRAW_POINTS, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000796 SkASSERT(initialOffset+getPaintOffset(DRAW_POINTS, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 addPaint(paint);
798 addInt(mode);
799 addInt(count);
800 fWriter.writeMul4(pts, count * sizeof(SkPoint));
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000801 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802}
803
reed@google.com4ed0fb72012-12-12 20:48:18 +0000804void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000805 // op + paint index + rect
806 uint32_t size = 2 * kUInt32Size + sizeof(oval);
807 uint32_t initialOffset = this->addDraw(DRAW_OVAL, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000808 SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000809 addPaint(paint);
810 addRect(oval);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000811 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000812}
813
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000815 // op + paint index + rect
816 uint32_t size = 2 * kUInt32Size + sizeof(rect);
817 uint32_t initialOffset = this->addDraw(DRAW_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000818 SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 addPaint(paint);
820 addRect(rect);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000821 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822}
823
reed@google.com4ed0fb72012-12-12 20:48:18 +0000824void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000825 uint32_t initialOffset, size;
reed@google.com4ed0fb72012-12-12 20:48:18 +0000826 if (rrect.isRect()) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000827 // op + paint index + rect
828 size = 2 * kUInt32Size + sizeof(SkRect);
829 initialOffset = this->addDraw(DRAW_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000830 SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000831 addPaint(paint);
832 addRect(rrect.getBounds());
833 } else if (rrect.isOval()) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000834 // op + paint index + rect
835 size = 2 * kUInt32Size + sizeof(SkRect);
836 initialOffset = this->addDraw(DRAW_OVAL, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000837 SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000838 addPaint(paint);
839 addRect(rrect.getBounds());
840 } else {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000841 // op + paint index + rrect
842 size = 2 * kUInt32Size + SkRRect::kSizeInMemory;
843 initialOffset = this->addDraw(DRAW_RRECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000844 SkASSERT(initialOffset+getPaintOffset(DRAW_RRECT, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000845 addPaint(paint);
846 addRRect(rrect);
847 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000848 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000849}
850
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000852 // op + paint index + path index
853 uint32_t size = 3 * kUInt32Size;
854 uint32_t initialOffset = this->addDraw(DRAW_PATH, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000855 SkASSERT(initialOffset+getPaintOffset(DRAW_PATH, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 addPaint(paint);
857 addPath(path);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000858 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859}
860
861void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
862 const SkPaint* paint = NULL) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000863 // op + paint index + bitmap index + left + top
864 uint32_t size = 3 * kUInt32Size + 2 * sizeof(SkScalar);
865 uint32_t initialOffset = this->addDraw(DRAW_BITMAP, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000866 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 addPaintPtr(paint);
868 addBitmap(bitmap);
869 addScalar(left);
870 addScalar(top);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000871 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872}
873
reed@google.com71121732012-09-18 15:14:33 +0000874void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 const SkRect& dst, const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000876 // id + paint index + bitmap index + bool for 'src'
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000877 uint32_t size = 4 * kUInt32Size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000878 if (NULL != src) {
879 size += sizeof(*src); // + rect
880 }
881 size += sizeof(dst); // + rect
882
883 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_RECT_TO_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000884 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_RECT_TO_RECT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885 addPaintPtr(paint);
886 addBitmap(bitmap);
reed@google.com71121732012-09-18 15:14:33 +0000887 addRectPtr(src); // may be null
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 addRect(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000889 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890}
891
892void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
reed@google.comf0b5e112011-09-07 11:57:34 +0000893 const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000894 // id + paint index + bitmap index + matrix index
895 uint32_t size = 4 * kUInt32Size;
896 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_MATRIX, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000897 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_MATRIX, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898 addPaintPtr(paint);
899 addBitmap(bitmap);
900 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000901 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902}
903
reed@google.comf0b5e112011-09-07 11:57:34 +0000904void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
905 const SkRect& dst, const SkPaint* paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000906 // op + paint index + bitmap id + center + dst rect
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000907 uint32_t size = 3 * kUInt32Size + sizeof(center) + sizeof(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000908 uint32_t initialOffset = this->addDraw(DRAW_BITMAP_NINE, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000909 SkASSERT(initialOffset+getPaintOffset(DRAW_BITMAP_NINE, size) == fWriter.size());
reed@google.comf0b5e112011-09-07 11:57:34 +0000910 addPaintPtr(paint);
911 addBitmap(bitmap);
912 addIRect(center);
913 addRect(dst);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000914 validate(initialOffset, size);
reed@google.comf0b5e112011-09-07 11:57:34 +0000915}
916
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
918 const SkPaint* paint = NULL) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000919 // op + paint index + bitmap index + left + top
920 uint32_t size = 5 * kUInt32Size;
921 uint32_t initialOffset = this->addDraw(DRAW_SPRITE, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000922 SkASSERT(initialOffset+getPaintOffset(DRAW_SPRITE, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 addPaintPtr(paint);
924 addBitmap(bitmap);
925 addInt(left);
926 addInt(top);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000927 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000928}
929
reed@google.com45954262012-12-07 17:14:40 +0000930// Return fontmetrics.fTop,fBottom in topbot[0,1], after they have been
931// tweaked by paint.computeFastBounds().
932//
933static void computeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 SkPaint::FontMetrics metrics;
935 paint.getFontMetrics(&metrics);
936 SkRect bounds;
937 // construct a rect so we can see any adjustments from the paint.
938 // we use 0,1 for left,right, just so the rect isn't empty
reed@google.com45954262012-12-07 17:14:40 +0000939 bounds.set(0, metrics.fTop, SK_Scalar1, metrics.fBottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940 (void)paint.computeFastBounds(bounds, &bounds);
reed@google.com45954262012-12-07 17:14:40 +0000941 topbot[0] = bounds.fTop;
942 topbot[1] = bounds.fBottom;
943}
944
junov@chromium.orgf3b12232013-01-22 17:50:47 +0000945void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData& flat,
reed@google.com45954262012-12-07 17:14:40 +0000946 SkScalar minY, SkScalar maxY) {
junov@chromium.org3f5ecd62013-01-22 18:01:26 +0000947 if (!flat.isTopBotWritten()) {
948 computeFontMetricsTopBottom(paint, flat.writableTopBot());
949 SkASSERT(flat.isTopBotWritten());
reed@google.com45954262012-12-07 17:14:40 +0000950 }
junov@chromium.org3f5ecd62013-01-22 18:01:26 +0000951 addScalar(flat.topBot()[0] + minY);
952 addScalar(flat.topBot()[1] + maxY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953}
954
reed@google.com82065d62011-02-07 15:30:46 +0000955void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 SkScalar y, const SkPaint& paint) {
reed@google.com2eb5bb12012-04-12 14:27:42 +0000957 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com82065d62011-02-07 15:30:46 +0000958
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000959 // op + paint index + length + 'length' worth of chars + x + y
960 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * sizeof(SkScalar);
961 if (fast) {
962 size += 2 * sizeof(SkScalar); // + top & bottom
963 }
964
robertphillips@google.come37ad352013-03-01 19:44:30 +0000965 DrawType op = fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT;
966 uint32_t initialOffset = this->addDraw(op, &size);
967 SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
junov@chromium.orgf3b12232013-01-22 17:50:47 +0000968 const SkFlatData* flatPaintData = addPaint(paint);
969 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 addText(text, byteLength);
971 addScalar(x);
972 addScalar(y);
973 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +0000974 addFontMetricsTopBottom(paint, *flatPaintData, y, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000976 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977}
978
reed@google.com82065d62011-02-07 15:30:46 +0000979void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 const SkPoint pos[], const SkPaint& paint) {
981 size_t points = paint.countText(text, byteLength);
982 if (0 == points)
983 return;
984
985 bool canUseDrawH = true;
reed@google.com9efd9a02012-01-30 15:41:43 +0000986 SkScalar minY = pos[0].fY;
987 SkScalar maxY = pos[0].fY;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 // check if the caller really should have used drawPosTextH()
989 {
990 const SkScalar firstY = pos[0].fY;
991 for (size_t index = 1; index < points; index++) {
992 if (pos[index].fY != firstY) {
993 canUseDrawH = false;
reed@google.com9efd9a02012-01-30 15:41:43 +0000994 if (pos[index].fY < minY) {
995 minY = pos[index].fY;
996 } else if (pos[index].fY > maxY) {
997 maxY = pos[index].fY;
998 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 }
1000 }
1001 }
reed@google.com82065d62011-02-07 15:30:46 +00001002
reed@google.com2eb5bb12012-04-12 14:27:42 +00001003 bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com9efd9a02012-01-30 15:41:43 +00001004 bool fast = canUseDrawH && fastBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001006 // op + paint index + length + 'length' worth of data + num points
1007 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1008 if (canUseDrawH) {
1009 if (fast) {
1010 size += 2 * sizeof(SkScalar); // + top & bottom
1011 }
1012 // + y-pos + actual x-point data
1013 size += sizeof(SkScalar) + points * sizeof(SkScalar);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 } else {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001015 // + x&y point data
1016 size += points * sizeof(SkPoint);
1017 if (fastBounds) {
1018 size += 2 * sizeof(SkScalar); // + top & bottom
1019 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001021
1022 DrawType op;
1023 if (fast) {
1024 op = DRAW_POS_TEXT_H_TOP_BOTTOM;
1025 } else if (canUseDrawH) {
1026 op = DRAW_POS_TEXT_H;
1027 } else if (fastBounds) {
1028 op = DRAW_POS_TEXT_TOP_BOTTOM;
1029 } else {
1030 op = DRAW_POS_TEXT;
1031 }
1032 uint32_t initialOffset = this->addDraw(op, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001033 SkASSERT(initialOffset+getPaintOffset(op, size) == fWriter.size());
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001034 const SkFlatData* flatPaintData = addPaint(paint);
1035 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036 addText(text, byteLength);
1037 addInt(points);
1038
1039#ifdef SK_DEBUG_SIZE
1040 size_t start = fWriter.size();
1041#endif
1042 if (canUseDrawH) {
1043 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001044 addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 }
1046 addScalar(pos[0].fY);
1047 SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
reed@google.com82065d62011-02-07 15:30:46 +00001048 for (size_t index = 0; index < points; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 *xptr++ = pos[index].fX;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001050 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 fWriter.writeMul4(pos, points * sizeof(SkPoint));
reed@google.com9efd9a02012-01-30 15:41:43 +00001052 if (fastBounds) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001053 addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY);
reed@google.com9efd9a02012-01-30 15:41:43 +00001054 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 }
1056#ifdef SK_DEBUG_SIZE
1057 fPointBytes += fWriter.size() - start;
1058 fPointWrites += points;
1059#endif
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001060 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061}
1062
1063void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
1064 const SkScalar xpos[], SkScalar constY,
1065 const SkPaint& paint) {
1066 size_t points = paint.countText(text, byteLength);
1067 if (0 == points)
1068 return;
reed@google.com82065d62011-02-07 15:30:46 +00001069
reed@google.com2eb5bb12012-04-12 14:27:42 +00001070 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001072 // op + paint index + length + 'length' worth of data + num points
1073 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 1 * kUInt32Size;
1074 if (fast) {
1075 size += 2 * sizeof(SkScalar); // + top & bottom
1076 }
1077 // + y + the actual points
1078 size += 1 * kUInt32Size + points * sizeof(SkScalar);
1079
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +00001080 uint32_t initialOffset = this->addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H,
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001081 &size);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001082 const SkFlatData* flatPaintData = addPaint(paint);
1083 SkASSERT(flatPaintData);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084 addText(text, byteLength);
1085 addInt(points);
reed@google.com82065d62011-02-07 15:30:46 +00001086
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087#ifdef SK_DEBUG_SIZE
1088 size_t start = fWriter.size();
1089#endif
1090 if (fast) {
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001091 addFontMetricsTopBottom(paint, *flatPaintData, constY, constY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 }
1093 addScalar(constY);
1094 fWriter.writeMul4(xpos, points * sizeof(SkScalar));
1095#ifdef SK_DEBUG_SIZE
1096 fPointBytes += fWriter.size() - start;
1097 fPointWrites += points;
1098#endif
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001099 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100}
1101
reed@google.com82065d62011-02-07 15:30:46 +00001102void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength,
1103 const SkPath& path, const SkMatrix* matrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001105 // op + paint index + length + 'length' worth of data + path index + matrix index
1106 uint32_t size = 3 * kUInt32Size + SkAlign4(byteLength) + 2 * kUInt32Size;
1107 uint32_t initialOffset = this->addDraw(DRAW_TEXT_ON_PATH, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001108 SkASSERT(initialOffset+getPaintOffset(DRAW_TEXT_ON_PATH, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 addPaint(paint);
1110 addText(text, byteLength);
1111 addPath(path);
1112 addMatrixPtr(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001113 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114}
1115
1116void SkPictureRecord::drawPicture(SkPicture& picture) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001117 // op + picture index
1118 uint32_t size = 2 * kUInt32Size;
1119 uint32_t initialOffset = this->addDraw(DRAW_PICTURE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120 addPicture(picture);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001121 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122}
1123
1124void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
1125 const SkPoint vertices[], const SkPoint texs[],
1126 const SkColor colors[], SkXfermode*,
1127 const uint16_t indices[], int indexCount,
1128 const SkPaint& paint) {
1129 uint32_t flags = 0;
1130 if (texs) {
1131 flags |= DRAW_VERTICES_HAS_TEXS;
1132 }
1133 if (colors) {
1134 flags |= DRAW_VERTICES_HAS_COLORS;
1135 }
1136 if (indexCount > 0) {
1137 flags |= DRAW_VERTICES_HAS_INDICES;
1138 }
1139
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001140 // op + paint index + flags + vmode + vCount + vertices
1141 uint32_t size = 5 * kUInt32Size + vertexCount * sizeof(SkPoint);
1142 if (flags & DRAW_VERTICES_HAS_TEXS) {
1143 size += vertexCount * sizeof(SkPoint); // + uvs
1144 }
1145 if (flags & DRAW_VERTICES_HAS_COLORS) {
1146 size += vertexCount * sizeof(SkColor); // + vert colors
1147 }
1148 if (flags & DRAW_VERTICES_HAS_INDICES) {
1149 // + num indices + indices
1150 size += 1 * kUInt32Size + SkAlign4(indexCount * sizeof(uint16_t));
1151 }
1152
1153 uint32_t initialOffset = this->addDraw(DRAW_VERTICES, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +00001154 SkASSERT(initialOffset+getPaintOffset(DRAW_VERTICES, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 addPaint(paint);
1156 addInt(flags);
1157 addInt(vmode);
1158 addInt(vertexCount);
1159 addPoints(vertices, vertexCount);
1160 if (flags & DRAW_VERTICES_HAS_TEXS) {
1161 addPoints(texs, vertexCount);
1162 }
1163 if (flags & DRAW_VERTICES_HAS_COLORS) {
1164 fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
1165 }
1166 if (flags & DRAW_VERTICES_HAS_INDICES) {
1167 addInt(indexCount);
1168 fWriter.writePad(indices, indexCount * sizeof(uint16_t));
1169 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001170 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171}
1172
reed@android.comcb608442009-12-04 21:32:27 +00001173void SkPictureRecord::drawData(const void* data, size_t length) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001174 // op + length + 'length' worth of data
1175 uint32_t size = 2 * kUInt32Size + SkAlign4(length);
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +00001176 uint32_t initialOffset = this->addDraw(DRAW_DATA, &size);
reed@android.comcb608442009-12-04 21:32:27 +00001177 addInt(length);
1178 fWriter.writePad(data, length);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001179 validate(initialOffset, size);
reed@android.comcb608442009-12-04 21:32:27 +00001180}
1181
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182///////////////////////////////////////////////////////////////////////////////
reed@google.com82065d62011-02-07 15:30:46 +00001183
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
scroggo@google.com4b90b112012-12-04 15:08:56 +00001185 const int index = fBitmapHeap->insert(bitmap);
1186 // In debug builds, a bad return value from insert() will crash, allowing for debugging. In
1187 // release builds, the invalid value will be recorded so that the reader will know that there
1188 // was a problem.
1189 SkASSERT(index != SkBitmapHeap::INVALID_SLOT);
1190 addInt(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191}
1192
1193void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
1194 addMatrixPtr(&matrix);
1195}
1196
1197void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
reed@google.com83ca3372012-07-12 15:27:54 +00001198 this->addInt(matrix ? fMatrices.find(*matrix) : 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199}
1200
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001201const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
1202 const SkFlatData* data = paint ? fPaints.findAndReturnFlat(*paint) : NULL;
1203 int index = data ? data->index() : 0;
reed@google.com45954262012-12-07 17:14:40 +00001204 this->addInt(index);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001205 return data;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206}
1207
1208void SkPictureRecord::addPath(const SkPath& path) {
1209 if (NULL == fPathHeap) {
1210 fPathHeap = SkNEW(SkPathHeap);
1211 }
1212 addInt(fPathHeap->append(path));
1213}
1214
1215void SkPictureRecord::addPicture(SkPicture& picture) {
1216 int index = fPictureRefs.find(&picture);
1217 if (index < 0) { // not found
1218 index = fPictureRefs.count();
1219 *fPictureRefs.append() = &picture;
1220 picture.ref();
1221 }
1222 // follow the convention of recording a 1-based index
1223 addInt(index + 1);
1224}
1225
1226void SkPictureRecord::addPoint(const SkPoint& point) {
1227#ifdef SK_DEBUG_SIZE
1228 size_t start = fWriter.size();
1229#endif
1230 fWriter.writePoint(point);
1231#ifdef SK_DEBUG_SIZE
1232 fPointBytes += fWriter.size() - start;
1233 fPointWrites++;
1234#endif
1235}
reed@google.com82065d62011-02-07 15:30:46 +00001236
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
1238 fWriter.writeMul4(pts, count * sizeof(SkPoint));
1239#ifdef SK_DEBUG_SIZE
1240 fPointBytes += count * sizeof(SkPoint);
1241 fPointWrites++;
1242#endif
1243}
1244
1245void SkPictureRecord::addRect(const SkRect& rect) {
1246#ifdef SK_DEBUG_SIZE
1247 size_t start = fWriter.size();
1248#endif
1249 fWriter.writeRect(rect);
1250#ifdef SK_DEBUG_SIZE
1251 fRectBytes += fWriter.size() - start;
1252 fRectWrites++;
1253#endif
1254}
1255
1256void SkPictureRecord::addRectPtr(const SkRect* rect) {
1257 if (fWriter.writeBool(rect != NULL)) {
1258 fWriter.writeRect(*rect);
1259 }
1260}
1261
reed@google.comf0b5e112011-09-07 11:57:34 +00001262void SkPictureRecord::addIRect(const SkIRect& rect) {
1263 fWriter.write(&rect, sizeof(rect));
1264}
1265
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
1267 if (fWriter.writeBool(rect != NULL)) {
1268 *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
1269 }
1270}
1271
reed@google.com4ed0fb72012-12-12 20:48:18 +00001272void SkPictureRecord::addRRect(const SkRRect& rrect) {
1273 fWriter.writeRRect(rrect);
1274}
1275
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276void SkPictureRecord::addRegion(const SkRegion& region) {
reed@google.com83ca3372012-07-12 15:27:54 +00001277 addInt(fRegions.find(region));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278}
1279
1280void SkPictureRecord::addText(const void* text, size_t byteLength) {
1281#ifdef SK_DEBUG_SIZE
1282 size_t start = fWriter.size();
1283#endif
1284 addInt(byteLength);
1285 fWriter.writePad(text, byteLength);
1286#ifdef SK_DEBUG_SIZE
1287 fTextBytes += fWriter.size() - start;
1288 fTextWrites++;
1289#endif
1290}
1291
1292///////////////////////////////////////////////////////////////////////////////
1293
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294#ifdef SK_DEBUG_SIZE
1295size_t SkPictureRecord::size() const {
1296 size_t result = 0;
1297 size_t sizeData;
1298 bitmaps(&sizeData);
1299 result += sizeData;
1300 matrices(&sizeData);
1301 result += sizeData;
1302 paints(&sizeData);
1303 result += sizeData;
1304 paths(&sizeData);
1305 result += sizeData;
1306 pictures(&sizeData);
1307 result += sizeData;
1308 regions(&sizeData);
1309 result += sizeData;
1310 result += streamlen();
1311 return result;
1312}
1313
1314int SkPictureRecord::bitmaps(size_t* size) const {
1315 size_t result = 0;
1316 int count = fBitmaps.count();
reed@google.com82065d62011-02-07 15:30:46 +00001317 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318 result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
1319 *size = result;
1320 return count;
1321}
1322
1323int SkPictureRecord::matrices(size_t* size) const {
1324 int count = fMatrices.count();
1325 *size = sizeof(fMatrices[0]) * count;
1326 return count;
1327}
1328
1329int SkPictureRecord::paints(size_t* size) const {
1330 size_t result = 0;
1331 int count = fPaints.count();
reed@google.com82065d62011-02-07 15:30:46 +00001332 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 result += sizeof(fPaints[index]) + fPaints[index]->size();
1334 *size = result;
1335 return count;
1336}
1337
1338int SkPictureRecord::paths(size_t* size) const {
1339 size_t result = 0;
1340 int count = fPaths.count();
reed@google.com82065d62011-02-07 15:30:46 +00001341 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342 result += sizeof(fPaths[index]) + fPaths[index]->size();
1343 *size = result;
1344 return count;
1345}
1346
1347int SkPictureRecord::regions(size_t* size) const {
1348 size_t result = 0;
1349 int count = fRegions.count();
reed@google.com82065d62011-02-07 15:30:46 +00001350 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351 result += sizeof(fRegions[index]) + fRegions[index]->size();
1352 *size = result;
1353 return count;
1354}
1355
1356size_t SkPictureRecord::streamlen() const {
1357 return fWriter.size();
1358}
1359#endif
1360
1361#ifdef SK_DEBUG_VALIDATE
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001362void SkPictureRecord::validate(uint32_t initialOffset, uint32_t size) const {
1363 SkASSERT(fWriter.size() == initialOffset + size);
1364
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365 validateBitmaps();
1366 validateMatrices();
1367 validatePaints();
1368 validatePaths();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001369 validateRegions();
1370}
1371
1372void SkPictureRecord::validateBitmaps() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001373 int count = fBitmapHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374 SkASSERT((unsigned) count < 0x1000);
1375 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001376 const SkBitmap* bitPtr = fBitmapHeap->getBitmap(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 SkASSERT(bitPtr);
1378 bitPtr->validate();
1379 }
1380}
1381
1382void SkPictureRecord::validateMatrices() const {
1383 int count = fMatrices.count();
1384 SkASSERT((unsigned) count < 0x1000);
1385 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001386 const SkFlatData* matrix = fMatrices[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387 SkASSERT(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001388// matrix->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389 }
1390}
1391
1392void SkPictureRecord::validatePaints() const {
1393 int count = fPaints.count();
1394 SkASSERT((unsigned) count < 0x1000);
1395 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001396 const SkFlatData* paint = fPaints[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 SkASSERT(paint);
1398// paint->validate();
1399 }
1400}
1401
1402void SkPictureRecord::validatePaths() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001403 if (NULL == fPathHeap) {
1404 return;
1405 }
1406
1407 int count = fPathHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 SkASSERT((unsigned) count < 0x1000);
1409 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001410 const SkPath& path = (*fPathHeap)[index];
1411 path.validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 }
1413}
1414
1415void SkPictureRecord::validateRegions() const {
1416 int count = fRegions.count();
1417 SkASSERT((unsigned) count < 0x1000);
1418 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001419 const SkFlatData* region = fRegions[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420 SkASSERT(region);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001421// region->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 }
1423}
1424#endif