blob: edc67a064ed7c202504c336ffe81898bc1f7d861 [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"
rileya@google.com9f5898d2012-09-11 20:21:44 +000011#include "SkBBoxHierarchy.h"
12#include "SkPictureStateTree.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013
14#define MIN_WRITER_SIZE 16384
15#define HEAP_BLOCK_SIZE 4096
16
junov@chromium.org4e6dfa52012-07-16 14:04:59 +000017enum {
reed@google.comd86e7ab2012-09-27 20:31:31 +000018 // just need a value that save or getSaveCount would never return
junov@chromium.org4e6dfa52012-07-16 14:04:59 +000019 kNoInitialSave = -1,
20};
21
reed@google.comd86e7ab2012-09-27 20:31:31 +000022SkPictureRecord::SkPictureRecord(uint32_t flags, SkDevice* device) :
23 INHERITED(device),
robertphillips@google.com178a2672012-09-13 13:25:30 +000024 fBoundingHierarchy(NULL),
25 fStateTree(NULL),
djsollen@google.com21830d92012-08-07 19:49:41 +000026 fFlattenableHeap(HEAP_BLOCK_SIZE),
27 fMatrices(&fFlattenableHeap),
28 fPaints(&fFlattenableHeap),
29 fRegions(&fFlattenableHeap),
djsollen@google.comd2700ee2012-05-30 16:54:13 +000030 fWriter(MIN_WRITER_SIZE),
31 fRecordFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000032#ifdef SK_DEBUG_SIZE
33 fPointBytes = fRectBytes = fTextBytes = 0;
34 fPointWrites = fRectWrites = fTextWrites = 0;
35#endif
36
37 fRestoreOffsetStack.setReserve(32);
reed@google.com82065d62011-02-07 15:30:46 +000038
djsollen@google.comc9ab9872012-08-29 18:52:07 +000039 fBitmapHeap = SkNEW(SkBitmapHeap);
40 fFlattenableHeap.setBitmapStorage(fBitmapHeap);
reed@android.com8a1c16f2008-12-17 15:59:43 +000041 fPathHeap = NULL; // lazy allocate
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +000042 fFirstSavedLayerIndex = kNoSavedLayerIndex;
reed@google.comd86e7ab2012-09-27 20:31:31 +000043
44 fInitialSaveCount = kNoInitialSave;
reed@android.com8a1c16f2008-12-17 15:59:43 +000045}
46
47SkPictureRecord::~SkPictureRecord() {
djsollen@google.comc9ab9872012-08-29 18:52:07 +000048 SkSafeUnref(fBitmapHeap);
djsollen@google.com21830d92012-08-07 19:49:41 +000049 SkSafeUnref(fPathHeap);
rileya@google.com9f5898d2012-09-11 20:21:44 +000050 SkSafeUnref(fBoundingHierarchy);
51 SkSafeUnref(fStateTree);
djsollen@google.com21830d92012-08-07 19:49:41 +000052 fFlattenableHeap.setBitmapStorage(NULL);
53 fPictureRefs.unrefAll();
reed@android.com8a1c16f2008-12-17 15:59:43 +000054}
55
56///////////////////////////////////////////////////////////////////////////////
57
junov@chromium.org4e6dfa52012-07-16 14:04:59 +000058SkDevice* SkPictureRecord::setDevice(SkDevice* device) {
reed@google.comd86e7ab2012-09-27 20:31:31 +000059 SkASSERT(!"eeek, don't try to change the device on a recording canvas");
60 return this->INHERITED::setDevice(device);
junov@chromium.org4e6dfa52012-07-16 14:04:59 +000061}
62
reed@android.com8a1c16f2008-12-17 15:59:43 +000063int SkPictureRecord::save(SaveFlags flags) {
reed@google.comffacd3c2012-08-30 15:31:23 +000064 // record the offset to us, making it non-positive to distinguish a save
65 // from a clip entry.
66 fRestoreOffsetStack.push(-(int32_t)fWriter.size());
skia.committer@gmail.com11f86922012-08-31 17:14:46 +000067
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 addDraw(SAVE);
69 addInt(flags);
reed@google.com82065d62011-02-07 15:30:46 +000070
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 validate();
72 return this->INHERITED::save(flags);
73}
74
75int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
76 SaveFlags flags) {
reed@google.comffacd3c2012-08-30 15:31:23 +000077 // record the offset to us, making it non-positive to distinguish a save
78 // from a clip entry.
79 fRestoreOffsetStack.push(-(int32_t)fWriter.size());
skia.committer@gmail.com11f86922012-08-31 17:14:46 +000080
reed@android.com8a1c16f2008-12-17 15:59:43 +000081 addDraw(SAVE_LAYER);
82 addRectPtr(bounds);
83 addPaintPtr(paint);
84 addInt(flags);
85
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +000086 if (kNoSavedLayerIndex == fFirstSavedLayerIndex) {
87 fFirstSavedLayerIndex = fRestoreOffsetStack.count();
88 }
89
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 validate();
reed@android.com261ae4d2009-10-02 16:37:46 +000091 /* Don't actually call saveLayer, because that will try to allocate an
92 offscreen device (potentially very big) which we don't actually need
93 at this time (and may not be able to afford since during record our
94 clip starts out the size of the picture, which is often much larger
95 than the size of the actual device we'll use during playback).
96 */
junov@chromium.orga907ac32012-02-24 21:54:07 +000097 int count = this->INHERITED::save(flags);
98 this->clipRectBounds(bounds, flags, NULL);
99 return count;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100}
101
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000102bool SkPictureRecord::isDrawingToLayer() const {
103 return fFirstSavedLayerIndex != kNoSavedLayerIndex;
104}
105
reed@google.comffacd3c2012-08-30 15:31:23 +0000106// Return the size of the specified drawType's recorded block, or 0 if this verb
107// is variable sized, and therefore not known.
108static inline uint32_t getSkipableSize(unsigned drawType) {
109 static const uint8_t gSizes[LAST_DRAWTYPE_ENUM + 1] = {
110 0, // UNUSED,
111 4, // CLIP_PATH,
112 4, // CLIP_REGION,
113 7, // CLIP_RECT,
114 2, // CONCAT,
115 0, // DRAW_BITMAP,
116 0, // DRAW_BITMAP_MATRIX,
117 0, // DRAW_BITMAP_NINE,
118 0, // DRAW_BITMAP_RECT,
119 0, // DRAW_CLEAR,
120 0, // DRAW_DATA,
121 0, // DRAW_PAINT,
122 0, // DRAW_PATH,
123 0, // DRAW_PICTURE,
124 0, // DRAW_POINTS,
125 0, // DRAW_POS_TEXT,
126 0, // DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT
127 0, // DRAW_POS_TEXT_H,
128 0, // DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
129 0, // DRAW_RECT,
130 0, // DRAW_SPRITE,
131 0, // DRAW_TEXT,
132 0, // DRAW_TEXT_ON_PATH,
133 0, // DRAW_TEXT_TOP_BOTTOM, // fast variant of DRAW_TEXT
134 0, // DRAW_VERTICES,
135 0, // RESTORE,
136 2, // ROTATE,
137 2, // SAVE,
138 0, // SAVE_LAYER,
139 3, // SCALE,
140 2, // SET_MATRIX,
141 3, // SKEW,
142 3, // TRANSLATE,
143 };
144
145 SkASSERT(sizeof(gSizes) == LAST_DRAWTYPE_ENUM + 1);
146 SkASSERT((unsigned)drawType <= (unsigned)LAST_DRAWTYPE_ENUM);
147 return gSizes[drawType] * sizeof(uint32_t);
148}
149
150#ifdef TRACK_COLLAPSE_STATS
151 static int gCollapseCount, gCollapseCalls;
152#endif
153
154/*
155 * Restore has just been called (but not recoreded), so look back at the
156 * matching save(), and see if we can eliminate the pair of them, due to no
157 * intervening matrix/clip calls.
158 *
159 * If so, update the writer and return true, in which case we won't even record
160 * the restore() call. If we still need the restore(), return false.
161 */
162static bool collapseSaveClipRestore(SkWriter32* writer, int32_t offset) {
reed@google.com6132eb92012-09-14 19:07:28 +0000163 // Some unexplained crashes in Chrome may be caused by this. Disabling
164 // for now to see if it helps.
165 // crbug.com/147406
reed@google.coma3301b62012-10-02 15:54:26 +0000166#ifdef SK_DISABLE_PICTURE_PEEPHOLE_OPTIMIZATION
reed@google.com6132eb92012-09-14 19:07:28 +0000167 return false;
168#endif
169
reed@google.comffacd3c2012-08-30 15:31:23 +0000170#ifdef TRACK_COLLAPSE_STATS
171 gCollapseCalls += 1;
172#endif
173
174 int32_t restoreOffset = (int32_t)writer->size();
175
176 // back up to the save block
177 while (offset > 0) {
178 offset = *writer->peek32(offset);
179 }
180
181 // now offset points to a save
182 offset = -offset;
183 if (SAVE_LAYER == *writer->peek32(offset)) {
184 // not ready to cull these out yet (mrr)
185 return false;
186 }
187 SkASSERT(SAVE == *writer->peek32(offset));
188
189 // Walk forward until we get back to either a draw-verb (abort) or we hit
190 // our restore (success).
191 int32_t saveOffset = offset;
192
193 offset += getSkipableSize(SAVE);
194 while (offset < restoreOffset) {
195 uint32_t* block = writer->peek32(offset);
196 uint32_t op = *block;
197 uint32_t opSize = getSkipableSize(op);
198 if (0 == opSize) {
199 // drawing verb, abort
200 return false;
201 }
202 offset += opSize;
203 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000204
reed@google.comffacd3c2012-08-30 15:31:23 +0000205#ifdef TRACK_COLLAPSE_STATS
206 gCollapseCount += 1;
207 SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls,
208 (double)gCollapseCount / gCollapseCalls, "%");
209#endif
210
211 writer->rewindToOffset(saveOffset);
212 return true;
213}
214
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215void SkPictureRecord::restore() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000216 // FIXME: SkDeferredCanvas needs to be refactored to respect
217 // save/restore balancing so that the following test can be
218 // turned on permanently.
219#if 0
220 SkASSERT(fRestoreOffsetStack.count() > 1);
221#endif
222
reed@android.comb4e22d62009-07-09 15:20:25 +0000223 // check for underflow
224 if (fRestoreOffsetStack.count() == 0) {
225 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 }
reed@android.comb4e22d62009-07-09 15:20:25 +0000227
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000228 if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
229 fFirstSavedLayerIndex = kNoSavedLayerIndex;
230 }
231
reed@google.comffacd3c2012-08-30 15:31:23 +0000232 if (!collapseSaveClipRestore(&fWriter, fRestoreOffsetStack.top())) {
233 fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
234 this->addDraw(RESTORE);
235 }
236
reed@android.comb4e22d62009-07-09 15:20:25 +0000237 fRestoreOffsetStack.pop();
reed@android.com32a42492009-07-10 03:33:52 +0000238
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 validate();
240 return this->INHERITED::restore();
241}
242
243bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) {
244 addDraw(TRANSLATE);
245 addScalar(dx);
246 addScalar(dy);
247 validate();
248 return this->INHERITED::translate(dx, dy);
249}
250
251bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) {
reed@google.com82065d62011-02-07 15:30:46 +0000252 addDraw(SCALE);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 addScalar(sx);
254 addScalar(sy);
255 validate();
256 return this->INHERITED::scale(sx, sy);
257}
258
259bool SkPictureRecord::rotate(SkScalar degrees) {
reed@google.com82065d62011-02-07 15:30:46 +0000260 addDraw(ROTATE);
261 addScalar(degrees);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 validate();
263 return this->INHERITED::rotate(degrees);
264}
265
266bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) {
reed@google.com82065d62011-02-07 15:30:46 +0000267 addDraw(SKEW);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 addScalar(sx);
269 addScalar(sy);
270 validate();
271 return this->INHERITED::skew(sx, sy);
272}
273
274bool SkPictureRecord::concat(const SkMatrix& matrix) {
275 validate();
276 addDraw(CONCAT);
277 addMatrix(matrix);
278 validate();
279 return this->INHERITED::concat(matrix);
280}
281
reed@android.com6e073b92009-01-06 15:03:30 +0000282void SkPictureRecord::setMatrix(const SkMatrix& matrix) {
283 validate();
284 addDraw(SET_MATRIX);
285 addMatrix(matrix);
286 validate();
287 this->INHERITED::setMatrix(matrix);
288}
289
reed@google.com45482d12011-08-29 19:02:39 +0000290static bool regionOpExpands(SkRegion::Op op) {
291 switch (op) {
292 case SkRegion::kUnion_Op:
293 case SkRegion::kXOR_Op:
294 case SkRegion::kReverseDifference_Op:
295 case SkRegion::kReplace_Op:
296 return true;
297 case SkRegion::kIntersect_Op:
298 case SkRegion::kDifference_Op:
299 return false;
300 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000301 SkDEBUGFAIL("unknown region op");
reed@google.com45482d12011-08-29 19:02:39 +0000302 return false;
303 }
304}
305
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000306void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(
307 uint32_t restoreOffset) {
reed@google.comffacd3c2012-08-30 15:31:23 +0000308 int32_t offset = fRestoreOffsetStack.top();
309 while (offset > 0) {
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000310 uint32_t* peek = fWriter.peek32(offset);
311 offset = *peek;
312 *peek = restoreOffset;
313 }
skia.committer@gmail.com11f86922012-08-31 17:14:46 +0000314
reed@google.comffacd3c2012-08-30 15:31:23 +0000315#ifdef SK_DEBUG
316 // assert that the final offset value points to a save verb
317 uint32_t drawOp = *fWriter.peek32(-offset);
318 SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
319#endif
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000320}
321
reed@google.comd86e7ab2012-09-27 20:31:31 +0000322void SkPictureRecord::beginRecording() {
323 // we have to call this *after* our constructor, to ensure that it gets
324 // recorded. This is balanced by restoreToCount() call from endRecording,
325 // which in-turn calls our overridden restore(), so those get recorded too.
326 fInitialSaveCount = this->save(kMatrixClip_SaveFlag);
327}
328
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000329void SkPictureRecord::endRecording() {
junov@chromium.org4e6dfa52012-07-16 14:04:59 +0000330 SkASSERT(kNoInitialSave != fInitialSaveCount);
331 this->restoreToCount(fInitialSaveCount);
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000332}
333
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000334void SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
reed@google.com21b519d2012-10-02 17:42:15 +0000335 if (fRestoreOffsetStack.isEmpty()) {
336 return;
337 }
338
reed@google.com45482d12011-08-29 19:02:39 +0000339 if (regionOpExpands(op)) {
340 // Run back through any previous clip ops, and mark their offset to
341 // be 0, disabling their ability to trigger a jump-to-restore, otherwise
342 // they could hide this clips ability to expand the clip (i.e. go from
343 // empty to non-empty).
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000344 fillRestoreOffsetPlaceholdersForCurrentStackLevel(0);
reed@google.com45482d12011-08-29 19:02:39 +0000345 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000346
reed@google.com45482d12011-08-29 19:02:39 +0000347 size_t offset = fWriter.size();
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000348 // The RestoreOffset field is initially filled with a placeholder
349 // value that points to the offset of the previous RestoreOffset
350 // in the current stack level, thus forming a linked list so that
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000351 // the restore offsets can be filled in when the corresponding
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000352 // restore command is recorded.
reed@google.com45482d12011-08-29 19:02:39 +0000353 addInt(fRestoreOffsetStack.top());
354 fRestoreOffsetStack.top() = offset;
355}
356
reed@google.com071eef92011-10-12 11:52:53 +0000357bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358 addDraw(CLIP_RECT);
359 addRect(rect);
reed@google.com83ab4952011-11-11 21:34:54 +0000360 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000361 recordRestoreOffsetPlaceholder(op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362
363 validate();
reed@google.com071eef92011-10-12 11:52:53 +0000364 return this->INHERITED::clipRect(rect, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365}
366
reed@google.com071eef92011-10-12 11:52:53 +0000367bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368 addDraw(CLIP_PATH);
369 addPath(path);
reed@google.com83ab4952011-11-11 21:34:54 +0000370 addInt(ClipParams_pack(op, doAA));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000371 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000372
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 validate();
reed@google.com82065d62011-02-07 15:30:46 +0000374
reed@android.comae814c82009-02-13 14:56:09 +0000375 if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
reed@google.com071eef92011-10-12 11:52:53 +0000376 return this->INHERITED::clipRect(path.getBounds(), op, doAA);
reed@android.comae814c82009-02-13 14:56:09 +0000377 } else {
reed@google.com071eef92011-10-12 11:52:53 +0000378 return this->INHERITED::clipPath(path, op, doAA);
reed@android.comae814c82009-02-13 14:56:09 +0000379 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380}
381
382bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) {
reed@google.com82065d62011-02-07 15:30:46 +0000383 addDraw(CLIP_REGION);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384 addRegion(region);
reed@google.com83ab4952011-11-11 21:34:54 +0000385 addInt(ClipParams_pack(op, false));
junov@chromium.orge3dbedb2012-07-09 16:03:55 +0000386 recordRestoreOffsetPlaceholder(op);
reed@google.com82065d62011-02-07 15:30:46 +0000387
reed@android.com8a1c16f2008-12-17 15:59:43 +0000388 validate();
389 return this->INHERITED::clipRegion(region, op);
390}
391
reed@google.com2a981812011-04-14 18:59:28 +0000392void SkPictureRecord::clear(SkColor color) {
393 addDraw(DRAW_CLEAR);
394 addInt(color);
395 validate();
396}
397
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398void SkPictureRecord::drawPaint(const SkPaint& paint) {
399 addDraw(DRAW_PAINT);
400 addPaint(paint);
401 validate();
402}
403
404void SkPictureRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
405 const SkPaint& paint) {
406 addDraw(DRAW_POINTS);
407 addPaint(paint);
408 addInt(mode);
409 addInt(count);
410 fWriter.writeMul4(pts, count * sizeof(SkPoint));
411 validate();
412}
413
414void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
415 addDraw(DRAW_RECT);
416 addPaint(paint);
417 addRect(rect);
418 validate();
419}
420
421void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
422 addDraw(DRAW_PATH);
423 addPaint(paint);
424 addPath(path);
425 validate();
426}
427
428void SkPictureRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
429 const SkPaint* paint = NULL) {
430 addDraw(DRAW_BITMAP);
431 addPaintPtr(paint);
432 addBitmap(bitmap);
433 addScalar(left);
434 addScalar(top);
435 validate();
436}
437
reed@google.com71121732012-09-18 15:14:33 +0000438void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439 const SkRect& dst, const SkPaint* paint) {
reed@google.com71121732012-09-18 15:14:33 +0000440 addDraw(DRAW_BITMAP_RECT_TO_RECT);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 addPaintPtr(paint);
442 addBitmap(bitmap);
reed@google.com71121732012-09-18 15:14:33 +0000443 addRectPtr(src); // may be null
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 addRect(dst);
445 validate();
446}
447
448void SkPictureRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
reed@google.comf0b5e112011-09-07 11:57:34 +0000449 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 addDraw(DRAW_BITMAP_MATRIX);
451 addPaintPtr(paint);
452 addBitmap(bitmap);
453 addMatrix(matrix);
454 validate();
455}
456
reed@google.comf0b5e112011-09-07 11:57:34 +0000457void SkPictureRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
458 const SkRect& dst, const SkPaint* paint) {
459 addDraw(DRAW_BITMAP_NINE);
460 addPaintPtr(paint);
461 addBitmap(bitmap);
462 addIRect(center);
463 addRect(dst);
464 validate();
465}
466
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
468 const SkPaint* paint = NULL) {
469 addDraw(DRAW_SPRITE);
470 addPaintPtr(paint);
471 addBitmap(bitmap);
472 addInt(left);
473 addInt(top);
474 validate();
475}
476
477void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint,
reed@google.com9efd9a02012-01-30 15:41:43 +0000478 SkScalar minY, SkScalar maxY) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479 SkPaint::FontMetrics metrics;
480 paint.getFontMetrics(&metrics);
481 SkRect bounds;
482 // construct a rect so we can see any adjustments from the paint.
483 // we use 0,1 for left,right, just so the rect isn't empty
reed@google.com9efd9a02012-01-30 15:41:43 +0000484 bounds.set(0, metrics.fTop + minY,
485 SK_Scalar1, metrics.fBottom + maxY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 (void)paint.computeFastBounds(bounds, &bounds);
487 // now record the top and bottom
488 addScalar(bounds.fTop);
489 addScalar(bounds.fBottom);
490}
491
reed@google.com82065d62011-02-07 15:30:46 +0000492void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 SkScalar y, const SkPaint& paint) {
reed@google.com2eb5bb12012-04-12 14:27:42 +0000494 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com82065d62011-02-07 15:30:46 +0000495
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 addDraw(fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT);
497 addPaint(paint);
498 addText(text, byteLength);
499 addScalar(x);
500 addScalar(y);
501 if (fast) {
reed@google.com9efd9a02012-01-30 15:41:43 +0000502 addFontMetricsTopBottom(paint, y, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 }
504 validate();
505}
506
reed@google.com82065d62011-02-07 15:30:46 +0000507void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 const SkPoint pos[], const SkPaint& paint) {
509 size_t points = paint.countText(text, byteLength);
510 if (0 == points)
511 return;
512
513 bool canUseDrawH = true;
reed@google.com9efd9a02012-01-30 15:41:43 +0000514 SkScalar minY = pos[0].fY;
515 SkScalar maxY = pos[0].fY;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 // check if the caller really should have used drawPosTextH()
517 {
518 const SkScalar firstY = pos[0].fY;
519 for (size_t index = 1; index < points; index++) {
520 if (pos[index].fY != firstY) {
521 canUseDrawH = false;
reed@google.com9efd9a02012-01-30 15:41:43 +0000522 if (pos[index].fY < minY) {
523 minY = pos[index].fY;
524 } else if (pos[index].fY > maxY) {
525 maxY = pos[index].fY;
526 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 }
528 }
529 }
reed@google.com82065d62011-02-07 15:30:46 +0000530
reed@google.com2eb5bb12012-04-12 14:27:42 +0000531 bool fastBounds = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@google.com9efd9a02012-01-30 15:41:43 +0000532 bool fast = canUseDrawH && fastBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533
534 if (fast) {
535 addDraw(DRAW_POS_TEXT_H_TOP_BOTTOM);
reed@google.com9efd9a02012-01-30 15:41:43 +0000536 } else if (canUseDrawH) {
537 addDraw(DRAW_POS_TEXT_H);
538 } else if (fastBounds) {
539 addDraw(DRAW_POS_TEXT_TOP_BOTTOM);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 } else {
reed@google.com9efd9a02012-01-30 15:41:43 +0000541 addDraw(DRAW_POS_TEXT);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 }
543 addPaint(paint);
544 addText(text, byteLength);
545 addInt(points);
546
547#ifdef SK_DEBUG_SIZE
548 size_t start = fWriter.size();
549#endif
550 if (canUseDrawH) {
551 if (fast) {
reed@google.com9efd9a02012-01-30 15:41:43 +0000552 addFontMetricsTopBottom(paint, pos[0].fY, pos[0].fY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 }
554 addScalar(pos[0].fY);
555 SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
reed@google.com82065d62011-02-07 15:30:46 +0000556 for (size_t index = 0; index < points; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 *xptr++ = pos[index].fX;
558 }
559 else {
560 fWriter.writeMul4(pos, points * sizeof(SkPoint));
reed@google.com9efd9a02012-01-30 15:41:43 +0000561 if (fastBounds) {
562 addFontMetricsTopBottom(paint, minY, maxY);
563 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 }
565#ifdef SK_DEBUG_SIZE
566 fPointBytes += fWriter.size() - start;
567 fPointWrites += points;
568#endif
569 validate();
570}
571
572void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
573 const SkScalar xpos[], SkScalar constY,
574 const SkPaint& paint) {
575 size_t points = paint.countText(text, byteLength);
576 if (0 == points)
577 return;
reed@google.com82065d62011-02-07 15:30:46 +0000578
reed@google.com2eb5bb12012-04-12 14:27:42 +0000579 bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580
581 addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H);
582 addPaint(paint);
583 addText(text, byteLength);
584 addInt(points);
reed@google.com82065d62011-02-07 15:30:46 +0000585
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586#ifdef SK_DEBUG_SIZE
587 size_t start = fWriter.size();
588#endif
589 if (fast) {
reed@google.com9efd9a02012-01-30 15:41:43 +0000590 addFontMetricsTopBottom(paint, constY, constY);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 }
592 addScalar(constY);
593 fWriter.writeMul4(xpos, points * sizeof(SkScalar));
594#ifdef SK_DEBUG_SIZE
595 fPointBytes += fWriter.size() - start;
596 fPointWrites += points;
597#endif
598 validate();
599}
600
reed@google.com82065d62011-02-07 15:30:46 +0000601void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength,
602 const SkPath& path, const SkMatrix* matrix,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 const SkPaint& paint) {
604 addDraw(DRAW_TEXT_ON_PATH);
605 addPaint(paint);
606 addText(text, byteLength);
607 addPath(path);
608 addMatrixPtr(matrix);
609 validate();
610}
611
612void SkPictureRecord::drawPicture(SkPicture& picture) {
613 addDraw(DRAW_PICTURE);
614 addPicture(picture);
615 validate();
616}
617
618void SkPictureRecord::drawVertices(VertexMode vmode, int vertexCount,
619 const SkPoint vertices[], const SkPoint texs[],
620 const SkColor colors[], SkXfermode*,
621 const uint16_t indices[], int indexCount,
622 const SkPaint& paint) {
623 uint32_t flags = 0;
624 if (texs) {
625 flags |= DRAW_VERTICES_HAS_TEXS;
626 }
627 if (colors) {
628 flags |= DRAW_VERTICES_HAS_COLORS;
629 }
630 if (indexCount > 0) {
631 flags |= DRAW_VERTICES_HAS_INDICES;
632 }
633
634 addDraw(DRAW_VERTICES);
635 addPaint(paint);
636 addInt(flags);
637 addInt(vmode);
638 addInt(vertexCount);
639 addPoints(vertices, vertexCount);
640 if (flags & DRAW_VERTICES_HAS_TEXS) {
641 addPoints(texs, vertexCount);
642 }
643 if (flags & DRAW_VERTICES_HAS_COLORS) {
644 fWriter.writeMul4(colors, vertexCount * sizeof(SkColor));
645 }
646 if (flags & DRAW_VERTICES_HAS_INDICES) {
647 addInt(indexCount);
648 fWriter.writePad(indices, indexCount * sizeof(uint16_t));
649 }
650}
651
reed@android.comcb608442009-12-04 21:32:27 +0000652void SkPictureRecord::drawData(const void* data, size_t length) {
653 addDraw(DRAW_DATA);
654 addInt(length);
655 fWriter.writePad(data, length);
656}
657
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658///////////////////////////////////////////////////////////////////////////////
reed@google.com82065d62011-02-07 15:30:46 +0000659
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
djsollen@google.comc9ab9872012-08-29 18:52:07 +0000661 addInt(fBitmapHeap->insert(bitmap));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662}
663
664void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
665 addMatrixPtr(&matrix);
666}
667
668void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
reed@google.com83ca3372012-07-12 15:27:54 +0000669 this->addInt(matrix ? fMatrices.find(*matrix) : 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670}
671
672void SkPictureRecord::addPaint(const SkPaint& paint) {
673 addPaintPtr(&paint);
674}
675
676void SkPictureRecord::addPaintPtr(const SkPaint* paint) {
scroggo@google.com4dffc592012-07-17 16:49:40 +0000677 this->addInt(paint ? fPaints.find(*paint) : 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678}
679
680void SkPictureRecord::addPath(const SkPath& path) {
681 if (NULL == fPathHeap) {
682 fPathHeap = SkNEW(SkPathHeap);
683 }
684 addInt(fPathHeap->append(path));
685}
686
687void SkPictureRecord::addPicture(SkPicture& picture) {
688 int index = fPictureRefs.find(&picture);
689 if (index < 0) { // not found
690 index = fPictureRefs.count();
691 *fPictureRefs.append() = &picture;
692 picture.ref();
693 }
694 // follow the convention of recording a 1-based index
695 addInt(index + 1);
696}
697
698void SkPictureRecord::addPoint(const SkPoint& point) {
699#ifdef SK_DEBUG_SIZE
700 size_t start = fWriter.size();
701#endif
702 fWriter.writePoint(point);
703#ifdef SK_DEBUG_SIZE
704 fPointBytes += fWriter.size() - start;
705 fPointWrites++;
706#endif
707}
reed@google.com82065d62011-02-07 15:30:46 +0000708
reed@android.com8a1c16f2008-12-17 15:59:43 +0000709void SkPictureRecord::addPoints(const SkPoint pts[], int count) {
710 fWriter.writeMul4(pts, count * sizeof(SkPoint));
711#ifdef SK_DEBUG_SIZE
712 fPointBytes += count * sizeof(SkPoint);
713 fPointWrites++;
714#endif
715}
716
717void SkPictureRecord::addRect(const SkRect& rect) {
718#ifdef SK_DEBUG_SIZE
719 size_t start = fWriter.size();
720#endif
721 fWriter.writeRect(rect);
722#ifdef SK_DEBUG_SIZE
723 fRectBytes += fWriter.size() - start;
724 fRectWrites++;
725#endif
726}
727
728void SkPictureRecord::addRectPtr(const SkRect* rect) {
729 if (fWriter.writeBool(rect != NULL)) {
730 fWriter.writeRect(*rect);
731 }
732}
733
reed@google.comf0b5e112011-09-07 11:57:34 +0000734void SkPictureRecord::addIRect(const SkIRect& rect) {
735 fWriter.write(&rect, sizeof(rect));
736}
737
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738void SkPictureRecord::addIRectPtr(const SkIRect* rect) {
739 if (fWriter.writeBool(rect != NULL)) {
740 *(SkIRect*)fWriter.reserve(sizeof(SkIRect)) = *rect;
741 }
742}
743
744void SkPictureRecord::addRegion(const SkRegion& region) {
reed@google.com83ca3372012-07-12 15:27:54 +0000745 addInt(fRegions.find(region));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746}
747
748void SkPictureRecord::addText(const void* text, size_t byteLength) {
749#ifdef SK_DEBUG_SIZE
750 size_t start = fWriter.size();
751#endif
752 addInt(byteLength);
753 fWriter.writePad(text, byteLength);
754#ifdef SK_DEBUG_SIZE
755 fTextBytes += fWriter.size() - start;
756 fTextWrites++;
757#endif
758}
759
760///////////////////////////////////////////////////////////////////////////////
761
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762#ifdef SK_DEBUG_SIZE
763size_t SkPictureRecord::size() const {
764 size_t result = 0;
765 size_t sizeData;
766 bitmaps(&sizeData);
767 result += sizeData;
768 matrices(&sizeData);
769 result += sizeData;
770 paints(&sizeData);
771 result += sizeData;
772 paths(&sizeData);
773 result += sizeData;
774 pictures(&sizeData);
775 result += sizeData;
776 regions(&sizeData);
777 result += sizeData;
778 result += streamlen();
779 return result;
780}
781
782int SkPictureRecord::bitmaps(size_t* size) const {
783 size_t result = 0;
784 int count = fBitmaps.count();
reed@google.com82065d62011-02-07 15:30:46 +0000785 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 result += sizeof(fBitmaps[index]) + fBitmaps[index]->size();
787 *size = result;
788 return count;
789}
790
791int SkPictureRecord::matrices(size_t* size) const {
792 int count = fMatrices.count();
793 *size = sizeof(fMatrices[0]) * count;
794 return count;
795}
796
797int SkPictureRecord::paints(size_t* size) const {
798 size_t result = 0;
799 int count = fPaints.count();
reed@google.com82065d62011-02-07 15:30:46 +0000800 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 result += sizeof(fPaints[index]) + fPaints[index]->size();
802 *size = result;
803 return count;
804}
805
806int SkPictureRecord::paths(size_t* size) const {
807 size_t result = 0;
808 int count = fPaths.count();
reed@google.com82065d62011-02-07 15:30:46 +0000809 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 result += sizeof(fPaths[index]) + fPaths[index]->size();
811 *size = result;
812 return count;
813}
814
815int SkPictureRecord::regions(size_t* size) const {
816 size_t result = 0;
817 int count = fRegions.count();
reed@google.com82065d62011-02-07 15:30:46 +0000818 for (int index = 0; index < count; index++)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 result += sizeof(fRegions[index]) + fRegions[index]->size();
820 *size = result;
821 return count;
822}
823
824size_t SkPictureRecord::streamlen() const {
825 return fWriter.size();
826}
827#endif
828
829#ifdef SK_DEBUG_VALIDATE
830void SkPictureRecord::validate() const {
831 validateBitmaps();
832 validateMatrices();
833 validatePaints();
834 validatePaths();
835 validatePictures();
836 validateRegions();
837}
838
839void SkPictureRecord::validateBitmaps() const {
840 int count = fBitmaps.count();
841 SkASSERT((unsigned) count < 0x1000);
842 for (int index = 0; index < count; index++) {
843 const SkFlatBitmap* bitPtr = fBitmaps[index];
844 SkASSERT(bitPtr);
845 bitPtr->validate();
846 }
847}
848
849void SkPictureRecord::validateMatrices() const {
850 int count = fMatrices.count();
851 SkASSERT((unsigned) count < 0x1000);
852 for (int index = 0; index < count; index++) {
853 const SkFlatMatrix* matrix = fMatrices[index];
854 SkASSERT(matrix);
reed@google.com82065d62011-02-07 15:30:46 +0000855 matrix->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 }
857}
858
859void SkPictureRecord::validatePaints() const {
860 int count = fPaints.count();
861 SkASSERT((unsigned) count < 0x1000);
862 for (int index = 0; index < count; index++) {
863 const SkFlatPaint* paint = fPaints[index];
864 SkASSERT(paint);
865// paint->validate();
866 }
867}
868
869void SkPictureRecord::validatePaths() const {
870 int count = fPaths.count();
871 SkASSERT((unsigned) count < 0x1000);
872 for (int index = 0; index < count; index++) {
873 const SkFlatPath* path = fPaths[index];
874 SkASSERT(path);
875 path->validate();
876 }
877}
878
879void SkPictureRecord::validateRegions() const {
880 int count = fRegions.count();
881 SkASSERT((unsigned) count < 0x1000);
882 for (int index = 0; index < count; index++) {
883 const SkFlatRegion* region = fRegions[index];
884 SkASSERT(region);
885 region->validate();
886 }
887}
888#endif
889