3 * Copyright 2007 The Android Open Source Project
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
10 #include "SkPictureFlat.h"
11 #include "SkPicturePlayback.h"
12 #include "SkPictureRecord.h"
14 #include "SkBBHFactory.h"
15 #include "SkBitmapDevice.h"
17 #include "SkChunkAlloc.h"
18 #include "SkPaintPriv.h"
19 #include "SkPicture.h"
22 #include "SkTDArray.h"
23 #include "SkTSearch.h"
26 #include "SkReader32.h"
27 #include "SkWriter32.h"
29 #include "SkBBoxHierarchyRecord.h"
32 #include "GrContext.h"
35 template <typename T> int SafeCount(const T* obj) {
36 return obj ? obj->count() : 0;
39 #define DUMP_BUFFER_SIZE 65536
41 //#define ENABLE_TIME_DRAW // dumps milliseconds for each draw
45 // enable SK_DEBUG_TRACE to trace DrawType elements when
46 // recorded and played back
47 // #define SK_DEBUG_TRACE
48 // enable SK_DEBUG_SIZE to see the size of picture components
49 // #define SK_DEBUG_SIZE
50 // enable SK_DEBUG_DUMP to see the contents of recorded elements
51 // #define SK_DEBUG_DUMP
52 // enable SK_DEBUG_VALIDATE to check internal structures for consistency
53 // #define SK_DEBUG_VALIDATE
56 #if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP
57 const char* DrawTypeToString(DrawType drawType) {
59 case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break;
60 case CLIP_PATH: return "CLIP_PATH";
61 case CLIP_REGION: return "CLIP_REGION";
62 case CLIP_RECT: return "CLIP_RECT";
63 case CLIP_RRECT: return "CLIP_RRECT";
64 case CONCAT: return "CONCAT";
65 case DRAW_BITMAP: return "DRAW_BITMAP";
66 case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX";
67 case DRAW_BITMAP_NINE: return "DRAW_BITMAP_NINE";
68 case DRAW_BITMAP_RECT_TO_RECT: return "DRAW_BITMAP_RECT_TO_RECT";
69 case DRAW_CLEAR: return "DRAW_CLEAR";
70 case DRAW_DATA: return "DRAW_DATA";
71 case DRAW_OVAL: return "DRAW_OVAL";
72 case DRAW_PAINT: return "DRAW_PAINT";
73 case DRAW_PATH: return "DRAW_PATH";
74 case DRAW_PICTURE: return "DRAW_PICTURE";
75 case DRAW_POINTS: return "DRAW_POINTS";
76 case DRAW_POS_TEXT: return "DRAW_POS_TEXT";
77 case DRAW_POS_TEXT_TOP_BOTTOM: return "DRAW_POS_TEXT_TOP_BOTTOM";
78 case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H";
79 case DRAW_POS_TEXT_H_TOP_BOTTOM: return "DRAW_POS_TEXT_H_TOP_BOTTOM";
80 case DRAW_RECT: return "DRAW_RECT";
81 case DRAW_RRECT: return "DRAW_RRECT";
82 case DRAW_SPRITE: return "DRAW_SPRITE";
83 case DRAW_TEXT: return "DRAW_TEXT";
84 case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH";
85 case DRAW_TEXT_TOP_BOTTOM: return "DRAW_TEXT_TOP_BOTTOM";
86 case DRAW_VERTICES: return "DRAW_VERTICES";
87 case RESTORE: return "RESTORE";
88 case ROTATE: return "ROTATE";
89 case SAVE: return "SAVE";
90 case SAVE_LAYER: return "SAVE_LAYER";
91 case SCALE: return "SCALE";
92 case SET_MATRIX: return "SET_MATRIX";
93 case SKEW: return "SKEW";
94 case TRANSLATE: return "TRANSLATE";
95 case NOOP: return "NOOP";
97 SkDebugf("DrawType error 0x%08x\n", drawType);
106 #ifdef SK_DEBUG_VALIDATE
107 static void validateMatrix(const SkMatrix* matrix) {
108 SkScalar scaleX = matrix->getScaleX();
109 SkScalar scaleY = matrix->getScaleY();
110 SkScalar skewX = matrix->getSkewX();
111 SkScalar skewY = matrix->getSkewY();
112 SkScalar perspX = matrix->getPerspX();
113 SkScalar perspY = matrix->getPerspY();
114 if (scaleX != 0 && skewX != 0)
115 SkDebugf("scaleX != 0 && skewX != 0\n");
116 SkASSERT(scaleX == 0 || skewX == 0);
117 SkASSERT(scaleY == 0 || skewY == 0);
118 SkASSERT(perspX == 0);
119 SkASSERT(perspY == 0);
124 ///////////////////////////////////////////////////////////////////////////////
126 SkPicture::SkPicture()
128 this->needsNewGenID();
131 fWidth = fHeight = 0;
134 SkPicture::SkPicture(const SkPicture& src)
137 , fContentInfo(src.fContentInfo) {
138 this->needsNewGenID();
140 fHeight = src.fHeight;
143 /* We want to copy the src's playback. However, if that hasn't been built
144 yet, we need to fake a call to endRecording() without actually calling
145 it (since it is destructive, and we don't want to change src).
148 fPlayback = SkNEW_ARGS(SkPicturePlayback, (this, *src.fPlayback));
149 SkASSERT(NULL == src.fRecord);
150 fUniqueID = src.uniqueID(); // need to call method to ensure != 0
151 } else if (src.fRecord) {
153 this->createHeader(&info);
154 // here we do a fake src.endRecording()
155 fPlayback = SkNEW_ARGS(SkPicturePlayback, (this, *src.fRecord, info));
160 fPathHeap.reset(SkSafeRef(src.fPathHeap.get()));
163 const SkPath& SkPicture::getPath(int index) const {
164 return (*fPathHeap.get())[index];
167 int SkPicture::addPathToHeap(const SkPath& path) {
168 if (NULL == fPathHeap) {
169 fPathHeap.reset(SkNEW(SkPathHeap));
171 #ifdef SK_DEDUP_PICTURE_PATHS
172 return fPathHeap->insert(path);
174 return fPathHeap->append(path);
178 void SkPicture::initForPlayback() const {
179 // ensure that the paths bounds are pre-computed
180 if (NULL != fPathHeap.get()) {
181 for (int i = 0; i < fPathHeap->count(); i++) {
182 (*fPathHeap.get())[i].updateBoundsCache();
187 void SkPicture::dumpSize() const {
188 SkDebugf("--- picture size: paths=%d\n",
189 SafeCount(fPathHeap.get()));
192 SkPicture::~SkPicture() {
193 SkSafeUnref(fRecord);
195 SkSafeUnref(fAccelData);
198 void SkPicture::internalOnly_EnableOpts(bool enableOpts) {
199 if (NULL != fRecord) {
200 fRecord->internalOnly_EnableOpts(enableOpts);
204 void SkPicture::swap(SkPicture& other) {
205 SkTSwap(fUniqueID, other.fUniqueID);
206 SkTSwap(fRecord, other.fRecord);
207 SkTSwap(fPlayback, other.fPlayback);
208 SkTSwap(fAccelData, other.fAccelData);
209 SkTSwap(fWidth, other.fWidth);
210 SkTSwap(fHeight, other.fHeight);
211 fPathHeap.swap(&other.fPathHeap);
212 fContentInfo.swap(&other.fContentInfo);
215 SkPicture* SkPicture::clone() const {
216 SkPicture* clonedPicture = SkNEW(SkPicture);
217 this->clone(clonedPicture, 1);
218 return clonedPicture;
221 void SkPicture::clone(SkPicture* pictures, int count) const {
222 SkPictCopyInfo copyInfo;
224 this->createHeader(&info);
226 for (int i = 0; i < count; i++) {
227 SkPicture* clone = &pictures[i];
229 clone->needsNewGenID();
230 clone->fWidth = fWidth;
231 clone->fHeight = fHeight;
232 SkSafeSetNull(clone->fRecord);
233 SkDELETE(clone->fPlayback);
234 clone->fContentInfo.set(fContentInfo);
236 /* We want to copy the src's playback. However, if that hasn't been built
237 yet, we need to fake a call to endRecording() without actually calling
238 it (since it is destructive, and we don't want to change src).
241 if (!copyInfo.initialized) {
242 int paintCount = SafeCount(fPlayback->fPaints);
244 /* The alternative to doing this is to have a clone method on the paint and have it
245 * make the deep copy of its internal structures as needed. The holdup to doing
246 * that is at this point we would need to pass the SkBitmapHeap so that we don't
247 * unnecessarily flatten the pixels in a bitmap shader.
249 copyInfo.paintData.setCount(paintCount);
251 /* Use an SkBitmapHeap to avoid flattening bitmaps in shaders. If there already is
252 * one, use it. If this SkPicturePlayback was created from a stream, fBitmapHeap
253 * will be NULL, so create a new one.
255 if (fPlayback->fBitmapHeap.get() == NULL) {
256 // FIXME: Put this on the stack inside SkPicture::clone.
257 SkBitmapHeap* heap = SkNEW(SkBitmapHeap);
258 copyInfo.controller.setBitmapStorage(heap);
261 copyInfo.controller.setBitmapStorage(fPlayback->fBitmapHeap);
264 SkDEBUGCODE(int heapSize = SafeCount(fPlayback->fBitmapHeap.get());)
265 for (int i = 0; i < paintCount; i++) {
266 if (NeedsDeepCopy(fPlayback->fPaints->at(i))) {
267 copyInfo.paintData[i] =
268 SkFlatData::Create<SkPaint::FlatteningTraits>(©Info.controller,
269 fPlayback->fPaints->at(i), 0);
272 // this is our sentinel, which we use in the unflatten loop
273 copyInfo.paintData[i] = NULL;
276 SkASSERT(SafeCount(fPlayback->fBitmapHeap.get()) == heapSize);
278 // needed to create typeface playback
279 copyInfo.controller.setupPlaybacks();
280 copyInfo.initialized = true;
283 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (clone, *fPlayback, ©Info));
284 SkASSERT(NULL == fRecord);
285 clone->fUniqueID = this->uniqueID(); // need to call method to ensure != 0
286 } else if (fRecord) {
287 // here we do a fake src.endRecording()
288 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (clone, *fRecord, info, true));
290 clone->fPlayback = NULL;
293 clone->fPathHeap.reset(SkSafeRef(fPathHeap.get()));
297 SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
298 static int32_t gNextID = 0;
300 int32_t id = sk_atomic_inc(&gNextID);
301 if (id >= 1 << (8 * sizeof(Domain))) {
305 return static_cast<Domain>(id);
308 ///////////////////////////////////////////////////////////////////////////////
310 #ifdef SK_SUPPORT_LEGACY_DERIVED_PICTURE_CLASSES
312 SkCanvas* SkPicture::beginRecording(int width, int height,
313 uint32_t recordingFlags) {
318 SkSafeUnref(fAccelData);
319 SkSafeSetNull(fRecord);
320 fContentInfo.reset();
322 this->needsNewGenID();
324 // Must be set before calling createBBoxHierarchy
328 const SkISize size = SkISize::Make(width, height);
330 if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) {
331 SkBBoxHierarchy* tree = this->createBBoxHierarchy();
332 SkASSERT(NULL != tree);
333 fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (this, size, recordingFlags, tree));
336 fRecord = SkNEW_ARGS(SkPictureRecord, (this, size, recordingFlags));
338 fRecord->beginRecording();
345 SkCanvas* SkPicture::beginRecording(int width, int height,
346 SkBBHFactory* bbhFactory,
347 uint32_t recordingFlags) {
352 SkSafeUnref(fAccelData);
353 SkSafeSetNull(fRecord);
354 SkASSERT(NULL == fPathHeap);
355 fContentInfo.reset();
357 this->needsNewGenID();
362 const SkISize size = SkISize::Make(width, height);
364 if (NULL != bbhFactory) {
365 SkAutoTUnref<SkBBoxHierarchy> tree((*bbhFactory)(width, height));
366 SkASSERT(NULL != tree);
367 fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (this, size,
369 kOptimizeForClippedPlayback_RecordingFlag,
372 fRecord = SkNEW_ARGS(SkPictureRecord, (this, size, recordingFlags));
374 fRecord->beginRecording();
380 #ifdef SK_SUPPORT_LEGACY_DERIVED_PICTURE_CLASSES
382 SkBBoxHierarchy* SkPicture::createBBoxHierarchy() const {
383 // TODO: this code is now replicated in SkRTreePicture. Once all external
384 // clients have been weaned off of kOptimizeForClippedPlayback_RecordingFlag,
385 // this code can be removed.
387 // These values were empirically determined to produce reasonable
388 // performance in most cases.
389 static const int kRTreeMinChildren = 6;
390 static const int kRTreeMaxChildren = 11;
392 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
393 SkIntToScalar(fHeight));
394 bool sortDraws = false; // Do not sort draw calls when bulk loading.
396 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
397 aspectRatio, sortDraws);
402 SkCanvas* SkPicture::getRecordingCanvas() const {
403 // will be null if we are not recording
407 void SkPicture::endRecording() {
408 if (NULL == fPlayback) {
409 if (NULL != fRecord) {
410 fRecord->endRecording();
412 this->createHeader(&info);
413 fPlayback = SkNEW_ARGS(SkPicturePlayback, (this, *fRecord, info));
414 SkSafeSetNull(fRecord);
417 SkASSERT(NULL == fRecord);
420 const SkPicture::OperationList& SkPicture::OperationList::InvalidList() {
421 static OperationList gInvalid;
425 const SkPicture::OperationList& SkPicture::EXPERIMENTAL_getActiveOps(const SkIRect& queryRect) {
426 this->endRecording(); // TODO: remove eventually
427 if (NULL != fPlayback) {
428 return fPlayback->getActiveOps(queryRect);
430 return OperationList::InvalidList();
433 size_t SkPicture::EXPERIMENTAL_curOpID() const {
434 if (NULL != fPlayback) {
435 return fPlayback->curOpID();
440 void SkPicture::draw(SkCanvas* surface, SkDrawPictureCallback* callback) {
441 this->endRecording(); // TODO: remove eventually
442 if (NULL != fPlayback) {
443 fPlayback->draw(*surface, callback);
447 ///////////////////////////////////////////////////////////////////////////////
449 #include "SkStream.h"
451 static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
453 bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
454 if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
458 if (info.fVersion < MIN_PICTURE_VERSION ||
459 info.fVersion > CURRENT_PICTURE_VERSION) {
466 bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
467 if (NULL == stream) {
471 // Check magic bytes.
473 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
474 if (!stream->read(&info, sizeof(info)) || !IsValidPictInfo(info)) {
484 bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer& buffer, SkPictInfo* pInfo) {
485 // Check magic bytes.
487 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
488 if (!buffer.readByteArray(&info, sizeof(info)) || !IsValidPictInfo(info)) {
498 SkPicture::SkPicture(SkPicturePlayback* playback, int width, int height)
499 : fPlayback(playback)
504 this->needsNewGenID();
507 SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
510 if (!InternalOnly_StreamIsSKP(stream, &info)) {
514 SkPicture* newPict = SkNEW_ARGS(SkPicture, (NULL, info.fWidth, info.fHeight));
516 // Check to see if there is a playback to recreate.
517 if (stream->readBool()) {
518 SkPicturePlayback* playback = SkPicturePlayback::CreateFromStream(newPict, stream,
520 if (NULL == playback) {
524 newPict->fPlayback = playback;
530 SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
533 if (!InternalOnly_BufferIsSKP(buffer, &info)) {
537 SkPicture* newPict = SkNEW_ARGS(SkPicture, (NULL, info.fWidth, info.fHeight));
539 // Check to see if there is a playback to recreate.
540 if (buffer.readBool()) {
541 SkPicturePlayback* playback = SkPicturePlayback::CreateFromBuffer(newPict, buffer, info);
542 if (NULL == playback) {
546 newPict->fPlayback = playback;
552 void SkPicture::createHeader(SkPictInfo* info) const {
553 // Copy magic bytes at the beginning of the header
554 SkASSERT(sizeof(kMagic) == 8);
555 SkASSERT(sizeof(kMagic) == sizeof(info->fMagic));
556 memcpy(info->fMagic, kMagic, sizeof(kMagic));
558 // Set picture info after magic bytes in the header
559 info->fVersion = CURRENT_PICTURE_VERSION;
560 info->fWidth = fWidth;
561 info->fHeight = fHeight;
562 info->fFlags = SkPictInfo::kCrossProcess_Flag;
563 // TODO: remove this flag, since we're always float (now)
564 info->fFlags |= SkPictInfo::kScalarIsFloat_Flag;
566 if (8 == sizeof(void*)) {
567 info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
571 void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
572 SkPicturePlayback* playback = fPlayback;
575 this->createHeader(&info);
576 if (NULL == playback && fRecord) {
577 playback = SkNEW_ARGS(SkPicturePlayback, (this, *fRecord, info));
580 stream->write(&info, sizeof(info));
582 stream->writeBool(true);
583 playback->serialize(stream, encoder);
584 // delete playback if it is a local version (i.e. cons'd up just now)
585 if (playback != fPlayback) {
589 stream->writeBool(false);
593 void SkPicture::WriteTagSize(SkWriteBuffer& buffer, uint32_t tag, size_t size) {
594 buffer.writeUInt(tag);
595 buffer.writeUInt(SkToU32(size));
598 void SkPicture::WriteTagSize(SkWStream* stream, uint32_t tag, size_t size) {
599 stream->write32(tag);
600 stream->write32(SkToU32(size));
603 bool SkPicture::parseBufferTag(SkReadBuffer& buffer,
607 case SK_PICT_PATH_BUFFER_TAG:
609 fPathHeap.reset(SkNEW_ARGS(SkPathHeap, (buffer)));
613 // The tag was invalid.
617 return true; // success
620 void SkPicture::flattenToBuffer(SkWriteBuffer& buffer) const {
623 if ((n = SafeCount(fPathHeap.get())) > 0) {
624 WriteTagSize(buffer, SK_PICT_PATH_BUFFER_TAG, n);
625 fPathHeap->flatten(buffer);
629 void SkPicture::flatten(SkWriteBuffer& buffer) const {
630 SkPicturePlayback* playback = fPlayback;
633 this->createHeader(&info);
634 if (NULL == playback && fRecord) {
635 playback = SkNEW_ARGS(SkPicturePlayback, (this, *fRecord, info));
638 buffer.writeByteArray(&info, sizeof(info));
640 buffer.writeBool(true);
641 playback->flatten(buffer);
642 // delete playback if it is a local version (i.e. cons'd up just now)
643 if (playback != fPlayback) {
647 buffer.writeBool(false);
652 bool SkPicture::suitableForGpuRasterization(GrContext* context) const {
653 // TODO: the heuristic used here needs to be refined
654 static const int kNumPaintWithPathEffectUsesTol = 1;
655 static const int kNumAAConcavePaths = 5;
657 SkASSERT(this->numAAHairlineConcavePaths() <= this->numAAConcavePaths());
659 return this->numPaintWithPathEffectUses() < kNumPaintWithPathEffectUsesTol &&
660 (this->numAAConcavePaths()-this->numAAHairlineConcavePaths()) < kNumAAConcavePaths;
664 bool SkPicture::willPlayBackBitmaps() const {
668 return fPlayback->containsBitmaps();
671 #ifdef SK_BUILD_FOR_ANDROID
672 void SkPicture::abortPlayback() {
673 if (NULL == fPlayback) {
680 static int32_t next_picture_generation_id() {
681 static int32_t gPictureGenerationID = 0;
682 // do a loop in case our global wraps around, as we never want to
686 genID = sk_atomic_inc(&gPictureGenerationID) + 1;
687 } while (SK_InvalidGenID == genID);
691 uint32_t SkPicture::uniqueID() const {
692 if (NULL != fRecord) {
693 SkASSERT(NULL == fPlayback);
694 return SK_InvalidGenID;
697 if (SK_InvalidGenID == fUniqueID) {
698 fUniqueID = next_picture_generation_id();