blob: 82257c8e5778614662ba3f244cd32131d80be75f [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
reed@google.comd86e7ab2012-09-27 20:31:31 +000023SkPictureRecord::SkPictureRecord(uint32_t flags, SkDevice* device) :
24 INHERITED(device),
robertphillips@google.com178a2672012-09-13 13:25:30 +000025 fBoundingHierarchy(NULL),
26 fStateTree(NULL),
djsollen@google.com21830d92012-08-07 19:49:41 +000027 fFlattenableHeap(HEAP_BLOCK_SIZE),
28 fMatrices(&fFlattenableHeap),
29 fPaints(&fFlattenableHeap),
30 fRegions(&fFlattenableHeap),
djsollen@google.comd2700ee2012-05-30 16:54:13 +000031 fWriter(MIN_WRITER_SIZE),
32 fRecordFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000033#ifdef SK_DEBUG_SIZE
34 fPointBytes = fRectBytes = fTextBytes = 0;
35 fPointWrites = fRectWrites = fTextWrites = 0;
36#endif
37
38 fRestoreOffsetStack.setReserve(32);
reed@google.com82065d62011-02-07 15:30:46 +000039
djsollen@google.comc9ab9872012-08-29 18:52:07 +000040 fBitmapHeap = SkNEW(SkBitmapHeap);
41 fFlattenableHeap.setBitmapStorage(fBitmapHeap);
reed@android.com8a1c16f2008-12-17 15:59:43 +000042 fPathHeap = NULL; // lazy allocate
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +000043 fFirstSavedLayerIndex = kNoSavedLayerIndex;
reed@google.comd86e7ab2012-09-27 20:31:31 +000044
45 fInitialSaveCount = kNoInitialSave;
reed@android.com8a1c16f2008-12-17 15:59:43 +000046}
47
48SkPictureRecord::~SkPictureRecord() {
djsollen@google.comc9ab9872012-08-29 18:52:07 +000049 SkSafeUnref(fBitmapHeap);
djsollen@google.com21830d92012-08-07 19:49:41 +000050 SkSafeUnref(fPathHeap);
rileya@google.com9f5898d2012-09-11 20:21:44 +000051 SkSafeUnref(fBoundingHierarchy);
52 SkSafeUnref(fStateTree);
djsollen@google.com21830d92012-08-07 19:49:41 +000053 fFlattenableHeap.setBitmapStorage(NULL);
54 fPictureRefs.unrefAll();
reed@android.com8a1c16f2008-12-17 15:59:43 +000055}
56
57///////////////////////////////////////////////////////////////////////////////
58
junov@chromium.org4e6dfa52012-07-16 14:04:59 +000059SkDevice* SkPictureRecord::setDevice(SkDevice* device) {
reed@google.comd86e7ab2012-09-27 20:31:31 +000060 SkASSERT(!"eeek, don't try to change the device on a recording canvas");
61 return this->INHERITED::setDevice(device);
junov@chromium.org4e6dfa52012-07-16 14:04:59 +000062}
63
reed@android.com8a1c16f2008-12-17 15:59:43 +000064int SkPictureRecord::save(SaveFlags flags) {
reed@google.comffacd3c2012-08-30 15:31:23 +000065 // record the offset to us, making it non-positive to distinguish a save
66 // from a clip entry.
67 fRestoreOffsetStack.push(-(int32_t)fWriter.size());
skia.committer@gmail.com11f86922012-08-31 17:14:46 +000068
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 addDraw(SAVE);
70 addInt(flags);
reed@google.com82065d62011-02-07 15:30:46 +000071
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 validate();
73 return this->INHERITED::save(flags);
74}
75
76int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
77 SaveFlags flags) {
reed@google.comffacd3c2012-08-30 15:31:23 +000078 // record the offset to us, making it non-positive to distinguish a save
79 // from a clip entry.
80 fRestoreOffsetStack.push(-(int32_t)fWriter.size());
skia.committer@gmail.com11f86922012-08-31 17:14:46 +000081
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 addDraw(SAVE_LAYER);
83 addRectPtr(bounds);
84 addPaintPtr(paint);
85 addInt(flags);
86
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +000087 if (kNoSavedLayerIndex == fFirstSavedLayerIndex) {
88 fFirstSavedLayerIndex = fRestoreOffsetStack.count();
89 }
90
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 validate();
reed@android.com261ae4d2009-10-02 16:37:46 +000092 /* Don't actually call saveLayer, because that will try to allocate an
93 offscreen device (potentially very big) which we don't actually need
94 at this time (and may not be able to afford since during record our
95 clip starts out the size of the picture, which is often much larger
96 than the size of the actual device we'll use during playback).
97 */
junov@chromium.orga907ac32012-02-24 21:54:07 +000098 int count = this->INHERITED::save(flags);
99 this->clipRectBounds(bounds, flags, NULL);
100 return count;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101}
102
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000103bool SkPictureRecord::isDrawingToLayer() const {
104 return fFirstSavedLayerIndex != kNoSavedLayerIndex;
105}
106
reed@google.comffacd3c2012-08-30 15:31:23 +0000107// Return the size of the specified drawType's recorded block, or 0 if this verb
108// is variable sized, and therefore not known.
109static inline uint32_t getSkipableSize(unsigned drawType) {
110 static const uint8_t gSizes[LAST_DRAWTYPE_ENUM + 1] = {
111 0, // UNUSED,
112 4, // CLIP_PATH,
113 4, // CLIP_REGION,
114 7, // CLIP_RECT,
reed@google.com4ed0fb72012-12-12 20:48:18 +0000115 15, // CLIP_RRECT,
reed@google.comffacd3c2012-08-30 15:31:23 +0000116 2, // CONCAT,
117 0, // DRAW_BITMAP,
118 0, // DRAW_BITMAP_MATRIX,
119 0, // DRAW_BITMAP_NINE,
120 0, // DRAW_BITMAP_RECT,
121 0, // DRAW_CLEAR,
122 0, // DRAW_DATA,
reed@google.com4ed0fb72012-12-12 20:48:18 +0000123 0, // DRAW_OVAL,
reed@google.comffacd3c2012-08-30 15:31:23 +0000124 0, // DRAW_PAINT,
125 0, // DRAW_PATH,
126 0, // DRAW_PICTURE,
127 0, // DRAW_POINTS,
128 0, // DRAW_POS_TEXT,
129 0, // DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT
130 0, // DRAW_POS_TEXT_H,
131 0, // DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
132 0, // DRAW_RECT,
reed@google.com4ed0fb72012-12-12 20:48:18 +0000133 0, // DRAW_RRECT,
reed@google.comffacd3c2012-08-30 15:31:23 +0000134 0, // DRAW_SPRITE,
135 0, // DRAW_TEXT,
136 0, // DRAW_TEXT_ON_PATH,
137 0, // DRAW_TEXT_TOP_BOTTOM, // fast variant of DRAW_TEXT
138 0, // DRAW_VERTICES,
139 0, // RESTORE,
140 2, // ROTATE,
141 2, // SAVE,
142 0, // SAVE_LAYER,
143 3, // SCALE,
144 2, // SET_MATRIX,
145 3, // SKEW,
146 3, // TRANSLATE,
147 };
148
149 SkASSERT(sizeof(gSizes) == LAST_DRAWTYPE_ENUM + 1);
150 SkASSERT((unsigned)drawType <= (unsigned)LAST_DRAWTYPE_ENUM);
151 return gSizes[drawType] * sizeof(uint32_t);
152}
153
154#ifdef TRACK_COLLAPSE_STATS
155 static int gCollapseCount, gCollapseCalls;
156#endif
157
158/*
159 * Restore has just been called (but not recoreded), so look back at the
160 * matching save(), and see if we can eliminate the pair of them, due to no
161 * intervening matrix/clip calls.
162 *
163 * If so, update the writer and return true, in which case we won't even record
164 * the restore() call. If we still need the restore(), return false.
165 */
166static bool collapseSaveClipRestore(SkWriter32* writer, int32_t offset) {
167#ifdef TRACK_COLLAPSE_STATS
168 gCollapseCalls += 1;
169#endif
170
171 int32_t restoreOffset = (int32_t)writer->size();
172
173 // back up to the save block
174 while (offset > 0) {
175 offset = *writer->peek32(offset);
176 }
177
178 // now offset points to a save
179 offset = -offset;
180 if (SAVE_LAYER == *writer->peek32(offset)) {
181 // not ready to cull these out yet (mrr)
182 return false;
183 }
184 SkASSERT(SAVE == *writer->peek32(offset));
185
186 // Walk forward until we get back to either a draw-verb (abort) or we hit
187 // our restore (success).
188 int32_t saveOffset = offset;
189
190 offset += getSkipableSize(SAVE);
191 while (offset < restoreOffset) {
192 uint32_t* block = writer->peek32(offset);
193 uint32_t op = *block;
194 uint32_t opSize = getSkipableSize(op);
195 if (0 == opSize) {
196 // drawing verb, abort
197 return false;
198 }
199 offset += opSize;
200 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000201
reed@google.comffacd3c2012-08-30 15:31:23 +0000202#ifdef TRACK_COLLAPSE_STATS
203 gCollapseCount += 1;
204 SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls,
205 (double)gCollapseCount / gCollapseCalls, "%");
206#endif
207
208 writer->rewindToOffset(saveOffset);
209 return true;
210}
211
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212void SkPictureRecord::restore() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000213 // FIXME: SkDeferredCanvas needs to be refactored to respect
214 // save/restore balancing so that the following test can be
215 // turned on permanently.
216#if 0
217 SkASSERT(fRestoreOffsetStack.count() > 1);
218#endif
219
reed@android.comb4e22d62009-07-09 15:20:25 +0000220 // check for underflow
221 if (fRestoreOffsetStack.count() == 0) {
222 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 }
reed@android.comb4e22d62009-07-09 15:20:25 +0000224
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000225 if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
226 fFirstSavedLayerIndex = kNoSavedLayerIndex;
227 }
228
reed@google.comffacd3c2012-08-30 15:31:23 +0000229 if (!collapseSaveClipRestore(&fWriter, fRestoreOffsetStack.top())) {
230 fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
231 this->addDraw(RESTORE);
232 }
233
reed@android.comb4e22d62009-07-09 15:20:25 +0000234 fRestoreOffsetStack.pop();
reed@android.com32a42492009-07-10 03:33:52 +0000235
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 validate();
237 return this->INHERITED::restore();
238}
239
240bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
241 addDraw(TRANSLATE);
242 addScalar(dx);
243 addScalar(dy);
244 validate();
245 return this->INHERITED::translate(dx, dy);
246}
247
248bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
reed@google.com82065d62011-02-07 15:30:46 +0000249 addDraw(SCALE);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 addScalar(sx);
251 addScalar(sy);
252 validate();
253 return this->INHERITED::scale(sx, sy);
254}
255
256bool SkPictureRecord::rotate(SkScalar degrees) {
reed@google.com82065d62011-02-07 15:30:46 +0000257 addDraw(ROTATE);
258 addScalar(degrees);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 validate();
260 return this->INHERITED::rotate(degrees);
261}
262
263bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
reed@google.com82065d62011-02-07 15:30:46 +0000264 addDraw(SKEW);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 addScalar(sx);
266 addScalar(sy);
267 validate();
268 return this->INHERITED::skew(sx, sy);
269}
270
271bool SkPictureRecord::concat(const SkMatrix& matrix) {
272 validate();
273 addDraw(CONCAT);
274 addMatrix(matrix);
275 validate();
276 return this->INHERITED::concat(matrix);
277}
278
reed@android.com6e073b92009-01-06 15:03:30 +0000279void SkPictureRecord::setMatrix(const SkMatrix& matrix) {
280 validate();
281 addDraw(SET_MATRIX);
282 addMatrix(matrix);
283 validate();
284 this->INHERITED::setMatrix(matrix);
285}
286
reed@google.com45482d12011-08-29 19:02:39 +0000287static bool regionOpExpands(SkRegion::Op op) {
288 switch (op) {
289 case SkRegion::kUnion_Op:
290 case SkRegion::kXOR_Op:
291 case SkRegion::kReverseDifference_Op:
292 case SkRegion::kReplace_Op:
293 return true;
294 case SkRegion::kIntersect_Op:
295 case SkRegion::kDifference_Op:
296 return false;
297 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000298 SkDEBUGFAIL("unknown region op");
reed@google.com45482d12011-08-29 19:02:39 +0000299 return false;
300 }
301}
302
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000303void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(
304 uint32_t restoreOffset) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000305 int32_t offset = fRestoreOffsetStack.top();
306 while (offset > 0) {
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000307 uint32_t* peek = fWriter.peek32(offset);
308 offset = *peek;
309 *peek = restoreOffset;
310 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000311
reed@google.comffacd3c2012-08-30 15:31:23 +0000312#ifdef SK_DEBUG
313 // assert that the final offset value points to a save verb
314 uint32_t drawOp = *fWriter.peek32(-offset);
315 SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
316#endif
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000317}
318
reed@google.comd86e7ab2012-09-27 20:31:31 +0000319void SkPictureRecord::beginRecording() {
320 // we have to call this *after* our constructor, to ensure that it gets
321 // recorded. This is balanced by restoreToCount() call from endRecording,
322 // which in-turn calls our overridden restore(), so those get recorded too.
323 fInitialSaveCount = this->save(kMatrixClip_SaveFlag);
324}
325
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000326void SkPictureRecord::endRecording() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000327 SkASSERT(kNoInitialSave != fInitialSaveCount);
328 this->restoreToCount(fInitialSaveCount);
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000329}
330
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000331void SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
reed@google.com21b519d2012-10-02 17:42:15 +0000332 if (fRestoreOffsetStack.isEmpty()) {
333 return;
334 }
335
reed@google.com45482d12011-08-29 19:02:39 +0000336 if (regionOpExpands(op)) {
337 // Run back through any previous clip ops, and mark their offset to
338 // be 0, disabling their ability to trigger a jump-to-restore, otherwise
339 // they could hide this clips ability to expand the clip (i.e. go from
340 // empty to non-empty).
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000341 fillRestoreOffsetPlaceholdersForCurrentStackLevel(0);
reed@google.com45482d12011-08-29 19:02:39 +0000342 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000343
reed@google.com45482d12011-08-29 19:02:39 +0000344 size_t offset = fWriter.size();
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000345 // The RestoreOffset field is initially filled with a placeholder
346 // value that points to the offset of the previous RestoreOffset
347 // in the current stack level, thus forming a linked list so that
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000348 // the restore offsets can be filled in when the corresponding
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000349 // restore command is recorded.
reed@google.com45482d12011-08-29 19:02:39 +0000350 addInt(fRestoreOffsetStack.top());
351 fRestoreOffsetStack.top() = offset;
352}
353
reed@google.com071eef92011-10-12 11:52:53 +0000354bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 addDraw(CLIP_RECT);
356 addRect(rect);
reed@google.com83ab4952011-11-11 21:34:54 +0000357 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000358 recordRestoreOffsetPlaceholder(op);
reed@google.com4ed0fb72012-12-12 20:48:18 +0000359
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 validate();
reed@google.com071eef92011-10-12 11:52:53 +0000361 return this->INHERITED::clipRect(rect, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362}
363
reed@google.com4ed0fb72012-12-12 20:48:18 +0000364bool SkPictureRecord::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
365 if (rrect.isRect()) {
366 return this->SkPictureRecord::clipRect(rrect.getBounds(), op, doAA);
367 }
368
369 addDraw(CLIP_RRECT);
370 addRRect(rrect);
371 addInt(ClipParams_pack(op, doAA));
372 recordRestoreOffsetPlaceholder(op);
373
374 validate();
375
376 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
377 return this->INHERITED::clipRect(rrect.getBounds(), op, doAA);
378 } else {
379 return this->INHERITED::clipRRect(rrect, op, doAA);
380 }
381}
382
reed@google.com071eef92011-10-12 11:52:53 +0000383bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000384
385 SkRect r;
reed@google.com907ef6c2012-12-10 15:50:37 +0000386 if (!path.isInverseFillType() && path.isRect(&r)) {
robertphillips@google.com33027832012-11-07 13:01:25 +0000387 return this->clipRect(r, op, doAA);
388 }
389
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390 addDraw(CLIP_PATH);
391 addPath(path);
reed@google.com83ab4952011-11-11 21:34:54 +0000392 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000393 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000394
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 validate();
reed@google.com82065d62011-02-07 15:30:46 +0000396
reed@android.comae814c82009-02-13 14:56:09 +0000397 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
reed@google.com071eef92011-10-12 11:52:53 +0000398 return this->INHERITED::clipRect(path.getBounds(), op, doAA);
reed@android.comae814c82009-02-13 14:56:09 +0000399 } else {
reed@google.com071eef92011-10-12 11:52:53 +0000400 return this->INHERITED::clipPath(path, op, doAA);
reed@android.comae814c82009-02-13 14:56:09 +0000401 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402}
403
404bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
reed@google.com82065d62011-02-07 15:30:46 +0000405 addDraw(CLIP_REGION);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406 addRegion(region);
reed@google.com83ab4952011-11-11 21:34:54 +0000407 addInt(ClipParams_pack(op, false));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000408 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000409
reed@android.com8a1c16f2008-12-17 15:59:43 +0000410 validate();
411 return this->INHERITED::clipRegion(region, op);
412}
413
reed@google.com2a981812011-04-14 18:59:28 +0000414void SkPictureRecord::clear(SkColor color) {
415 addDraw(DRAW_CLEAR);
416 addInt(color);
417 validate();
418}
419
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420void SkPictureRecord::drawPaint(const SkPaint& paint) {
421 addDraw(DRAW_PAINT);
422 addPaint(paint);
423 validate();
424}
425
426void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
427 const SkPaint& paint) {
428 addDraw(DRAW_POINTS);
429 addPaint(paint);
430 addInt(mode);
431 addInt(count);
432 fWriter.writeMul4(pts, count * sizeof(SkPoint));
433 validate();
434}
435
reed@google.com4ed0fb72012-12-12 20:48:18 +0000436void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) {
437 addDraw(DRAW_OVAL);
438 addPaint(paint);
439 addRect(oval);
440 validate();
441}
442
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
444 addDraw(DRAW_RECT);
445 addPaint(paint);
446 addRect(rect);
447 validate();
448}
449
reed@google.com4ed0fb72012-12-12 20:48:18 +0000450void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
451 if (rrect.isRect()) {
452 addDraw(DRAW_RECT);
453 addPaint(paint);
454 addRect(rrect.getBounds());
455 } else if (rrect.isOval()) {
456 addDraw(DRAW_OVAL);
457 addPaint(paint);
458 addRect(rrect.getBounds());
459 } else {
460 addDraw(DRAW_RRECT);
461 addPaint(paint);
462 addRRect(rrect);
463 }
464 validate();
465}
466
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
468 addDraw(DRAW_PATH);
469 addPaint(paint);
470 addPath(path);
471 validate();
472}
473
474void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
475 const SkPaint* paint = NULL) {
476 addDraw(DRAW_BITMAP);
477 addPaintPtr(paint);
478 addBitmap(bitmap);
479 addScalar(left);
480 addScalar(top);
481 validate();
482}
483
reed@google.com71121732012-09-18 15:14:33 +0000484void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 const SkRect& dst, const SkPaint* paint) {
reed@google.com71121732012-09-18 15:14:33 +0000486 addDraw(DRAW_BITMAP_RECT_TO_RECT);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 addPaintPtr(paint);
488 addBitmap(bitmap);
reed@google.com71121732012-09-18 15:14:33 +0000489 addRectPtr(src); // may be null
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 addRect(dst);
491 validate();
492}
493
494void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
reed@google.comf0b5e112011-09-07 11:57:34 +0000495 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 addDraw(DRAW_BITMAP_MATRIX);
497 addPaintPtr(paint);
498 addBitmap(bitmap);
499 addMatrix(matrix);
500 validate();
501}
502
reed@google.comf0b5e112011-09-07 11:57:34 +0000503void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
504 const SkRect& dst, const SkPaint* paint) {
505 addDraw(DRAW_BITMAP_NINE);
506 addPaintPtr(paint);
507 addBitmap(bitmap);
508 addIRect(center);
509 addRect(dst);
510 validate();
511}
512
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
514 const SkPaint* paint = NULL) {
515 addDraw(DRAW_SPRITE);
516 addPaintPtr(paint);
517 addBitmap(bitmap);
518 addInt(left);
519 addInt(top);
520 validate();
521}
522
reed@google.com45954262012-12-07 17:14:40 +0000523// Return fontmetrics.fTop,fBottom in topbot[0,1], after they have been
524// tweaked by paint.computeFastBounds().
525//
526static void computeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 SkPaint::FontMetrics metrics;
528 paint.getFontMetrics(&metrics);
529 SkRect bounds;
530 // construct a rect so we can see any adjustments from the paint.
531 // we use 0,1 for left,right, just so the rect isn't empty
reed@google.com45954262012-12-07 17:14:40 +0000532 bounds.set(0, metrics.fTop, SK_Scalar1, metrics.fBottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 (void)paint.computeFastBounds(bounds, &bounds);
reed@google.com45954262012-12-07 17:14:40 +0000534 topbot[0] = bounds.fTop;
535 topbot[1] = bounds.fBottom;
536}
537
538void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, int index,
539 SkScalar minY, SkScalar maxY) {
540 SkFlatData* flat = fPaints.writableFlatData(index);
541 if (!flat->isTopBotValid()) {
542 computeFontMetricsTopBottom(paint, flat->writableTopBot());
543 SkASSERT(flat->isTopBotValid());
544 }
545 addScalar(flat->topBot()[0] + minY);
546 addScalar(flat->topBot()[1] + maxY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547}
548
reed@google.com82065d62011-02-07 15:30:46 +0000549void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 SkScalar y, const SkPaint& paint) {
reed@google.com2eb5bb12012-04-12 14:27:42 +0000551 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com82065d62011-02-07 15:30:46 +0000552
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 addDraw(fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT);
reed@google.com45954262012-12-07 17:14:40 +0000554 int paintIndex = addPaint(paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 addText(text, byteLength);
556 addScalar(x);
557 addScalar(y);
558 if (fast) {
reed@google.com45954262012-12-07 17:14:40 +0000559 addFontMetricsTopBottom(paint, paintIndex - 1, y, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 }
561 validate();
562}
563
reed@google.com82065d62011-02-07 15:30:46 +0000564void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 const SkPoint pos[], const SkPaint& paint) {
566 size_t points = paint.countText(text, byteLength);
567 if (0 == points)
568 return;
569
570 bool canUseDrawH = true;
reed@google.com9efd9a02012-01-30 15:41:43 +0000571 SkScalar minY = pos[0].fY;
572 SkScalar maxY = pos[0].fY;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 // check if the caller really should have used drawPosTextH()
574 {
575 const SkScalar firstY = pos[0].fY;
576 for (size_t index = 1; index < points; index++) {
577 if (pos[index].fY != firstY) {
578 canUseDrawH = false;
reed@google.com9efd9a02012-01-30 15:41:43 +0000579 if (pos[index].fY < minY) {
580 minY = pos[index].fY;
581 } else if (pos[index].fY > maxY) {
582 maxY = pos[index].fY;
583 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 }
585 }
586 }
reed@google.com82065d62011-02-07 15:30:46 +0000587
reed@google.com2eb5bb12012-04-12 14:27:42 +0000588 bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com9efd9a02012-01-30 15:41:43 +0000589 bool fast = canUseDrawH && fastBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590
591 if (fast) {
592 addDraw(DRAW_POS_TEXT_H_TOP_BOTTOM);
reed@google.com9efd9a02012-01-30 15:41:43 +0000593 } else if (canUseDrawH) {
594 addDraw(DRAW_POS_TEXT_H);
595 } else if (fastBounds) {
596 addDraw(DRAW_POS_TEXT_TOP_BOTTOM);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 } else {
reed@google.com9efd9a02012-01-30 15:41:43 +0000598 addDraw(DRAW_POS_TEXT);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 }
reed@google.com45954262012-12-07 17:14:40 +0000600 int paintIndex = addPaint(paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 addText(text, byteLength);
602 addInt(points);
603
604#ifdef SK_DEBUG_SIZE
605 size_t start = fWriter.size();
606#endif
607 if (canUseDrawH) {
608 if (fast) {
reed@google.com45954262012-12-07 17:14:40 +0000609 addFontMetricsTopBottom(paint, paintIndex - 1, pos[0].fY, pos[0].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 }
611 addScalar(pos[0].fY);
612 SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
reed@google.com82065d62011-02-07 15:30:46 +0000613 for (size_t index = 0; index < points; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 *xptr++ = pos[index].fX;
615 }
616 else {
617 fWriter.writeMul4(pos, points * sizeof(SkPoint));
reed@google.com9efd9a02012-01-30 15:41:43 +0000618 if (fastBounds) {
reed@google.com45954262012-12-07 17:14:40 +0000619 addFontMetricsTopBottom(paint, paintIndex - 1, minY, maxY);
reed@google.com9efd9a02012-01-30 15:41:43 +0000620 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 }
622#ifdef SK_DEBUG_SIZE
623 fPointBytes += fWriter.size() - start;
624 fPointWrites += points;
625#endif
626 validate();
627}
628
629void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
630 const SkScalar xpos[], SkScalar constY,
631 const SkPaint& paint) {
632 size_t points = paint.countText(text, byteLength);
633 if (0 == points)
634 return;
reed@google.com82065d62011-02-07 15:30:46 +0000635
reed@google.com2eb5bb12012-04-12 14:27:42 +0000636 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637
638 addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H);
reed@google.com45954262012-12-07 17:14:40 +0000639 int paintIndex = this->addPaint(paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 addText(text, byteLength);
641 addInt(points);
reed@google.com82065d62011-02-07 15:30:46 +0000642
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643#ifdef SK_DEBUG_SIZE
644 size_t start = fWriter.size();
645#endif
646 if (fast) {
reed@google.com45954262012-12-07 17:14:40 +0000647 addFontMetricsTopBottom(paint, paintIndex - 1, constY, constY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648 }
649 addScalar(constY);
650 fWriter.writeMul4(xpos, points * sizeof(SkScalar));
651#ifdef SK_DEBUG_SIZE
652 fPointBytes += fWriter.size() - start;
653 fPointWrites += points;
654#endif
655 validate();
656}
657
reed@google.com82065d62011-02-07 15:30:46 +0000658void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength,
659 const SkPath& path, const SkMatrix* matrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 const SkPaint& paint) {
661 addDraw(DRAW_TEXT_ON_PATH);
662 addPaint(paint);
663 addText(text, byteLength);
664 addPath(path);
665 addMatrixPtr(matrix);
666 validate();
667}
668
669void SkPictureRecord::drawPicture(SkPicture& picture) {
670 addDraw(DRAW_PICTURE);
671 addPicture(picture);
672 validate();
673}
674
675void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
676 const SkPoint vertices[], const SkPoint texs[],
677 const SkColor colors[], SkXfermode*,
678 const uint16_t indices[], int indexCount,
679 const SkPaint& paint) {
680 uint32_t flags = 0;
681 if (texs) {
682 flags |= DRAW_VERTICES_HAS_TEXS;
683 }
684 if (colors) {
685 flags |= DRAW_VERTICES_HAS_COLORS;
686 }
687 if (indexCount > 0) {
688 flags |= DRAW_VERTICES_HAS_INDICES;
689 }
690
691 addDraw(DRAW_VERTICES);
692 addPaint(paint);
693 addInt(flags);
694 addInt(vmode);
695 addInt(vertexCount);
696 addPoints(vertices, vertexCount);
697 if (flags & DRAW_VERTICES_HAS_TEXS) {
698 addPoints(texs, vertexCount);
699 }
700 if (flags & DRAW_VERTICES_HAS_COLORS) {
701 fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
702 }
703 if (flags & DRAW_VERTICES_HAS_INDICES) {
704 addInt(indexCount);
705 fWriter.writePad(indices, indexCount * sizeof(uint16_t));
706 }
707}
708
reed@android.comcb608442009-12-04 21:32:27 +0000709void SkPictureRecord::drawData(const void* data, size_t length) {
710 addDraw(DRAW_DATA);
711 addInt(length);
712 fWriter.writePad(data, length);
713}
714
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715///////////////////////////////////////////////////////////////////////////////
reed@google.com82065d62011-02-07 15:30:46 +0000716
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
scroggo@google.com4b90b112012-12-04 15:08:56 +0000718 const int index = fBitmapHeap->insert(bitmap);
719 // In debug builds, a bad return value from insert() will crash, allowing for debugging. In
720 // release builds, the invalid value will be recorded so that the reader will know that there
721 // was a problem.
722 SkASSERT(index != SkBitmapHeap::INVALID_SLOT);
723 addInt(index);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724}
725
726void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
727 addMatrixPtr(&matrix);
728}
729
730void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
reed@google.com83ca3372012-07-12 15:27:54 +0000731 this->addInt(matrix ? fMatrices.find(*matrix) : 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732}
733
reed@google.com45954262012-12-07 17:14:40 +0000734int SkPictureRecord::addPaintPtr(const SkPaint* paint) {
735 int index = paint ? fPaints.find(*paint) : 0;
736 this->addInt(index);
737 return index;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738}
739
740void SkPictureRecord::addPath(const SkPath& path) {
741 if (NULL == fPathHeap) {
742 fPathHeap = SkNEW(SkPathHeap);
743 }
744 addInt(fPathHeap->append(path));
745}
746
747void SkPictureRecord::addPicture(SkPicture& picture) {
748 int index = fPictureRefs.find(&picture);
749 if (index < 0) { // not found
750 index = fPictureRefs.count();
751 *fPictureRefs.append() = &picture;
752 picture.ref();
753 }
754 // follow the convention of recording a 1-based index
755 addInt(index + 1);
756}
757
758void SkPictureRecord::addPoint(const SkPoint& point) {
759#ifdef SK_DEBUG_SIZE
760 size_t start = fWriter.size();
761#endif
762 fWriter.writePoint(point);
763#ifdef SK_DEBUG_SIZE
764 fPointBytes += fWriter.size() - start;
765 fPointWrites++;
766#endif
767}
reed@google.com82065d62011-02-07 15:30:46 +0000768
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
770 fWriter.writeMul4(pts, count * sizeof(SkPoint));
771#ifdef SK_DEBUG_SIZE
772 fPointBytes += count * sizeof(SkPoint);
773 fPointWrites++;
774#endif
775}
776
777void SkPictureRecord::addRect(const SkRect& rect) {
778#ifdef SK_DEBUG_SIZE
779 size_t start = fWriter.size();
780#endif
781 fWriter.writeRect(rect);
782#ifdef SK_DEBUG_SIZE
783 fRectBytes += fWriter.size() - start;
784 fRectWrites++;
785#endif
786}
787
788void SkPictureRecord::addRectPtr(const SkRect* rect) {
789 if (fWriter.writeBool(rect != NULL)) {
790 fWriter.writeRect(*rect);
791 }
792}
793
reed@google.comf0b5e112011-09-07 11:57:34 +0000794void SkPictureRecord::addIRect(const SkIRect& rect) {
795 fWriter.write(&rect, sizeof(rect));
796}
797
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
799 if (fWriter.writeBool(rect != NULL)) {
800 *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
801 }
802}
803
reed@google.com4ed0fb72012-12-12 20:48:18 +0000804void SkPictureRecord::addRRect(const SkRRect& rrect) {
805 fWriter.writeRRect(rrect);
806}
807
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808void SkPictureRecord::addRegion(const SkRegion& region) {
reed@google.com83ca3372012-07-12 15:27:54 +0000809 addInt(fRegions.find(region));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810}
811
812void SkPictureRecord::addText(const void* text, size_t byteLength) {
813#ifdef SK_DEBUG_SIZE
814 size_t start = fWriter.size();
815#endif
816 addInt(byteLength);
817 fWriter.writePad(text, byteLength);
818#ifdef SK_DEBUG_SIZE
819 fTextBytes += fWriter.size() - start;
820 fTextWrites++;
821#endif
822}
823
824///////////////////////////////////////////////////////////////////////////////
825
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826#ifdef SK_DEBUG_SIZE
827size_t SkPictureRecord::size() const {
828 size_t result = 0;
829 size_t sizeData;
830 bitmaps(&sizeData);
831 result += sizeData;
832 matrices(&sizeData);
833 result += sizeData;
834 paints(&sizeData);
835 result += sizeData;
836 paths(&sizeData);
837 result += sizeData;
838 pictures(&sizeData);
839 result += sizeData;
840 regions(&sizeData);
841 result += sizeData;
842 result += streamlen();
843 return result;
844}
845
846int SkPictureRecord::bitmaps(size_t* size) const {
847 size_t result = 0;
848 int count = fBitmaps.count();
reed@google.com82065d62011-02-07 15:30:46 +0000849 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
851 *size = result;
852 return count;
853}
854
855int SkPictureRecord::matrices(size_t* size) const {
856 int count = fMatrices.count();
857 *size = sizeof(fMatrices[0]) * count;
858 return count;
859}
860
861int SkPictureRecord::paints(size_t* size) const {
862 size_t result = 0;
863 int count = fPaints.count();
reed@google.com82065d62011-02-07 15:30:46 +0000864 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 result += sizeof(fPaints[index]) + fPaints[index]->size();
866 *size = result;
867 return count;
868}
869
870int SkPictureRecord::paths(size_t* size) const {
871 size_t result = 0;
872 int count = fPaths.count();
reed@google.com82065d62011-02-07 15:30:46 +0000873 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 result += sizeof(fPaths[index]) + fPaths[index]->size();
875 *size = result;
876 return count;
877}
878
879int SkPictureRecord::regions(size_t* size) const {
880 size_t result = 0;
881 int count = fRegions.count();
reed@google.com82065d62011-02-07 15:30:46 +0000882 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883 result += sizeof(fRegions[index]) + fRegions[index]->size();
884 *size = result;
885 return count;
886}
887
888size_t SkPictureRecord::streamlen() const {
889 return fWriter.size();
890}
891#endif
892
893#ifdef SK_DEBUG_VALIDATE
894void SkPictureRecord::validate() const {
895 validateBitmaps();
896 validateMatrices();
897 validatePaints();
898 validatePaths();
899 validatePictures();
900 validateRegions();
901}
902
903void SkPictureRecord::validateBitmaps() const {
904 int count = fBitmaps.count();
905 SkASSERT((unsigned) count < 0x1000);
906 for (int index = 0; index < count; index++) {
907 const SkFlatBitmap* bitPtr = fBitmaps[index];
908 SkASSERT(bitPtr);
909 bitPtr->validate();
910 }
911}
912
913void SkPictureRecord::validateMatrices() const {
914 int count = fMatrices.count();
915 SkASSERT((unsigned) count < 0x1000);
916 for (int index = 0; index < count; index++) {
917 const SkFlatMatrix* matrix = fMatrices[index];
918 SkASSERT(matrix);
reed@google.com82065d62011-02-07 15:30:46 +0000919 matrix->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920 }
921}
922
923void SkPictureRecord::validatePaints() const {
924 int count = fPaints.count();
925 SkASSERT((unsigned) count < 0x1000);
926 for (int index = 0; index < count; index++) {
927 const SkFlatPaint* paint = fPaints[index];
928 SkASSERT(paint);
929// paint->validate();
930 }
931}
932
933void SkPictureRecord::validatePaths() const {
934 int count = fPaths.count();
935 SkASSERT((unsigned) count < 0x1000);
936 for (int index = 0; index < count; index++) {
937 const SkFlatPath* path = fPaths[index];
938 SkASSERT(path);
939 path->validate();
940 }
941}
942
943void SkPictureRecord::validateRegions() const {
944 int count = fRegions.count();
945 SkASSERT((unsigned) count < 0x1000);
946 for (int index = 0; index < count; index++) {
947 const SkFlatRegion* region = fRegions[index];
948 SkASSERT(region);
949 region->validate();
950 }
951}
952#endif
953