[go: nahoru, domu]

blob: 3b04906e0919e2358d4e22e1e15d76e7418ceea2 [file] [log] [blame]
epoger@google.com685cfc02011-07-28 14:26:00 +00001
reed@android.combcd4d5a2008-12-17 15:59:43 +00002/*
epoger@google.com685cfc02011-07-28 14:26:00 +00003 * Copyright 2007 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
scroggo@google.com8b854502013-05-07 14:45:40 +00009
reed@android.combcd4d5a2008-12-17 15:59:43 +000010#include "SkPictureFlat.h"
11#include "SkPicturePlayback.h"
12#include "SkPictureRecord.h"
13
robertphillips@google.com7aefc822014-04-18 18:04:41 +000014#include "SkBBHFactory.h"
robertphillips@google.com2fd23902013-08-29 11:54:56 +000015#include "SkBitmapDevice.h"
reed@android.combcd4d5a2008-12-17 15:59:43 +000016#include "SkCanvas.h"
17#include "SkChunkAlloc.h"
18#include "SkPicture.h"
19#include "SkRegion.h"
20#include "SkStream.h"
21#include "SkTDArray.h"
22#include "SkTSearch.h"
23#include "SkTime.h"
24
25#include "SkReader32.h"
26#include "SkWriter32.h"
rileya@google.com7fb3d2f2012-09-13 21:41:51 +000027#include "SkRTree.h"
28#include "SkBBoxHierarchyRecord.h"
reed@android.combcd4d5a2008-12-17 15:59:43 +000029
commit-bot@chromium.org95cdc162014-03-19 21:24:25 +000030#if SK_SUPPORT_GPU
31#include "GrContext.h"
32#endif
33
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +000034template <typename T> int SafeCount(const T* obj) {
35 return obj ? obj->count() : 0;
36}
37
reed@android.combcd4d5a2008-12-17 15:59:43 +000038#define DUMP_BUFFER_SIZE 65536
39
40//#define ENABLE_TIME_DRAW // dumps milliseconds for each draw
41
42
43#ifdef SK_DEBUG
reed@google.com91911482011-02-07 15:30:46 +000044// enable SK_DEBUG_TRACE to trace DrawType elements when
reed@android.combcd4d5a2008-12-17 15:59:43 +000045// recorded and played back
46// #define SK_DEBUG_TRACE
47// enable SK_DEBUG_SIZE to see the size of picture components
48// #define SK_DEBUG_SIZE
49// enable SK_DEBUG_DUMP to see the contents of recorded elements
50// #define SK_DEBUG_DUMP
51// enable SK_DEBUG_VALIDATE to check internal structures for consistency
52// #define SK_DEBUG_VALIDATE
53#endif
54
55#if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP
56const char* DrawTypeToString(DrawType drawType) {
57 switch (drawType) {
58 case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break;
59 case CLIP_PATH: return "CLIP_PATH";
60 case CLIP_REGION: return "CLIP_REGION";
61 case CLIP_RECT: return "CLIP_RECT";
robertphillips@google.come7189be2013-02-15 17:19:15 +000062 case CLIP_RRECT: return "CLIP_RRECT";
reed@android.combcd4d5a2008-12-17 15:59:43 +000063 case CONCAT: return "CONCAT";
64 case DRAW_BITMAP: return "DRAW_BITMAP";
65 case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX";
robertphillips@google.come7189be2013-02-15 17:19:15 +000066 case DRAW_BITMAP_NINE: return "DRAW_BITMAP_NINE";
67 case DRAW_BITMAP_RECT_TO_RECT: return "DRAW_BITMAP_RECT_TO_RECT";
68 case DRAW_CLEAR: return "DRAW_CLEAR";
69 case DRAW_DATA: return "DRAW_DATA";
70 case DRAW_OVAL: return "DRAW_OVAL";
reed@android.combcd4d5a2008-12-17 15:59:43 +000071 case DRAW_PAINT: return "DRAW_PAINT";
72 case DRAW_PATH: return "DRAW_PATH";
73 case DRAW_PICTURE: return "DRAW_PICTURE";
74 case DRAW_POINTS: return "DRAW_POINTS";
75 case DRAW_POS_TEXT: return "DRAW_POS_TEXT";
robertphillips@google.come7189be2013-02-15 17:19:15 +000076 case DRAW_POS_TEXT_TOP_BOTTOM: return "DRAW_POS_TEXT_TOP_BOTTOM";
reed@android.combcd4d5a2008-12-17 15:59:43 +000077 case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H";
robertphillips@google.come7189be2013-02-15 17:19:15 +000078 case DRAW_POS_TEXT_H_TOP_BOTTOM: return "DRAW_POS_TEXT_H_TOP_BOTTOM";
79 case DRAW_RECT: return "DRAW_RECT";
80 case DRAW_RRECT: return "DRAW_RRECT";
reed@android.combcd4d5a2008-12-17 15:59:43 +000081 case DRAW_SPRITE: return "DRAW_SPRITE";
82 case DRAW_TEXT: return "DRAW_TEXT";
83 case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH";
robertphillips@google.come7189be2013-02-15 17:19:15 +000084 case DRAW_TEXT_TOP_BOTTOM: return "DRAW_TEXT_TOP_BOTTOM";
85 case DRAW_VERTICES: return "DRAW_VERTICES";
reed@android.combcd4d5a2008-12-17 15:59:43 +000086 case RESTORE: return "RESTORE";
87 case ROTATE: return "ROTATE";
88 case SAVE: return "SAVE";
89 case SAVE_LAYER: return "SAVE_LAYER";
90 case SCALE: return "SCALE";
robertphillips@google.come7189be2013-02-15 17:19:15 +000091 case SET_MATRIX: return "SET_MATRIX";
reed@android.combcd4d5a2008-12-17 15:59:43 +000092 case SKEW: return "SKEW";
93 case TRANSLATE: return "TRANSLATE";
robertphillips@google.come7189be2013-02-15 17:19:15 +000094 case NOOP: return "NOOP";
reed@google.com91911482011-02-07 15:30:46 +000095 default:
96 SkDebugf("DrawType error 0x%08x\n", drawType);
97 SkASSERT(0);
reed@android.combcd4d5a2008-12-17 15:59:43 +000098 break;
99 }
reed@google.com91911482011-02-07 15:30:46 +0000100 SkASSERT(0);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000101 return NULL;
102}
103#endif
104
105#ifdef SK_DEBUG_VALIDATE
106static void validateMatrix(const SkMatrix* matrix) {
107 SkScalar scaleX = matrix->getScaleX();
108 SkScalar scaleY = matrix->getScaleY();
109 SkScalar skewX = matrix->getSkewX();
110 SkScalar skewY = matrix->getSkewY();
111 SkScalar perspX = matrix->getPerspX();
112 SkScalar perspY = matrix->getPerspY();
113 if (scaleX != 0 && skewX != 0)
114 SkDebugf("scaleX != 0 && skewX != 0\n");
115 SkASSERT(scaleX == 0 || skewX == 0);
116 SkASSERT(scaleY == 0 || skewY == 0);
117 SkASSERT(perspX == 0);
118 SkASSERT(perspY == 0);
119}
120#endif
121
122
123///////////////////////////////////////////////////////////////////////////////
124
commit-bot@chromium.org7a1832f2014-04-24 21:53:13 +0000125SkPicture::SkPicture()
126 : fAccelData(NULL) {
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000127 this->needsNewGenID();
reed@android.combcd4d5a2008-12-17 15:59:43 +0000128 fRecord = NULL;
129 fPlayback = NULL;
130 fWidth = fHeight = 0;
131}
132
skia.committer@gmail.com8b5598b2014-03-17 03:02:17 +0000133SkPicture::SkPicture(const SkPicture& src)
commit-bot@chromium.org73857a32014-03-16 19:46:36 +0000134 : INHERITED()
commit-bot@chromium.org7a1832f2014-04-24 21:53:13 +0000135 , fAccelData(NULL)
136 , fContentInfo(src.fContentInfo) {
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000137 this->needsNewGenID();
reed@android.combcd4d5a2008-12-17 15:59:43 +0000138 fWidth = src.fWidth;
139 fHeight = src.fHeight;
140 fRecord = NULL;
141
142 /* We want to copy the src's playback. However, if that hasn't been built
143 yet, we need to fake a call to endRecording() without actually calling
144 it (since it is destructive, and we don't want to change src).
145 */
146 if (src.fPlayback) {
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000147 fPlayback = SkNEW_ARGS(SkPicturePlayback, (this, *src.fPlayback));
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000148 SkASSERT(NULL == src.fRecord);
commit-bot@chromium.orgb50bcb92014-04-07 18:26:22 +0000149 fUniqueID = src.uniqueID(); // need to call method to ensure != 0
reed@android.combcd4d5a2008-12-17 15:59:43 +0000150 } else if (src.fRecord) {
commit-bot@chromium.org206dc582014-03-28 18:05:47 +0000151 SkPictInfo info;
152 this->createHeader(&info);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000153 // here we do a fake src.endRecording()
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000154 fPlayback = SkNEW_ARGS(SkPicturePlayback, (this, *src.fRecord, info));
reed@android.combcd4d5a2008-12-17 15:59:43 +0000155 } else {
156 fPlayback = NULL;
157 }
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000158
159 fPathHeap.reset(SkSafeRef(src.fPathHeap.get()));
160}
161
162const SkPath& SkPicture::getPath(int index) const {
163 return (*fPathHeap.get())[index];
164}
165
166int SkPicture::addPathToHeap(const SkPath& path) {
167 if (NULL == fPathHeap) {
168 fPathHeap.reset(SkNEW(SkPathHeap));
169 }
170#ifdef SK_DEDUP_PICTURE_PATHS
171 return fPathHeap->insert(path);
172#else
173 return fPathHeap->append(path);
174#endif
175}
176
177void SkPicture::initForPlayback() const {
178 // ensure that the paths bounds are pre-computed
179 if (NULL != fPathHeap.get()) {
180 for (int i = 0; i < fPathHeap->count(); i++) {
181 (*fPathHeap.get())[i].updateBoundsCache();
182 }
183 }
184}
185
186void SkPicture::dumpSize() const {
187 SkDebugf("--- picture size: paths=%d\n",
188 SafeCount(fPathHeap.get()));
reed@android.combcd4d5a2008-12-17 15:59:43 +0000189}
190
191SkPicture::~SkPicture() {
reed@google.com91911482011-02-07 15:30:46 +0000192 SkSafeUnref(fRecord);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000193 SkDELETE(fPlayback);
commit-bot@chromium.org73857a32014-03-16 19:46:36 +0000194 SkSafeUnref(fAccelData);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000195}
196
commit-bot@chromium.orge6e785e2014-03-04 19:08:57 +0000197void SkPicture::internalOnly_EnableOpts(bool enableOpts) {
198 if (NULL != fRecord) {
199 fRecord->internalOnly_EnableOpts(enableOpts);
200 }
201}
202
reed@android.combcd4d5a2008-12-17 15:59:43 +0000203void SkPicture::swap(SkPicture& other) {
commit-bot@chromium.orgb50bcb92014-04-07 18:26:22 +0000204 SkTSwap(fUniqueID, other.fUniqueID);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000205 SkTSwap(fRecord, other.fRecord);
206 SkTSwap(fPlayback, other.fPlayback);
commit-bot@chromium.org73857a32014-03-16 19:46:36 +0000207 SkTSwap(fAccelData, other.fAccelData);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000208 SkTSwap(fWidth, other.fWidth);
209 SkTSwap(fHeight, other.fHeight);
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000210 fPathHeap.swap(&other.fPathHeap);
commit-bot@chromium.org7a1832f2014-04-24 21:53:13 +0000211 fContentInfo.swap(&other.fContentInfo);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000212}
213
djsollen@google.com3855f872012-08-29 18:52:07 +0000214SkPicture* SkPicture::clone() const {
215 SkPicture* clonedPicture = SkNEW(SkPicture);
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000216 this->clone(clonedPicture, 1);
djsollen@google.com3855f872012-08-29 18:52:07 +0000217 return clonedPicture;
218}
219
commit-bot@chromium.orgf143d1b2014-04-29 17:22:54 +0000220static bool needs_deep_copy(const SkPaint& paint) {
221 /*
222 * These fields are known to be immutable, and so can be shallow-copied
223 *
224 * getTypeface()
225 * getAnnotation()
226 * paint.getColorFilter()
227 * getXfermode()
228 * getPathEffect()
229 * getMaskFilter()
230 */
231
232 return paint.getShader() ||
233#ifdef SK_SUPPORT_LEGACY_LAYERRASTERIZER_API
234 paint.getRasterizer() ||
235#endif
236 paint.getLooper() || // needs to hide its addLayer...
237 paint.getImageFilter();
238}
239
djsollen@google.com3855f872012-08-29 18:52:07 +0000240void SkPicture::clone(SkPicture* pictures, int count) const {
241 SkPictCopyInfo copyInfo;
commit-bot@chromium.org206dc582014-03-28 18:05:47 +0000242 SkPictInfo info;
243 this->createHeader(&info);
djsollen@google.com3855f872012-08-29 18:52:07 +0000244
245 for (int i = 0; i < count; i++) {
246 SkPicture* clone = &pictures[i];
247
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000248 clone->needsNewGenID();
djsollen@google.com3855f872012-08-29 18:52:07 +0000249 clone->fWidth = fWidth;
250 clone->fHeight = fHeight;
commit-bot@chromium.orgefea6992014-02-14 17:27:10 +0000251 SkSafeSetNull(clone->fRecord);
scroggo@google.coma0dd4332012-11-02 20:51:19 +0000252 SkDELETE(clone->fPlayback);
commit-bot@chromium.org7a1832f2014-04-24 21:53:13 +0000253 clone->fContentInfo.set(fContentInfo);
scroggo@google.coma0dd4332012-11-02 20:51:19 +0000254
djsollen@google.com3855f872012-08-29 18:52:07 +0000255 /* We want to copy the src's playback. However, if that hasn't been built
256 yet, we need to fake a call to endRecording() without actually calling
257 it (since it is destructive, and we don't want to change src).
258 */
259 if (fPlayback) {
commit-bot@chromium.orgf143d1b2014-04-29 17:22:54 +0000260 if (!copyInfo.initialized) {
261 int paintCount = SafeCount(fPlayback->fPaints);
262
263 /* The alternative to doing this is to have a clone method on the paint and have it
264 * make the deep copy of its internal structures as needed. The holdup to doing
skia.committer@gmail.com87e78422014-04-30 03:05:25 +0000265 * that is at this point we would need to pass the SkBitmapHeap so that we don't
commit-bot@chromium.orgf143d1b2014-04-29 17:22:54 +0000266 * unnecessarily flatten the pixels in a bitmap shader.
267 */
268 copyInfo.paintData.setCount(paintCount);
269
skia.committer@gmail.com87e78422014-04-30 03:05:25 +0000270 /* Use an SkBitmapHeap to avoid flattening bitmaps in shaders. If there already is
271 * one, use it. If this SkPicturePlayback was created from a stream, fBitmapHeap
commit-bot@chromium.orgf143d1b2014-04-29 17:22:54 +0000272 * will be NULL, so create a new one.
273 */
274 if (fPlayback->fBitmapHeap.get() == NULL) {
275 // FIXME: Put this on the stack inside SkPicture::clone.
276 SkBitmapHeap* heap = SkNEW(SkBitmapHeap);
277 copyInfo.controller.setBitmapStorage(heap);
278 heap->unref();
279 } else {
280 copyInfo.controller.setBitmapStorage(fPlayback->fBitmapHeap);
281 }
282
283 SkDEBUGCODE(int heapSize = SafeCount(fPlayback->fBitmapHeap.get());)
284 for (int i = 0; i < paintCount; i++) {
285 if (needs_deep_copy(fPlayback->fPaints->at(i))) {
286 copyInfo.paintData[i] =
287 SkFlatData::Create<SkPaint::FlatteningTraits>(&copyInfo.controller,
288 fPlayback->fPaints->at(i), 0);
289
290 } else {
291 // this is our sentinel, which we use in the unflatten loop
292 copyInfo.paintData[i] = NULL;
293 }
294 }
295 SkASSERT(SafeCount(fPlayback->fBitmapHeap.get()) == heapSize);
296
297 // needed to create typeface playback
298 copyInfo.controller.setupPlaybacks();
299 copyInfo.initialized = true;
300 }
301
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000302 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (clone, *fPlayback, &copyInfo));
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000303 SkASSERT(NULL == fRecord);
commit-bot@chromium.orgb50bcb92014-04-07 18:26:22 +0000304 clone->fUniqueID = this->uniqueID(); // need to call method to ensure != 0
djsollen@google.com3855f872012-08-29 18:52:07 +0000305 } else if (fRecord) {
306 // here we do a fake src.endRecording()
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000307 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (clone, *fRecord, info, true));
djsollen@google.com3855f872012-08-29 18:52:07 +0000308 } else {
309 clone->fPlayback = NULL;
310 }
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000311
312 clone->fPathHeap.reset(SkSafeRef(fPathHeap.get()));
djsollen@google.com3855f872012-08-29 18:52:07 +0000313 }
314}
315
commit-bot@chromium.org73857a32014-03-16 19:46:36 +0000316SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
317 static int32_t gNextID = 0;
318
319 int32_t id = sk_atomic_inc(&gNextID);
320 if (id >= 1 << (8 * sizeof(Domain))) {
321 SK_CRASH();
322 }
323
324 return static_cast<Domain>(id);
325}
326
reed@android.combcd4d5a2008-12-17 15:59:43 +0000327///////////////////////////////////////////////////////////////////////////////
328
commit-bot@chromium.org48da8612014-04-17 23:35:06 +0000329#ifdef SK_SUPPORT_LEGACY_DERIVED_PICTURE_CLASSES
330
reed@android.comc4a55d72009-02-13 14:56:09 +0000331SkCanvas* SkPicture::beginRecording(int width, int height,
332 uint32_t recordingFlags) {
reed@android.combcd4d5a2008-12-17 15:59:43 +0000333 if (fPlayback) {
334 SkDELETE(fPlayback);
335 fPlayback = NULL;
336 }
commit-bot@chromium.org73857a32014-03-16 19:46:36 +0000337 SkSafeUnref(fAccelData);
commit-bot@chromium.orgefea6992014-02-14 17:27:10 +0000338 SkSafeSetNull(fRecord);
commit-bot@chromium.org7a1832f2014-04-24 21:53:13 +0000339 fContentInfo.reset();
reed@android.combcd4d5a2008-12-17 15:59:43 +0000340
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000341 this->needsNewGenID();
342
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000343 // Must be set before calling createBBoxHierarchy
344 fWidth = width;
345 fHeight = height;
346
commit-bot@chromium.orgca101d62014-02-17 15:28:00 +0000347 const SkISize size = SkISize::Make(width, height);
348
rileya@google.com7fb3d2f2012-09-13 21:41:51 +0000349 if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) {
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000350 SkBBoxHierarchy* tree = this->createBBoxHierarchy();
rileya@google.com7fb3d2f2012-09-13 21:41:51 +0000351 SkASSERT(NULL != tree);
robertphillips@google.com7c64bc92014-04-24 00:42:23 +0000352 fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (this, size, recordingFlags, tree));
rileya@google.com7fb3d2f2012-09-13 21:41:51 +0000353 tree->unref();
354 } else {
robertphillips@google.com7c64bc92014-04-24 00:42:23 +0000355 fRecord = SkNEW_ARGS(SkPictureRecord, (this, size, recordingFlags));
rileya@google.com7fb3d2f2012-09-13 21:41:51 +0000356 }
reed@google.com118a6d02012-09-27 20:31:31 +0000357 fRecord->beginRecording();
reed@android.combcd4d5a2008-12-17 15:59:43 +0000358
reed@android.combcd4d5a2008-12-17 15:59:43 +0000359 return fRecord;
360}
361
commit-bot@chromium.org48da8612014-04-17 23:35:06 +0000362#endif
363
skia.committer@gmail.com0f1b1d62014-04-18 03:03:54 +0000364SkCanvas* SkPicture::beginRecording(int width, int height,
commit-bot@chromium.org48da8612014-04-17 23:35:06 +0000365 SkBBHFactory* bbhFactory,
366 uint32_t recordingFlags) {
367 if (fPlayback) {
368 SkDELETE(fPlayback);
369 fPlayback = NULL;
370 }
371 SkSafeUnref(fAccelData);
372 SkSafeSetNull(fRecord);
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000373 SkASSERT(NULL == fPathHeap);
commit-bot@chromium.org7a1832f2014-04-24 21:53:13 +0000374 fContentInfo.reset();
commit-bot@chromium.org48da8612014-04-17 23:35:06 +0000375
376 this->needsNewGenID();
377
378 fWidth = width;
379 fHeight = height;
380
381 const SkISize size = SkISize::Make(width, height);
382
383 if (NULL != bbhFactory) {
384 SkAutoTUnref<SkBBoxHierarchy> tree((*bbhFactory)(width, height));
385 SkASSERT(NULL != tree);
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000386 fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (this, size,
commit-bot@chromium.org48da8612014-04-17 23:35:06 +0000387 recordingFlags|
388 kOptimizeForClippedPlayback_RecordingFlag,
389 tree.get()));
390 } else {
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000391 fRecord = SkNEW_ARGS(SkPictureRecord, (this, size, recordingFlags));
commit-bot@chromium.org48da8612014-04-17 23:35:06 +0000392 }
393 fRecord->beginRecording();
394
395 return fRecord;
396}
397
398
399#ifdef SK_SUPPORT_LEGACY_DERIVED_PICTURE_CLASSES
400
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000401SkBBoxHierarchy* SkPicture::createBBoxHierarchy() const {
commit-bot@chromium.orgfd73c592014-04-16 16:02:10 +0000402 // TODO: this code is now replicated in SkRTreePicture. Once all external
403 // clients have been weaned off of kOptimizeForClippedPlayback_RecordingFlag,
404 // this code can be removed.
405
skia.committer@gmail.com8d230dd2012-11-02 02:01:24 +0000406 // These values were empirically determined to produce reasonable
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000407 // performance in most cases.
408 static const int kRTreeMinChildren = 6;
409 static const int kRTreeMaxChildren = 11;
410
411 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
412 SkIntToScalar(fHeight));
sglez@google.com9baab6f2013-08-30 17:27:47 +0000413 bool sortDraws = false; // Do not sort draw calls when bulk loading.
414
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000415 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
sglez@google.com9baab6f2013-08-30 17:27:47 +0000416 aspectRatio, sortDraws);
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000417}
418
commit-bot@chromium.org48da8612014-04-17 23:35:06 +0000419#endif
420
reed@android.combcd4d5a2008-12-17 15:59:43 +0000421SkCanvas* SkPicture::getRecordingCanvas() const {
422 // will be null if we are not recording
423 return fRecord;
424}
425
426void SkPicture::endRecording() {
427 if (NULL == fPlayback) {
428 if (NULL != fRecord) {
junov@chromium.org9aa18db2012-07-12 17:47:34 +0000429 fRecord->endRecording();
commit-bot@chromium.org206dc582014-03-28 18:05:47 +0000430 SkPictInfo info;
431 this->createHeader(&info);
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000432 fPlayback = SkNEW_ARGS(SkPicturePlayback, (this, *fRecord, info));
commit-bot@chromium.orgefea6992014-02-14 17:27:10 +0000433 SkSafeSetNull(fRecord);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000434 }
435 }
436 SkASSERT(NULL == fRecord);
437}
438
commit-bot@chromium.org4b918972014-03-18 17:45:32 +0000439const SkPicture::OperationList& SkPicture::OperationList::InvalidList() {
440 static OperationList gInvalid;
441 return gInvalid;
442}
443
444const SkPicture::OperationList& SkPicture::EXPERIMENTAL_getActiveOps(const SkIRect& queryRect) {
robertphillips@google.com6dd381b2014-04-13 19:09:42 +0000445 this->endRecording(); // TODO: remove eventually
commit-bot@chromium.org4b918972014-03-18 17:45:32 +0000446 if (NULL != fPlayback) {
447 return fPlayback->getActiveOps(queryRect);
448 }
449 return OperationList::InvalidList();
450}
451
commit-bot@chromium.org0ea71132014-03-24 19:40:49 +0000452size_t SkPicture::EXPERIMENTAL_curOpID() const {
453 if (NULL != fPlayback) {
454 return fPlayback->curOpID();
455 }
456 return 0;
457}
458
reed@google.com2b3a5082013-05-20 17:02:41 +0000459void SkPicture::draw(SkCanvas* surface, SkDrawPictureCallback* callback) {
robertphillips@google.com6dd381b2014-04-13 19:09:42 +0000460 this->endRecording(); // TODO: remove eventually
commit-bot@chromium.org73857a32014-03-16 19:46:36 +0000461 if (NULL != fPlayback) {
reed@google.com2b3a5082013-05-20 17:02:41 +0000462 fPlayback->draw(*surface, callback);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000463 }
464}
465
466///////////////////////////////////////////////////////////////////////////////
467
468#include "SkStream.h"
469
rmistry@google.com8793ad72013-12-02 13:50:38 +0000470static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
commit-bot@chromium.org060e3a52014-03-12 14:46:41 +0000471
472bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
473 if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
474 return false;
475 }
476
477 if (info.fVersion < MIN_PICTURE_VERSION ||
478 info.fVersion > CURRENT_PICTURE_VERSION) {
479 return false;
480 }
481
482 return true;
483}
rmistry@google.com8793ad72013-12-02 13:50:38 +0000484
commit-bot@chromium.orgabad6d62014-03-03 19:18:39 +0000485bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
scroggo@google.com3b990302013-06-28 21:32:00 +0000486 if (NULL == stream) {
487 return false;
borenet@google.com30dda202012-09-17 18:26:06 +0000488 }
reed@google.com4bca7f62012-06-22 15:38:39 +0000489
rmistry@google.com8793ad72013-12-02 13:50:38 +0000490 // Check magic bytes.
reed@google.com4bca7f62012-06-22 15:38:39 +0000491 SkPictInfo info;
commit-bot@chromium.org060e3a52014-03-12 14:46:41 +0000492 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
493 if (!stream->read(&info, sizeof(info)) || !IsValidPictInfo(info)) {
scroggo@google.com3b990302013-06-28 21:32:00 +0000494 return false;
reed@google.com4bca7f62012-06-22 15:38:39 +0000495 }
reed@android.combcd4d5a2008-12-17 15:59:43 +0000496
scroggo@google.com3b990302013-06-28 21:32:00 +0000497 if (pInfo != NULL) {
498 *pInfo = info;
499 }
500 return true;
501}
502
commit-bot@chromium.orgabad6d62014-03-03 19:18:39 +0000503bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer& buffer, SkPictInfo* pInfo) {
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000504 // Check magic bytes.
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000505 SkPictInfo info;
commit-bot@chromium.org060e3a52014-03-12 14:46:41 +0000506 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
507 if (!buffer.readByteArray(&info, sizeof(info)) || !IsValidPictInfo(info)) {
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000508 return false;
509 }
510
511 if (pInfo != NULL) {
512 *pInfo = info;
513 }
514 return true;
515}
516
scroggo@google.com3b990302013-06-28 21:32:00 +0000517SkPicture::SkPicture(SkPicturePlayback* playback, int width, int height)
518 : fPlayback(playback)
519 , fRecord(NULL)
520 , fWidth(width)
commit-bot@chromium.org73857a32014-03-16 19:46:36 +0000521 , fHeight(height)
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000522 , fAccelData(NULL) {
523 this->needsNewGenID();
524}
scroggo@google.com3b990302013-06-28 21:32:00 +0000525
526SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
527 SkPictInfo info;
528
commit-bot@chromium.orgabad6d62014-03-03 19:18:39 +0000529 if (!InternalOnly_StreamIsSKP(stream, &info)) {
scroggo@google.com3b990302013-06-28 21:32:00 +0000530 return NULL;
531 }
532
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000533 SkPicture* newPict = SkNEW_ARGS(SkPicture, (NULL, info.fWidth, info.fHeight));
534
scroggo@google.com3b990302013-06-28 21:32:00 +0000535 // Check to see if there is a playback to recreate.
reed@android.combcd4d5a2008-12-17 15:59:43 +0000536 if (stream->readBool()) {
skia.committer@gmail.com88c3a5d2014-04-24 03:05:07 +0000537 SkPicturePlayback* playback = SkPicturePlayback::CreateFromStream(newPict, stream,
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000538 info, proc);
scroggo@google.com3b7431c2013-10-01 15:30:46 +0000539 if (NULL == playback) {
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000540 SkDELETE(newPict);
scroggo@google.com3b7431c2013-10-01 15:30:46 +0000541 return NULL;
542 }
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000543 newPict->fPlayback = playback;
reed@android.combcd4d5a2008-12-17 15:59:43 +0000544 }
reed@google.com4bca7f62012-06-22 15:38:39 +0000545
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000546 return newPict;
reed@android.combcd4d5a2008-12-17 15:59:43 +0000547}
548
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000549SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
550 SkPictInfo info;
551
commit-bot@chromium.orgabad6d62014-03-03 19:18:39 +0000552 if (!InternalOnly_BufferIsSKP(buffer, &info)) {
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000553 return NULL;
554 }
555
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000556 SkPicture* newPict = SkNEW_ARGS(SkPicture, (NULL, info.fWidth, info.fHeight));
557
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000558 // Check to see if there is a playback to recreate.
559 if (buffer.readBool()) {
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000560 SkPicturePlayback* playback = SkPicturePlayback::CreateFromBuffer(newPict, buffer, info);
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000561 if (NULL == playback) {
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000562 SkDELETE(newPict);
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000563 return NULL;
564 }
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000565 newPict->fPlayback = playback;
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000566 }
567
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000568 return newPict;
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000569}
570
commit-bot@chromium.org060e3a52014-03-12 14:46:41 +0000571void SkPicture::createHeader(SkPictInfo* info) const {
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000572 // Copy magic bytes at the beginning of the header
573 SkASSERT(sizeof(kMagic) == 8);
commit-bot@chromium.org060e3a52014-03-12 14:46:41 +0000574 SkASSERT(sizeof(kMagic) == sizeof(info->fMagic));
575 memcpy(info->fMagic, kMagic, sizeof(kMagic));
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000576
commit-bot@chromium.orgabad6d62014-03-03 19:18:39 +0000577 // Set picture info after magic bytes in the header
commit-bot@chromium.org338284f2014-02-25 02:16:10 +0000578 info->fVersion = CURRENT_PICTURE_VERSION;
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000579 info->fWidth = fWidth;
580 info->fHeight = fHeight;
581 info->fFlags = SkPictInfo::kCrossProcess_Flag;
582 // TODO: remove this flag, since we're always float (now)
583 info->fFlags |= SkPictInfo::kScalarIsFloat_Flag;
584
585 if (8 == sizeof(void*)) {
586 info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
587 }
588}
589
scroggo@google.com2e727312013-02-22 22:04:19 +0000590void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
reed@android.combcd4d5a2008-12-17 15:59:43 +0000591 SkPicturePlayback* playback = fPlayback;
reed@google.com91911482011-02-07 15:30:46 +0000592
commit-bot@chromium.org206dc582014-03-28 18:05:47 +0000593 SkPictInfo info;
594 this->createHeader(&info);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000595 if (NULL == playback && fRecord) {
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000596 playback = SkNEW_ARGS(SkPicturePlayback, (this, *fRecord, info));
reed@android.combcd4d5a2008-12-17 15:59:43 +0000597 }
598
commit-bot@chromium.org206dc582014-03-28 18:05:47 +0000599 stream->write(&info, sizeof(info));
reed@android.combcd4d5a2008-12-17 15:59:43 +0000600 if (playback) {
601 stream->writeBool(true);
scroggo@google.com6ff73172012-10-04 21:46:08 +0000602 playback->serialize(stream, encoder);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000603 // delete playback if it is a local version (i.e. cons'd up just now)
604 if (playback != fPlayback) {
605 SkDELETE(playback);
606 }
607 } else {
608 stream->writeBool(false);
609 }
610}
611
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000612void SkPicture::WriteTagSize(SkWriteBuffer& buffer, uint32_t tag, size_t size) {
613 buffer.writeUInt(tag);
614 buffer.writeUInt(SkToU32(size));
615}
616
617void SkPicture::WriteTagSize(SkWStream* stream, uint32_t tag, size_t size) {
618 stream->write32(tag);
619 stream->write32(SkToU32(size));
620}
621
622bool SkPicture::parseBufferTag(SkReadBuffer& buffer,
skia.committer@gmail.com88c3a5d2014-04-24 03:05:07 +0000623 uint32_t tag,
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000624 uint32_t size) {
625 switch (tag) {
626 case SK_PICT_PATH_BUFFER_TAG:
627 if (size > 0) {
628 fPathHeap.reset(SkNEW_ARGS(SkPathHeap, (buffer)));
629 }
630 break;
631 default:
632 // The tag was invalid.
633 return false;
634 }
635
636 return true; // success
637}
638
639void SkPicture::flattenToBuffer(SkWriteBuffer& buffer) const {
640 int n;
641
642 if ((n = SafeCount(fPathHeap.get())) > 0) {
643 WriteTagSize(buffer, SK_PICT_PATH_BUFFER_TAG, n);
644 fPathHeap->flatten(buffer);
645 }
646}
647
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000648void SkPicture::flatten(SkWriteBuffer& buffer) const {
649 SkPicturePlayback* playback = fPlayback;
650
commit-bot@chromium.org206dc582014-03-28 18:05:47 +0000651 SkPictInfo info;
652 this->createHeader(&info);
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000653 if (NULL == playback && fRecord) {
commit-bot@chromium.orgfc486cd2014-04-23 22:35:42 +0000654 playback = SkNEW_ARGS(SkPicturePlayback, (this, *fRecord, info));
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000655 }
656
commit-bot@chromium.org206dc582014-03-28 18:05:47 +0000657 buffer.writeByteArray(&info, sizeof(info));
commit-bot@chromium.org7183ff62014-02-07 12:20:04 +0000658 if (playback) {
659 buffer.writeBool(true);
660 playback->flatten(buffer);
661 // delete playback if it is a local version (i.e. cons'd up just now)
662 if (playback != fPlayback) {
663 SkDELETE(playback);
664 }
665 } else {
666 buffer.writeBool(false);
667 }
668}
669
commit-bot@chromium.org05937b42014-03-19 22:54:40 +0000670#if SK_SUPPORT_GPU
commit-bot@chromium.org95cdc162014-03-19 21:24:25 +0000671bool SkPicture::suitableForGpuRasterization(GrContext* context) const {
commit-bot@chromium.org7a1832f2014-04-24 21:53:13 +0000672 // TODO: the heuristic used here needs to be refined
673 static const int kNumPaintWithPathEffectUsesTol = 1;
674 static const int kNumAAConcavePaths = 5;
675
676 SkASSERT(this->numAAHairlineConcavePaths() <= this->numAAConcavePaths());
677
678 return this->numPaintWithPathEffectUses() < kNumPaintWithPathEffectUsesTol &&
679 (this->numAAConcavePaths()-this->numAAHairlineConcavePaths()) < kNumAAConcavePaths;
commit-bot@chromium.org95cdc162014-03-19 21:24:25 +0000680}
commit-bot@chromium.org05937b42014-03-19 22:54:40 +0000681#endif
commit-bot@chromium.org95cdc162014-03-19 21:24:25 +0000682
tomhudson@google.comc7941702013-10-24 11:12:47 +0000683bool SkPicture::willPlayBackBitmaps() const {
commit-bot@chromium.org73857a32014-03-16 19:46:36 +0000684 if (!fPlayback) {
685 return false;
686 }
tomhudson@google.comc7941702013-10-24 11:12:47 +0000687 return fPlayback->containsBitmaps();
688}
689
djsollen@google.com6789a342013-02-01 16:18:09 +0000690#ifdef SK_BUILD_FOR_ANDROID
reed@android.combcd4d5a2008-12-17 15:59:43 +0000691void SkPicture::abortPlayback() {
692 if (NULL == fPlayback) {
693 return;
694 }
695 fPlayback->abort();
696}
djsollen@google.com6789a342013-02-01 16:18:09 +0000697#endif
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000698
699static int32_t next_picture_generation_id() {
700 static int32_t gPictureGenerationID = 0;
701 // do a loop in case our global wraps around, as we never want to
702 // return a 0
703 int32_t genID;
704 do {
705 genID = sk_atomic_inc(&gPictureGenerationID) + 1;
commit-bot@chromium.orgb50bcb92014-04-07 18:26:22 +0000706 } while (SK_InvalidGenID == genID);
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000707 return genID;
708}
709
commit-bot@chromium.orgb50bcb92014-04-07 18:26:22 +0000710uint32_t SkPicture::uniqueID() const {
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000711 if (NULL != fRecord) {
712 SkASSERT(NULL == fPlayback);
commit-bot@chromium.orgb50bcb92014-04-07 18:26:22 +0000713 return SK_InvalidGenID;
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000714 }
715
commit-bot@chromium.orgb50bcb92014-04-07 18:26:22 +0000716 if (SK_InvalidGenID == fUniqueID) {
717 fUniqueID = next_picture_generation_id();
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000718 }
commit-bot@chromium.orgb50bcb92014-04-07 18:26:22 +0000719 return fUniqueID;
robertphillips@google.com647fe7f2014-04-02 23:51:13 +0000720}