blob: 915736373d0aba5df1ac3116a09607984a006811 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkPictureRecord.h"
9#include "SkTSearch.h"
junov@chromium.org4866cc02012-06-01 21:23:07 +000010#include "SkPixelRef.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000011#include "SkRRect.h"
rileya@google.com9f5898d2012-09-11 20:21:44 +000012#include "SkBBoxHierarchy.h"
junov@chromium.orgd575eed2013-05-08 15:39:13 +000013#include "SkDevice.h"
rileya@google.com9f5898d2012-09-11 20:21:44 +000014#include "SkPictureStateTree.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015
16#define MIN_WRITER_SIZE 16384
17#define HEAP_BLOCK_SIZE 4096
18
junov@chromium.org4e6dfa52012-07-16 14:04:59 +000019enum {
reed@google.comd86e7ab2012-09-27 20:31:31 +000020 // just need a value that save or getSaveCount would never return
junov@chromium.org4e6dfa52012-07-16 14:04:59 +000021 kNoInitialSave = -1,
22};
23
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +000024// A lot of basic types get stored as a uint32_t: bools, ints, paint indices, etc.
25static int const kUInt32Size = 4;
26
robertphillips@google.come37ad352013-03-01 19:44:30 +000027static const uint32_t kSaveLayerNoBoundsSize = 4 * kUInt32Size;
28static const uint32_t kSaveLayerWithBoundsSize = 4 * kUInt32Size + sizeof(SkRect);
29
reed@google.comd86e7ab2012-09-27 20:31:31 +000030SkPictureRecord::SkPictureRecord(uint32_t flags, SkDevice* device) :
31 INHERITED(device),
robertphillips@google.com178a2672012-09-13 13:25:30 +000032 fBoundingHierarchy(NULL),
33 fStateTree(NULL),
djsollen@google.com21830d92012-08-07 19:49:41 +000034 fFlattenableHeap(HEAP_BLOCK_SIZE),
35 fMatrices(&fFlattenableHeap),
36 fPaints(&fFlattenableHeap),
37 fRegions(&fFlattenableHeap),
djsollen@google.comd2700ee2012-05-30 16:54:13 +000038 fWriter(MIN_WRITER_SIZE),
39 fRecordFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000040#ifdef SK_DEBUG_SIZE
41 fPointBytes = fRectBytes = fTextBytes = 0;
42 fPointWrites = fRectWrites = fTextWrites = 0;
43#endif
44
45 fRestoreOffsetStack.setReserve(32);
reed@google.com82065d62011-02-07 15:30:46 +000046
djsollen@google.comc9ab9872012-08-29 18:52:07 +000047 fBitmapHeap = SkNEW(SkBitmapHeap);
48 fFlattenableHeap.setBitmapStorage(fBitmapHeap);
reed@android.com8a1c16f2008-12-17 15:59:43 +000049 fPathHeap = NULL; // lazy allocate
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +000050 fFirstSavedLayerIndex = kNoSavedLayerIndex;
reed@google.comd86e7ab2012-09-27 20:31:31 +000051
52 fInitialSaveCount = kNoInitialSave;
reed@android.com8a1c16f2008-12-17 15:59:43 +000053}
54
55SkPictureRecord::~SkPictureRecord() {
djsollen@google.comc9ab9872012-08-29 18:52:07 +000056 SkSafeUnref(fBitmapHeap);
djsollen@google.com21830d92012-08-07 19:49:41 +000057 SkSafeUnref(fPathHeap);
rileya@google.com9f5898d2012-09-11 20:21:44 +000058 SkSafeUnref(fBoundingHierarchy);
59 SkSafeUnref(fStateTree);
djsollen@google.com21830d92012-08-07 19:49:41 +000060 fFlattenableHeap.setBitmapStorage(NULL);
61 fPictureRefs.unrefAll();
reed@android.com8a1c16f2008-12-17 15:59:43 +000062}
63
64///////////////////////////////////////////////////////////////////////////////
65
robertphillips@google.come37ad352013-03-01 19:44:30 +000066// Return the offset of the paint inside a given op's byte stream. A zero
67// return value means there is no paint (and you really shouldn't be calling
68// this method)
69static inline uint32_t getPaintOffset(DrawType op, uint32_t opSize) {
70 // These offsets are where the paint would be if the op size doesn't overflow
skia.committer@gmail.comf140f182013-03-02 07:01:56 +000071 static const uint8_t gPaintOffsets[LAST_DRAWTYPE_ENUM + 1] = {
robertphillips@google.come37ad352013-03-01 19:44:30 +000072 0, // UNUSED - no paint
73 0, // CLIP_PATH - no paint
74 0, // CLIP_REGION - no paint
75 0, // CLIP_RECT - no paint
76 0, // CLIP_RRECT - no paint
77 0, // CONCAT - no paint
78 1, // DRAW_BITMAP - right after op code
79 1, // DRAW_BITMAP_MATRIX - right after op code
80 1, // DRAW_BITMAP_NINE - right after op code
81 1, // DRAW_BITMAP_RECT_TO_RECT - right after op code
82 0, // DRAW_CLEAR - no paint
83 0, // DRAW_DATA - no paint
84 1, // DRAW_OVAL - right after op code
85 1, // DRAW_PAINT - right after op code
86 1, // DRAW_PATH - right after op code
87 0, // DRAW_PICTURE - no paint
88 1, // DRAW_POINTS - right after op code
89 1, // DRAW_POS_TEXT - right after op code
90 1, // DRAW_POS_TEXT_TOP_BOTTOM - right after op code
91 1, // DRAW_POS_TEXT_H - right after op code
92 1, // DRAW_POS_TEXT_H_TOP_BOTTOM - right after op code
93 1, // DRAW_RECT - right after op code
94 1, // DRAW_RRECT - right after op code
95 1, // DRAW_SPRITE - right after op code
96 1, // DRAW_TEXT - right after op code
97 1, // DRAW_TEXT_ON_PATH - right after op code
98 1, // DRAW_TEXT_TOP_BOTTOM - right after op code
99 1, // DRAW_VERTICES - right after op code
100 0, // RESTORE - no paint
101 0, // ROTATE - no paint
102 0, // SAVE - no paint
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000103 0, // SAVE_LAYER - see below - this paint's location varies
robertphillips@google.come37ad352013-03-01 19:44:30 +0000104 0, // SCALE - no paint
105 0, // SET_MATRIX - no paint
106 0, // SKEW - no paint
107 0, // TRANSLATE - no paint
108 0, // NOOP - no paint
robertphillips@google.com0a4805e2013-05-29 13:24:23 +0000109 0, // BEGIN_GROUP - no paint
110 0, // COMMENT - no paint
111 0, // END_GROUP - no paint
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000112 };
robertphillips@google.come37ad352013-03-01 19:44:30 +0000113
114 SkASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1);
115 SkASSERT((unsigned)op <= (unsigned)LAST_DRAWTYPE_ENUM);
116
117 int overflow = 0;
118 if (0 != (opSize & ~MASK_24) || opSize == MASK_24) {
119 // This op's size overflows so an extra uint32_t will be written
120 // after the op code
121 overflow = sizeof(uint32_t);
122 }
123
124 if (SAVE_LAYER == op) {
125 static const uint32_t kSaveLayerNoBoundsPaintOffset = 2 * kUInt32Size;
126 static const uint32_t kSaveLayerWithBoundsPaintOffset = 2 * kUInt32Size + sizeof(SkRect);
127
128 if (kSaveLayerNoBoundsSize == opSize) {
129 return kSaveLayerNoBoundsPaintOffset + overflow;
130 } else {
131 SkASSERT(kSaveLayerWithBoundsSize == opSize);
132 return kSaveLayerWithBoundsPaintOffset + overflow;
133 }
134 }
135
136 SkASSERT(0 != gPaintOffsets[op]); // really shouldn't be calling this method
137 return gPaintOffsets[op] * sizeof(uint32_t) + overflow;
138}
139
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000140SkDevice* SkPictureRecord::setDevice(SkDevice* device) {
reed@google.comd86e7ab2012-09-27 20:31:31 +0000141 SkASSERT(!"eeek, don't try to change the device on a recording canvas");
142 return this->INHERITED::setDevice(device);
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000143}
144
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145int SkPictureRecord::save(SaveFlags flags) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000146 // record the offset to us, making it non-positive to distinguish a save
147 // from a clip entry.
148 fRestoreOffsetStack.push(-(int32_t)fWriter.size());
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000149
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000150 // op + flags
151 uint32_t size = 2 * kUInt32Size;
152 uint32_t initialOffset = this->addDraw(SAVE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 addInt(flags);
reed@google.com82065d62011-02-07 15:30:46 +0000154
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000155 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 return this->INHERITED::save(flags);
157}
158
159int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
160 SaveFlags flags) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000161 // record the offset to us, making it non-positive to distinguish a save
162 // from a clip entry.
163 fRestoreOffsetStack.push(-(int32_t)fWriter.size());
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000164
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000165 // op + bool for 'bounds'
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000166 uint32_t size = 2 * kUInt32Size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000167 if (NULL != bounds) {
168 size += sizeof(*bounds); // + rect
169 }
170 // + paint index + flags
171 size += 2 * kUInt32Size;
172
robertphillips@google.come37ad352013-03-01 19:44:30 +0000173 SkASSERT(kSaveLayerNoBoundsSize == size || kSaveLayerWithBoundsSize == size);
174
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000175 uint32_t initialOffset = this->addDraw(SAVE_LAYER, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 addRectPtr(bounds);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000177 SkASSERT(initialOffset+getPaintOffset(SAVE_LAYER, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 addPaintPtr(paint);
179 addInt(flags);
180
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000181 if (kNoSavedLayerIndex == fFirstSavedLayerIndex) {
182 fFirstSavedLayerIndex = fRestoreOffsetStack.count();
183 }
184
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000185 validate(initialOffset, size);
reed@android.com261ae4d2009-10-02 16:37:46 +0000186 /* Don't actually call saveLayer, because that will try to allocate an
187 offscreen device (potentially very big) which we don't actually need
188 at this time (and may not be able to afford since during record our
189 clip starts out the size of the picture, which is often much larger
190 than the size of the actual device we'll use during playback).
191 */
junov@chromium.orga907ac32012-02-24 21:54:07 +0000192 int count = this->INHERITED::save(flags);
193 this->clipRectBounds(bounds, flags, NULL);
194 return count;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195}
196
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000197bool SkPictureRecord::isDrawingToLayer() const {
198 return fFirstSavedLayerIndex != kNoSavedLayerIndex;
199}
200
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000201/*
202 * Read the op code from 'offset' in 'writer' and extract the size too.
203 */
204static DrawType peek_op_and_size(SkWriter32* writer, int32_t offset, uint32_t* size) {
205 uint32_t* peek = writer->peek32(offset);
reed@google.comffacd3c2012-08-30 15:31:23 +0000206
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000207 uint32_t op;
208 UNPACK_8_24(*peek, op, *size);
209 if (MASK_24 == *size) {
210 // size required its own slot right after the op code
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000211 *size = *writer->peek32(offset+kUInt32Size);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000212 }
213 return (DrawType) op;
reed@google.comffacd3c2012-08-30 15:31:23 +0000214}
215
216#ifdef TRACK_COLLAPSE_STATS
217 static int gCollapseCount, gCollapseCalls;
218#endif
219
robertphillips@google.come37ad352013-03-01 19:44:30 +0000220// Is the supplied paint simply a color?
221static bool is_simple(const SkPaint& p) {
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000222 intptr_t orAccum = (intptr_t)p.getPathEffect() |
robertphillips@google.come37ad352013-03-01 19:44:30 +0000223 (intptr_t)p.getShader() |
224 (intptr_t)p.getXfermode() |
225 (intptr_t)p.getMaskFilter() |
226 (intptr_t)p.getColorFilter() |
227 (intptr_t)p.getRasterizer() |
228 (intptr_t)p.getLooper() |
229 (intptr_t)p.getImageFilter();
230 return 0 == orAccum;
231}
232
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000233// CommandInfos are fed to the 'match' method and filled in with command
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000234// information.
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000235struct CommandInfo {
236 DrawType fActualOp;
237 uint32_t fOffset;
238 uint32_t fSize;
239};
240
reed@google.comffacd3c2012-08-30 15:31:23 +0000241/*
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000242 * Attempt to match the provided pattern of commands starting at 'offset'
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000243 * in the byte stream and stopping at the end of the stream. Upon success,
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000244 * return true with all the pattern information filled out in the result
245 * array (i.e., actual ops, offsets and sizes).
246 * Note this method skips any NOOPs seen in the stream
247 */
248static bool match(SkWriter32* writer, uint32_t offset,
249 int* pattern, CommandInfo* result, int numCommands) {
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000250 SkASSERT(offset < writer->size());
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000251
252 uint32_t curOffset = offset;
253 uint32_t curSize = 0;
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000254 int numMatched;
255 for (numMatched = 0; numMatched < numCommands && curOffset < writer->size(); ++numMatched) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000256 DrawType op = peek_op_and_size(writer, curOffset, &curSize);
257 while (NOOP == op && curOffset < writer->size()) {
258 curOffset += curSize;
259 op = peek_op_and_size(writer, curOffset, &curSize);
260 }
261
262 if (curOffset >= writer->size()) {
263 return false; // ran out of byte stream
264 }
265
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000266 if (kDRAW_BITMAP_FLAVOR == pattern[numMatched]) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000267 if (DRAW_BITMAP != op && DRAW_BITMAP_MATRIX != op &&
268 DRAW_BITMAP_NINE != op && DRAW_BITMAP_RECT_TO_RECT != op) {
269 return false;
270 }
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000271 } else if (op != pattern[numMatched]) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000272 return false;
273 }
274
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000275 result[numMatched].fActualOp = op;
276 result[numMatched].fOffset = curOffset;
277 result[numMatched].fSize = curSize;
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000278
279 curOffset += curSize;
280 }
281
robertphillips@google.comc04987f2013-03-12 17:53:53 +0000282 if (numMatched != numCommands) {
283 return false;
284 }
285
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000286 curOffset += curSize;
287 if (curOffset < writer->size()) {
288 // Something else between the last command and the end of the stream
289 return false;
290 }
291
292 return true;
293}
294
295// temporarily here to make code review easier
296static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
297 SkPaintDictionary* paintDict,
298 const CommandInfo& saveLayerInfo,
299 const CommandInfo& dbmInfo);
300
301/*
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000302 * Restore has just been called (but not recorded), look back at the
robertphillips@google.come37ad352013-03-01 19:44:30 +0000303 * matching save* and see if we are in the configuration:
304 * SAVE_LAYER
305 * DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
306 * RESTORE
307 * where the saveLayer's color can be moved into the drawBitmap*'s paint
308 */
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000309static bool remove_save_layer1(SkWriter32* writer, int32_t offset,
robertphillips@google.come37ad352013-03-01 19:44:30 +0000310 SkPaintDictionary* paintDict) {
robertphillips@google.come37ad352013-03-01 19:44:30 +0000311 // back up to the save block
312 // TODO: add a stack to track save*/restore offsets rather than searching backwards
313 while (offset > 0) {
314 offset = *writer->peek32(offset);
315 }
316
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000317 int pattern[] = { SAVE_LAYER, kDRAW_BITMAP_FLAVOR, /* RESTORE */ };
318 CommandInfo result[SK_ARRAY_COUNT(pattern)];
319
320 if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
321 return false;
robertphillips@google.come37ad352013-03-01 19:44:30 +0000322 }
323
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000324 if (kSaveLayerWithBoundsSize == result[0].fSize) {
robertphillips@google.come37ad352013-03-01 19:44:30 +0000325 // The saveLayer's bound can offset where the dbm is drawn
326 return false;
327 }
328
robertphillips@google.come37ad352013-03-01 19:44:30 +0000329
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000330 return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
331 result[0], result[1]);
332}
robertphillips@google.come37ad352013-03-01 19:44:30 +0000333
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000334/*
335 * Convert the command code located at 'offset' to a NOOP. Leave the size
336 * field alone so the NOOP can be skipped later.
337 */
338static void convert_command_to_noop(SkWriter32* writer, uint32_t offset) {
339 uint32_t* ptr = writer->peek32(offset);
340 *ptr = (*ptr & MASK_24) | (NOOP << 24);
341}
robertphillips@google.come37ad352013-03-01 19:44:30 +0000342
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000343/*
344 * Attempt to merge the saveLayer's paint into the drawBitmap*'s paint.
345 * Return true on success; false otherwise.
346 */
347static bool merge_savelayer_paint_into_drawbitmp(SkWriter32* writer,
348 SkPaintDictionary* paintDict,
349 const CommandInfo& saveLayerInfo,
350 const CommandInfo& dbmInfo) {
351 SkASSERT(SAVE_LAYER == saveLayerInfo.fActualOp);
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000352 SkASSERT(DRAW_BITMAP == dbmInfo.fActualOp ||
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000353 DRAW_BITMAP_MATRIX == dbmInfo.fActualOp ||
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000354 DRAW_BITMAP_NINE == dbmInfo.fActualOp ||
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000355 DRAW_BITMAP_RECT_TO_RECT == dbmInfo.fActualOp);
356
357 uint32_t dbmPaintOffset = getPaintOffset(dbmInfo.fActualOp, dbmInfo.fSize);
358 uint32_t slPaintOffset = getPaintOffset(SAVE_LAYER, saveLayerInfo.fSize);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000359
360 // we have a match, now we need to get the paints involved
robertphillips@google.com5ba0d902013-03-12 16:05:14 +0000361 uint32_t dbmPaintId = *writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
362 uint32_t saveLayerPaintId = *writer->peek32(saveLayerInfo.fOffset+slPaintOffset);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000363
364 if (0 == saveLayerPaintId) {
365 // In this case the saveLayer/restore isn't needed at all - just kill the saveLayer
366 // and signal the caller (by returning true) to not add the RESTORE op
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000367 convert_command_to_noop(writer, saveLayerInfo.fOffset);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000368 return true;
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000369 }
370
robertphillips@google.come37ad352013-03-01 19:44:30 +0000371 if (0 == dbmPaintId) {
372 // In this case just make the DBM* use the saveLayer's paint, kill the saveLayer
373 // and signal the caller (by returning true) to not add the RESTORE op
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000374 convert_command_to_noop(writer, saveLayerInfo.fOffset);
375 uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000376 SkASSERT(0 == *ptr);
377 *ptr = saveLayerPaintId;
378 return true;
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000379 }
robertphillips@google.come37ad352013-03-01 19:44:30 +0000380
381 SkAutoTDelete<SkPaint> saveLayerPaint(paintDict->unflatten(saveLayerPaintId));
382 if (NULL == saveLayerPaint.get() || !is_simple(*saveLayerPaint)) {
383 return false;
384 }
385
386 // For this optimization we only fold the saveLayer and drawBitmapRect
387 // together if the saveLayer's draw is simple (i.e., no fancy effects) and
388 // and the only difference in the colors is that the saveLayer's can have
389 // an alpha while the drawBitmapRect's is opaque.
390 // TODO: it should be possible to fold them together even if they both
391 // have different non-255 alphas
392 SkColor layerColor = saveLayerPaint->getColor() | 0xFF000000; // force opaque
393
394 SkAutoTDelete<SkPaint> dbmPaint(paintDict->unflatten(dbmPaintId));
395 if (NULL == dbmPaint.get() || dbmPaint->getColor() != layerColor) {
396 return false;
397 }
skia.committer@gmail.comf140f182013-03-02 07:01:56 +0000398
robertphillips@google.come37ad352013-03-01 19:44:30 +0000399 SkColor newColor = SkColorSetA(dbmPaint->getColor(),
400 SkColorGetA(saveLayerPaint->getColor()));
401 dbmPaint->setColor(newColor);
402
403 const SkFlatData* data = paintDict->findAndReturnFlat(*dbmPaint);
404 if (NULL == data) {
405 return false;
406 }
407
408 // kill the saveLayer and alter the DBMR2R's paint to be the modified one
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000409 convert_command_to_noop(writer, saveLayerInfo.fOffset);
410 uint32_t* ptr = writer->peek32(dbmInfo.fOffset+dbmPaintOffset);
411 SkASSERT(dbmPaintId == *ptr);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000412 *ptr = data->index();
413 return true;
414}
415
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000416/*
skia.committer@gmail.com91274b92013-03-13 07:01:04 +0000417 * Restore has just been called (but not recorded), look back at the
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000418 * matching save* and see if we are in the configuration:
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000419 * SAVE_LAYER (with NULL == bounds)
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000420 * SAVE
421 * CLIP_RECT
422 * DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT
423 * RESTORE
424 * RESTORE
425 * where the saveLayer's color can be moved into the drawBitmap*'s paint
426 */
427static bool remove_save_layer2(SkWriter32* writer, int32_t offset,
428 SkPaintDictionary* paintDict) {
429
430 // back up to the save block
431 // TODO: add a stack to track save*/restore offsets rather than searching backwards
432 while (offset > 0) {
433 offset = *writer->peek32(offset);
434 }
435
436 int pattern[] = { SAVE_LAYER, SAVE, CLIP_RECT, kDRAW_BITMAP_FLAVOR, RESTORE, /* RESTORE */ };
437 CommandInfo result[SK_ARRAY_COUNT(pattern)];
438
439 if (!match(writer, -offset, pattern, result, SK_ARRAY_COUNT(pattern))) {
440 return false;
441 }
442
443 if (kSaveLayerWithBoundsSize == result[0].fSize) {
444 // The saveLayer's bound can offset where the dbm is drawn
445 return false;
446 }
447
448 return merge_savelayer_paint_into_drawbitmp(writer, paintDict,
449 result[0], result[3]);
450}
robertphillips@google.come37ad352013-03-01 19:44:30 +0000451
452/*
453 * Restore has just been called (but not recorded), so look back at the
reed@google.comffacd3c2012-08-30 15:31:23 +0000454 * matching save(), and see if we can eliminate the pair of them, due to no
455 * intervening matrix/clip calls.
456 *
457 * If so, update the writer and return true, in which case we won't even record
458 * the restore() call. If we still need the restore(), return false.
459 */
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000460static bool collapse_save_clip_restore(SkWriter32* writer, int32_t offset,
461 SkPaintDictionary* paintDict) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000462#ifdef TRACK_COLLAPSE_STATS
463 gCollapseCalls += 1;
464#endif
465
466 int32_t restoreOffset = (int32_t)writer->size();
467
468 // back up to the save block
469 while (offset > 0) {
470 offset = *writer->peek32(offset);
471 }
472
473 // now offset points to a save
474 offset = -offset;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000475 uint32_t opSize;
476 DrawType op = peek_op_and_size(writer, offset, &opSize);
477 if (SAVE_LAYER == op) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000478 // not ready to cull these out yet (mrr)
479 return false;
480 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000481 SkASSERT(SAVE == op);
reed@google.comffacd3c2012-08-30 15:31:23 +0000482
483 // Walk forward until we get back to either a draw-verb (abort) or we hit
484 // our restore (success).
485 int32_t saveOffset = offset;
486
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000487 offset += opSize;
reed@google.comffacd3c2012-08-30 15:31:23 +0000488 while (offset < restoreOffset) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000489 op = peek_op_and_size(writer, offset, &opSize);
490 if ((op > CONCAT && op < ROTATE) || (SAVE_LAYER == op)) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000491 // drawing verb, abort
492 return false;
493 }
494 offset += opSize;
495 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000496
reed@google.comffacd3c2012-08-30 15:31:23 +0000497#ifdef TRACK_COLLAPSE_STATS
498 gCollapseCount += 1;
499 SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls,
500 (double)gCollapseCount / gCollapseCalls, "%");
501#endif
502
503 writer->rewindToOffset(saveOffset);
504 return true;
505}
506
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000507typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
508 SkPaintDictionary* paintDict);
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000509enum PictureRecordOptType {
510 kRewind_OptType, // Optimization rewinds the command stream
511 kCollapseSaveLayer_OptType, // Optimization eliminates a save/restore pair
512};
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000513
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000514struct PictureRecordOpt {
515 PictureRecordOptProc fProc;
516 PictureRecordOptType fType;
517};
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000518/*
519 * A list of the optimizations that are tried upon seeing a restore
520 * TODO: add a real API for such optimizations
521 * Add the ability to fire optimizations on any op (not just RESTORE)
522 */
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000523static const PictureRecordOpt gPictureRecordOpts[] = {
524 { collapse_save_clip_restore, kRewind_OptType },
525 { remove_save_layer1, kCollapseSaveLayer_OptType },
526 { remove_save_layer2, kCollapseSaveLayer_OptType }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000527};
528
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000529// This is called after an optimization has been applied to the command stream
530// in order to adjust the contents and state of the bounding box hierarchy and
531// state tree to reflect the optimization.
532static void apply_optimization_to_bbh(PictureRecordOptType opt, SkPictureStateTree* stateTree,
533 SkBBoxHierarchy* boundingHierarchy) {
534 switch (opt) {
535 case kCollapseSaveLayer_OptType:
robertphillips@google.com35e15632013-03-15 16:49:34 +0000536 if (NULL != stateTree) {
537 stateTree->saveCollapsed();
538 }
commit-bot@chromium.org4b32bd52013-03-15 15:06:03 +0000539 break;
540 case kRewind_OptType:
541 if (NULL != boundingHierarchy) {
542 boundingHierarchy->rewindInserts();
543 }
544 // Note: No need to touch the state tree for this to work correctly.
545 // Unused branches do not burden the playback, and pruning the tree
546 // would be O(N^2), so it is best to leave it alone.
547 break;
548 default:
549 SkASSERT(0);
550 }
551}
552
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553void SkPictureRecord::restore() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000554 // FIXME: SkDeferredCanvas needs to be refactored to respect
555 // save/restore balancing so that the following test can be
556 // turned on permanently.
557#if 0
558 SkASSERT(fRestoreOffsetStack.count() > 1);
559#endif
560
reed@android.comb4e22d62009-07-09 15:20:25 +0000561 // check for underflow
562 if (fRestoreOffsetStack.count() == 0) {
563 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 }
reed@android.comb4e22d62009-07-09 15:20:25 +0000565
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000566 if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
567 fFirstSavedLayerIndex = kNoSavedLayerIndex;
568 }
569
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000570 uint32_t initialOffset, size;
robertphillips@google.com31d81912013-04-12 15:24:29 +0000571 size_t opt = 0;
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000572 if (!(fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag)) {
573 for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
574 if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
575 // Some optimization fired so don't add the RESTORE
576 size = 0;
577 initialOffset = fWriter.size();
578 apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
579 fStateTree, fBoundingHierarchy);
580 break;
581 }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000582 }
skia.committer@gmail.com4bb50b22013-04-13 07:01:15 +0000583 }
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000584
robertphillips@google.comad7d4812013-04-12 15:13:35 +0000585 if ((fRecordFlags & SkPicture::kDisableRecordOptimizations_RecordingFlag) ||
586 SK_ARRAY_COUNT(gPictureRecordOpts) == opt) {
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000587 // No optimization fired so add the RESTORE
reed@google.comffacd3c2012-08-30 15:31:23 +0000588 fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
robertphillips@google.comb8f96102013-03-12 15:39:44 +0000589 size = 1 * kUInt32Size; // RESTORE consists solely of 1 op code
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000590 initialOffset = this->addDraw(RESTORE, &size);
reed@google.comffacd3c2012-08-30 15:31:23 +0000591 }
592
reed@android.comb4e22d62009-07-09 15:20:25 +0000593 fRestoreOffsetStack.pop();
reed@android.com32a42492009-07-10 03:33:52 +0000594
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000595 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 return this->INHERITED::restore();
597}
598
599bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000600 // op + dx + dy
601 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
602 uint32_t initialOffset = this->addDraw(TRANSLATE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 addScalar(dx);
604 addScalar(dy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000605 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 return this->INHERITED::translate(dx, dy);
607}
608
609bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000610 // op + sx + sy
611 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
612 uint32_t initialOffset = this->addDraw(SCALE, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 addScalar(sx);
614 addScalar(sy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000615 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 return this->INHERITED::scale(sx, sy);
617}
618
619bool SkPictureRecord::rotate(SkScalar degrees) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000620 // op + degrees
621 uint32_t size = 1 * kUInt32Size + sizeof(SkScalar);
622 uint32_t initialOffset = this->addDraw(ROTATE, &size);
reed@google.com82065d62011-02-07 15:30:46 +0000623 addScalar(degrees);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000624 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 return this->INHERITED::rotate(degrees);
626}
627
628bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000629 // op + sx + sy
630 uint32_t size = 1 * kUInt32Size + 2 * sizeof(SkScalar);
631 uint32_t initialOffset = this->addDraw(SKEW, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 addScalar(sx);
633 addScalar(sy);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000634 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000635 return this->INHERITED::skew(sx, sy);
636}
637
638bool SkPictureRecord::concat(const SkMatrix& matrix) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000639 validate(fWriter.size(), 0);
640 // op + matrix index
641 uint32_t size = 2 * kUInt32Size;
642 uint32_t initialOffset = this->addDraw(CONCAT, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000644 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 return this->INHERITED::concat(matrix);
646}
647
reed@android.com6e073b92009-01-06 15:03:30 +0000648void SkPictureRecord::setMatrix(const SkMatrix& matrix) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000649 validate(fWriter.size(), 0);
650 // op + matrix index
651 uint32_t size = 2 * kUInt32Size;
652 uint32_t initialOffset = this->addDraw(SET_MATRIX, &size);
reed@android.com6e073b92009-01-06 15:03:30 +0000653 addMatrix(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000654 validate(initialOffset, size);
reed@android.com6e073b92009-01-06 15:03:30 +0000655 this->INHERITED::setMatrix(matrix);
656}
657
reed@google.com45482d12011-08-29 19:02:39 +0000658static bool regionOpExpands(SkRegion::Op op) {
659 switch (op) {
660 case SkRegion::kUnion_Op:
661 case SkRegion::kXOR_Op:
662 case SkRegion::kReverseDifference_Op:
663 case SkRegion::kReplace_Op:
664 return true;
665 case SkRegion::kIntersect_Op:
666 case SkRegion::kDifference_Op:
667 return false;
668 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000669 SkDEBUGFAIL("unknown region op");
reed@google.com45482d12011-08-29 19:02:39 +0000670 return false;
671 }
672}
673
robertphillips@google.come37ad352013-03-01 19:44:30 +0000674void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t restoreOffset) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000675 int32_t offset = fRestoreOffsetStack.top();
676 while (offset > 0) {
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000677 uint32_t* peek = fWriter.peek32(offset);
678 offset = *peek;
679 *peek = restoreOffset;
680 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000681
reed@google.comffacd3c2012-08-30 15:31:23 +0000682#ifdef SK_DEBUG
683 // assert that the final offset value points to a save verb
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000684 uint32_t opSize;
685 DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize);
reed@google.comffacd3c2012-08-30 15:31:23 +0000686 SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
687#endif
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000688}
689
reed@google.comd86e7ab2012-09-27 20:31:31 +0000690void SkPictureRecord::beginRecording() {
691 // we have to call this *after* our constructor, to ensure that it gets
692 // recorded. This is balanced by restoreToCount() call from endRecording,
693 // which in-turn calls our overridden restore(), so those get recorded too.
694 fInitialSaveCount = this->save(kMatrixClip_SaveFlag);
695}
696
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000697void SkPictureRecord::endRecording() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000698 SkASSERT(kNoInitialSave != fInitialSaveCount);
699 this->restoreToCount(fInitialSaveCount);
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000700}
701
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000702void SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
reed@google.com21b519d2012-10-02 17:42:15 +0000703 if (fRestoreOffsetStack.isEmpty()) {
704 return;
705 }
706
reed@google.com45482d12011-08-29 19:02:39 +0000707 if (regionOpExpands(op)) {
708 // Run back through any previous clip ops, and mark their offset to
709 // be 0, disabling their ability to trigger a jump-to-restore, otherwise
710 // they could hide this clips ability to expand the clip (i.e. go from
711 // empty to non-empty).
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000712 fillRestoreOffsetPlaceholdersForCurrentStackLevel(0);
reed@google.com45482d12011-08-29 19:02:39 +0000713 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000714
reed@google.com45482d12011-08-29 19:02:39 +0000715 size_t offset = fWriter.size();
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000716 // The RestoreOffset field is initially filled with a placeholder
717 // value that points to the offset of the previous RestoreOffset
718 // in the current stack level, thus forming a linked list so that
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000719 // the restore offsets can be filled in when the corresponding
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000720 // restore command is recorded.
reed@google.com45482d12011-08-29 19:02:39 +0000721 addInt(fRestoreOffsetStack.top());
722 fRestoreOffsetStack.top() = offset;
723}
724
reed@google.com071eef92011-10-12 11:52:53 +0000725bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.comf9291502013-02-15 15:13:27 +0000726 // id + rect + clip params
727 uint32_t size = 1 * kUInt32Size + sizeof(rect) + 1 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000728 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000729 if (!fRestoreOffsetStack.isEmpty()) {
730 // + restore offset
731 size += kUInt32Size;
732 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000733 uint32_t initialOffset = this->addDraw(CLIP_RECT, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 addRect(rect);
reed@google.com83ab4952011-11-11 21:34:54 +0000735 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000736 recordRestoreOffsetPlaceholder(op);
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000737
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000738 validate(initialOffset, size);
reed@google.com071eef92011-10-12 11:52:53 +0000739 return this->INHERITED::clipRect(rect, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740}
741
reed@google.com4ed0fb72012-12-12 20:48:18 +0000742bool SkPictureRecord::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
743 if (rrect.isRect()) {
744 return this->SkPictureRecord::clipRect(rrect.getBounds(), op, doAA);
745 }
746
robertphillips@google.comf9291502013-02-15 15:13:27 +0000747 // op + rrect + clip params
748 uint32_t size = 1 * kUInt32Size + SkRRect::kSizeInMemory + 1 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000749 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000750 if (!fRestoreOffsetStack.isEmpty()) {
751 // + restore offset
752 size += kUInt32Size;
753 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000754 uint32_t initialOffset = this->addDraw(CLIP_RRECT, &size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000755 addRRect(rrect);
756 addInt(ClipParams_pack(op, doAA));
757 recordRestoreOffsetPlaceholder(op);
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000758
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000759 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000760
761 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +0000762 return this->updateClipConservativelyUsingBounds(rrect.getBounds(), op, false);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000763 } else {
764 return this->INHERITED::clipRRect(rrect, op, doAA);
765 }
766}
767
reed@google.com071eef92011-10-12 11:52:53 +0000768bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000769
770 SkRect r;
reed@google.com907ef6c2012-12-10 15:50:37 +0000771 if (!path.isInverseFillType() && path.isRect(&r)) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000772 return this->clipRect(r, op, doAA);
773 }
774
robertphillips@google.comf9291502013-02-15 15:13:27 +0000775 // op + path index + clip params
776 uint32_t size = 3 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000777 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000778 if (!fRestoreOffsetStack.isEmpty()) {
779 // + restore offset
780 size += kUInt32Size;
781 }
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000782 uint32_t initialOffset = this->addDraw(CLIP_PATH, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 addPath(path);
reed@google.com83ab4952011-11-11 21:34:54 +0000784 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000785 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000786
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000787 validate(initialOffset, size);
reed@google.com82065d62011-02-07 15:30:46 +0000788
reed@android.comae814c82009-02-13 14:56:09 +0000789 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +0000790 return this->updateClipConservativelyUsingBounds(path.getBounds(), op,
junov@chromium.orgd575eed2013-05-08 15:39:13 +0000791 path.isInverseFillType());
reed@android.comae814c82009-02-13 14:56:09 +0000792 } else {
reed@google.com071eef92011-10-12 11:52:53 +0000793 return this->INHERITED::clipPath(path, op, doAA);
reed@android.comae814c82009-02-13 14:56:09 +0000794 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795}
796
797bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
robertphillips@google.comf9291502013-02-15 15:13:27 +0000798 // op + region index + clip params
799 uint32_t size = 3 * kUInt32Size;
robertphillips@google.com4310c662013-03-01 14:17:58 +0000800 // recordRestoreOffsetPlaceholder doesn't always write an offset
robertphillips@google.comf9291502013-02-15 15:13:27 +0000801 if (!fRestoreOffsetStack.isEmpty()) {
802 // + restore offset
803 size += kUInt32Size;
804 }
skia.committer@gmail.comce8343d2013-02-16 07:01:31 +0000805 uint32_t initialOffset = this->addDraw(CLIP_REGION, &size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 addRegion(region);
reed@google.com83ab4952011-11-11 21:34:54 +0000807 addInt(ClipParams_pack(op, false));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000808 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000809
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000810 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 return this->INHERITED::clipRegion(region, op);
812}
813
reed@google.com2a981812011-04-14 18:59:28 +0000814void SkPictureRecord::clear(SkColor color) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000815 // op + color
816 uint32_t size = 2 * kUInt32Size;
817 uint32_t initialOffset = this->addDraw(DRAW_CLEAR, &size);
reed@google.com2a981812011-04-14 18:59:28 +0000818 addInt(color);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000819 validate(initialOffset, size);
reed@google.com2a981812011-04-14 18:59:28 +0000820}
821
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822void SkPictureRecord::drawPaint(const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000823 // op + paint index
824 uint32_t size = 2 * kUInt32Size;
825 uint32_t initialOffset = this->addDraw(DRAW_PAINT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000826 SkASSERT(initialOffset+getPaintOffset(DRAW_PAINT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 addPaint(paint);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000828 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829}
830
831void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000832 const SkPaint& paint) {
833 // op + paint index + mode + count + point data
834 uint32_t size = 4 * kUInt32Size + count * sizeof(SkPoint);
835 uint32_t initialOffset = this->addDraw(DRAW_POINTS, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000836 SkASSERT(initialOffset+getPaintOffset(DRAW_POINTS, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 addPaint(paint);
838 addInt(mode);
839 addInt(count);
840 fWriter.writeMul4(pts, count * sizeof(SkPoint));
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000841 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842}
843
reed@google.com4ed0fb72012-12-12 20:48:18 +0000844void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000845 // op + paint index + rect
846 uint32_t size = 2 * kUInt32Size + sizeof(oval);
847 uint32_t initialOffset = this->addDraw(DRAW_OVAL, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000848 SkASSERT(initialOffset+getPaintOffset(DRAW_OVAL, size) == fWriter.size());
reed@google.com4ed0fb72012-12-12 20:48:18 +0000849 addPaint(paint);
850 addRect(oval);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000851 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000852}
853
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000855 // op + paint index + rect
856 uint32_t size = 2 * kUInt32Size + sizeof(rect);
857 uint32_t initialOffset = this->addDraw(DRAW_RECT, &size);
robertphillips@google.come37ad352013-03-01 19:44:30 +0000858 SkASSERT(initialOffset+getPaintOffset(DRAW_RECT, size) == fWriter.size());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 addPaint(paint);
860 addRect(rect);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000861 validate(initialOffset, size);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862}
863
reed@google.com4ed0fb72012-12-12 20:48:18 +0000864void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
865 if (rrect.isRect()) {
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000866 this->SkPictureRecord::drawRect(rrect.getBounds(), paint);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000867 } else if (rrect.isOval()) {
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000868 this->SkPictureRecord::drawOval(rrect.getBounds(), paint);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000869 } else {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000870 // op + paint index + rrect
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000871 uint32_t initialOffset, size;
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +0000872 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);
junov@chromium.org0ce0df72013-05-10 14:39:26 +0000877 validate(initialOffset, size);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000878 }
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
robertphillips@google.com0a4805e2013-05-29 13:24:23 +00001212void SkPictureRecord::beginCommentGroup(const char* description) {
1213 // op/size + length of string + \0 terminated chars
1214 int length = strlen(description);
1215 uint32_t size = 2 * kUInt32Size + SkAlign4(length + 1);
1216 uint32_t initialOffset = this->addDraw(BEGIN_COMMENT_GROUP, &size);
1217 fWriter.writeString(description, length);
1218 validate(initialOffset, size);
1219}
1220
1221void SkPictureRecord::addComment(const char* kywd, const char* value) {
1222 // op/size + 2x length of string + 2x \0 terminated chars
1223 int kywdLen = strlen(kywd);
1224 int valueLen = strlen(value);
1225 uint32_t size = 3 * kUInt32Size + SkAlign4(kywdLen + 1) + SkAlign4(valueLen + 1);
1226 uint32_t initialOffset = this->addDraw(COMMENT, &size);
1227 fWriter.writeString(kywd, kywdLen);
1228 fWriter.writeString(value, valueLen);
1229 validate(initialOffset, size);
1230}
1231
1232void SkPictureRecord::endCommentGroup() {
1233 // op/size
1234 uint32_t size = 1 * kUInt32Size;
1235 uint32_t initialOffset = this->addDraw(END_COMMENT_GROUP, &size);
1236 validate(initialOffset, size);
1237}
1238
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239///////////////////////////////////////////////////////////////////////////////
reed@google.com82065d62011-02-07 15:30:46 +00001240
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
scroggo@google.com4b90b112012-12-04 15:08:56 +00001242 const int index = fBitmapHeap->insert(bitmap);
1243 // In debug builds, a bad return value from insert() will crash, allowing for debugging. In
1244 // release builds, the invalid value will be recorded so that the reader will know that there
1245 // was a problem.
1246 SkASSERT(index != SkBitmapHeap::INVALID_SLOT);
1247 addInt(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248}
1249
1250void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
1251 addMatrixPtr(&matrix);
1252}
1253
1254void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
reed@google.com83ca3372012-07-12 15:27:54 +00001255 this->addInt(matrix ? fMatrices.find(*matrix) : 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256}
1257
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001258const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
1259 const SkFlatData* data = paint ? fPaints.findAndReturnFlat(*paint) : NULL;
1260 int index = data ? data->index() : 0;
reed@google.com45954262012-12-07 17:14:40 +00001261 this->addInt(index);
junov@chromium.orgf3b12232013-01-22 17:50:47 +00001262 return data;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263}
1264
1265void SkPictureRecord::addPath(const SkPath& path) {
1266 if (NULL == fPathHeap) {
1267 fPathHeap = SkNEW(SkPathHeap);
1268 }
1269 addInt(fPathHeap->append(path));
1270}
1271
1272void SkPictureRecord::addPicture(SkPicture& picture) {
1273 int index = fPictureRefs.find(&picture);
1274 if (index < 0) { // not found
1275 index = fPictureRefs.count();
1276 *fPictureRefs.append() = &picture;
1277 picture.ref();
1278 }
1279 // follow the convention of recording a 1-based index
1280 addInt(index + 1);
1281}
1282
1283void SkPictureRecord::addPoint(const SkPoint& point) {
1284#ifdef SK_DEBUG_SIZE
1285 size_t start = fWriter.size();
1286#endif
1287 fWriter.writePoint(point);
1288#ifdef SK_DEBUG_SIZE
1289 fPointBytes += fWriter.size() - start;
1290 fPointWrites++;
1291#endif
1292}
reed@google.com82065d62011-02-07 15:30:46 +00001293
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
1295 fWriter.writeMul4(pts, count * sizeof(SkPoint));
1296#ifdef SK_DEBUG_SIZE
1297 fPointBytes += count * sizeof(SkPoint);
1298 fPointWrites++;
1299#endif
1300}
1301
1302void SkPictureRecord::addRect(const SkRect& rect) {
1303#ifdef SK_DEBUG_SIZE
1304 size_t start = fWriter.size();
1305#endif
1306 fWriter.writeRect(rect);
1307#ifdef SK_DEBUG_SIZE
1308 fRectBytes += fWriter.size() - start;
1309 fRectWrites++;
1310#endif
1311}
1312
1313void SkPictureRecord::addRectPtr(const SkRect* rect) {
1314 if (fWriter.writeBool(rect != NULL)) {
1315 fWriter.writeRect(*rect);
1316 }
1317}
1318
reed@google.comf0b5e112011-09-07 11:57:34 +00001319void SkPictureRecord::addIRect(const SkIRect& rect) {
1320 fWriter.write(&rect, sizeof(rect));
1321}
1322
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
1324 if (fWriter.writeBool(rect != NULL)) {
1325 *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
1326 }
1327}
1328
reed@google.com4ed0fb72012-12-12 20:48:18 +00001329void SkPictureRecord::addRRect(const SkRRect& rrect) {
1330 fWriter.writeRRect(rrect);
1331}
1332
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333void SkPictureRecord::addRegion(const SkRegion& region) {
reed@google.com83ca3372012-07-12 15:27:54 +00001334 addInt(fRegions.find(region));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335}
1336
1337void SkPictureRecord::addText(const void* text, size_t byteLength) {
1338#ifdef SK_DEBUG_SIZE
1339 size_t start = fWriter.size();
1340#endif
1341 addInt(byteLength);
1342 fWriter.writePad(text, byteLength);
1343#ifdef SK_DEBUG_SIZE
1344 fTextBytes += fWriter.size() - start;
1345 fTextWrites++;
1346#endif
1347}
1348
1349///////////////////////////////////////////////////////////////////////////////
1350
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351#ifdef SK_DEBUG_SIZE
1352size_t SkPictureRecord::size() const {
1353 size_t result = 0;
1354 size_t sizeData;
1355 bitmaps(&sizeData);
1356 result += sizeData;
1357 matrices(&sizeData);
1358 result += sizeData;
1359 paints(&sizeData);
1360 result += sizeData;
1361 paths(&sizeData);
1362 result += sizeData;
1363 pictures(&sizeData);
1364 result += sizeData;
1365 regions(&sizeData);
1366 result += sizeData;
1367 result += streamlen();
1368 return result;
1369}
1370
1371int SkPictureRecord::bitmaps(size_t* size) const {
1372 size_t result = 0;
1373 int count = fBitmaps.count();
reed@google.com82065d62011-02-07 15:30:46 +00001374 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
1376 *size = result;
1377 return count;
1378}
1379
1380int SkPictureRecord::matrices(size_t* size) const {
1381 int count = fMatrices.count();
1382 *size = sizeof(fMatrices[0]) * count;
1383 return count;
1384}
1385
1386int SkPictureRecord::paints(size_t* size) const {
1387 size_t result = 0;
1388 int count = fPaints.count();
reed@google.com82065d62011-02-07 15:30:46 +00001389 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 result += sizeof(fPaints[index]) + fPaints[index]->size();
1391 *size = result;
1392 return count;
1393}
1394
1395int SkPictureRecord::paths(size_t* size) const {
1396 size_t result = 0;
1397 int count = fPaths.count();
reed@google.com82065d62011-02-07 15:30:46 +00001398 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 result += sizeof(fPaths[index]) + fPaths[index]->size();
1400 *size = result;
1401 return count;
1402}
1403
1404int SkPictureRecord::regions(size_t* size) const {
1405 size_t result = 0;
1406 int count = fRegions.count();
reed@google.com82065d62011-02-07 15:30:46 +00001407 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 result += sizeof(fRegions[index]) + fRegions[index]->size();
1409 *size = result;
1410 return count;
1411}
1412
1413size_t SkPictureRecord::streamlen() const {
1414 return fWriter.size();
1415}
1416#endif
1417
1418#ifdef SK_DEBUG_VALIDATE
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001419void SkPictureRecord::validate(uint32_t initialOffset, uint32_t size) const {
1420 SkASSERT(fWriter.size() == initialOffset + size);
1421
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 validateBitmaps();
1423 validateMatrices();
1424 validatePaints();
1425 validatePaths();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426 validateRegions();
1427}
1428
1429void SkPictureRecord::validateBitmaps() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001430 int count = fBitmapHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 SkASSERT((unsigned) count < 0x1000);
1432 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001433 const SkBitmap* bitPtr = fBitmapHeap->getBitmap(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434 SkASSERT(bitPtr);
1435 bitPtr->validate();
1436 }
1437}
1438
1439void SkPictureRecord::validateMatrices() const {
1440 int count = fMatrices.count();
1441 SkASSERT((unsigned) count < 0x1000);
1442 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001443 const SkFlatData* matrix = fMatrices[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 SkASSERT(matrix);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001445// matrix->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446 }
1447}
1448
1449void SkPictureRecord::validatePaints() const {
1450 int count = fPaints.count();
1451 SkASSERT((unsigned) count < 0x1000);
1452 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001453 const SkFlatData* paint = fPaints[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 SkASSERT(paint);
1455// paint->validate();
1456 }
1457}
1458
1459void SkPictureRecord::validatePaths() const {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001460 if (NULL == fPathHeap) {
1461 return;
1462 }
1463
1464 int count = fPathHeap->count();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465 SkASSERT((unsigned) count < 0x1000);
1466 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001467 const SkPath& path = (*fPathHeap)[index];
1468 path.validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 }
1470}
1471
1472void SkPictureRecord::validateRegions() const {
1473 int count = fRegions.count();
1474 SkASSERT((unsigned) count < 0x1000);
1475 for (int index = 0; index < count; index++) {
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001476 const SkFlatData* region = fRegions[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477 SkASSERT(region);
robertphillips@google.com2ca1aaa2013-02-15 13:47:37 +00001478// region->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479 }
1480}
1481#endif