Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / core / SkPicture.cpp
1
2 /*
3  * 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
9
10 #include "SkPictureFlat.h"
11 #include "SkPicturePlayback.h"
12 #include "SkPictureRecord.h"
13
14 #include "SkBBHFactory.h"
15 #include "SkBitmapDevice.h"
16 #include "SkCanvas.h"
17 #include "SkChunkAlloc.h"
18 #include "SkPaintPriv.h"
19 #include "SkPicture.h"
20 #include "SkRegion.h"
21 #include "SkStream.h"
22 #include "SkTDArray.h"
23 #include "SkTSearch.h"
24 #include "SkTime.h"
25
26 #include "SkReader32.h"
27 #include "SkWriter32.h"
28 #include "SkRTree.h"
29 #include "SkBBoxHierarchyRecord.h"
30
31 #if SK_SUPPORT_GPU
32 #include "GrContext.h"
33 #endif
34
35 template <typename T> int SafeCount(const T* obj) {
36     return obj ? obj->count() : 0;
37 }
38
39 #define DUMP_BUFFER_SIZE 65536
40
41 //#define ENABLE_TIME_DRAW    // dumps milliseconds for each draw
42
43
44 #ifdef SK_DEBUG
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
54 #endif
55
56 #if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP
57 const char* DrawTypeToString(DrawType drawType) {
58     switch (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";
96         default:
97             SkDebugf("DrawType error 0x%08x\n", drawType);
98             SkASSERT(0);
99             break;
100     }
101     SkASSERT(0);
102     return NULL;
103 }
104 #endif
105
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);
120 }
121 #endif
122
123
124 ///////////////////////////////////////////////////////////////////////////////
125
126 SkPicture::SkPicture()
127     : fAccelData(NULL) {
128     this->needsNewGenID();
129     fRecord = NULL;
130     fPlayback = NULL;
131     fWidth = fHeight = 0;
132 }
133
134 SkPicture::SkPicture(const SkPicture& src)
135     : INHERITED()
136     , fAccelData(NULL)
137     , fContentInfo(src.fContentInfo) {
138     this->needsNewGenID();
139     fWidth = src.fWidth;
140     fHeight = src.fHeight;
141     fRecord = NULL;
142
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).
146      */
147     if (src.fPlayback) {
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) {
152         SkPictInfo info;
153         this->createHeader(&info);
154         // here we do a fake src.endRecording()
155         fPlayback = SkNEW_ARGS(SkPicturePlayback, (this, *src.fRecord, info));
156     } else {
157         fPlayback = NULL;
158     }
159
160     fPathHeap.reset(SkSafeRef(src.fPathHeap.get()));
161 }
162
163 const SkPath& SkPicture::getPath(int index) const {
164     return (*fPathHeap.get())[index];
165 }
166
167 int SkPicture::addPathToHeap(const SkPath& path) {
168     if (NULL == fPathHeap) {
169         fPathHeap.reset(SkNEW(SkPathHeap));
170     }
171 #ifdef SK_DEDUP_PICTURE_PATHS
172     return fPathHeap->insert(path);
173 #else
174     return fPathHeap->append(path);
175 #endif
176 }
177
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();
183         }
184     }
185 }
186
187 void SkPicture::dumpSize() const {
188     SkDebugf("--- picture size: paths=%d\n",
189              SafeCount(fPathHeap.get()));
190 }
191
192 SkPicture::~SkPicture() {
193     SkSafeUnref(fRecord);
194     SkDELETE(fPlayback);
195     SkSafeUnref(fAccelData);
196 }
197
198 void SkPicture::internalOnly_EnableOpts(bool enableOpts) {
199     if (NULL != fRecord) {
200         fRecord->internalOnly_EnableOpts(enableOpts);
201     }
202 }
203
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);
213 }
214
215 SkPicture* SkPicture::clone() const {
216     SkPicture* clonedPicture = SkNEW(SkPicture);
217     this->clone(clonedPicture, 1);
218     return clonedPicture;
219 }
220
221 void SkPicture::clone(SkPicture* pictures, int count) const {
222     SkPictCopyInfo copyInfo;
223     SkPictInfo info;
224     this->createHeader(&info);
225
226     for (int i = 0; i < count; i++) {
227         SkPicture* clone = &pictures[i];
228
229         clone->needsNewGenID();
230         clone->fWidth = fWidth;
231         clone->fHeight = fHeight;
232         SkSafeSetNull(clone->fRecord);
233         SkDELETE(clone->fPlayback);
234         clone->fContentInfo.set(fContentInfo);
235
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).
239          */
240         if (fPlayback) {
241             if (!copyInfo.initialized) {
242                 int paintCount = SafeCount(fPlayback->fPaints);
243
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.
248                  */
249                 copyInfo.paintData.setCount(paintCount);
250
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.
254                  */
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);
259                     heap->unref();
260                 } else {
261                     copyInfo.controller.setBitmapStorage(fPlayback->fBitmapHeap);
262                 }
263
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>(&copyInfo.controller,
269                                                               fPlayback->fPaints->at(i), 0);
270
271                     } else {
272                         // this is our sentinel, which we use in the unflatten loop
273                         copyInfo.paintData[i] = NULL;
274                     }
275                 }
276                 SkASSERT(SafeCount(fPlayback->fBitmapHeap.get()) == heapSize);
277
278                 // needed to create typeface playback
279                 copyInfo.controller.setupPlaybacks();
280                 copyInfo.initialized = true;
281             }
282
283             clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (clone, *fPlayback, &copyInfo));
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));
289         } else {
290             clone->fPlayback = NULL;
291         }
292
293         clone->fPathHeap.reset(SkSafeRef(fPathHeap.get()));
294     }
295 }
296
297 SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
298     static int32_t gNextID = 0;
299
300     int32_t id = sk_atomic_inc(&gNextID);
301     if (id >= 1 << (8 * sizeof(Domain))) {
302         SK_CRASH();
303     }
304
305     return static_cast<Domain>(id);
306 }
307
308 ///////////////////////////////////////////////////////////////////////////////
309
310 #ifdef SK_SUPPORT_LEGACY_DERIVED_PICTURE_CLASSES
311
312 SkCanvas* SkPicture::beginRecording(int width, int height,
313                                     uint32_t recordingFlags) {
314     if (fPlayback) {
315         SkDELETE(fPlayback);
316         fPlayback = NULL;
317     }
318     SkSafeUnref(fAccelData);
319     SkSafeSetNull(fRecord);
320     fContentInfo.reset();
321
322     this->needsNewGenID();
323
324     // Must be set before calling createBBoxHierarchy
325     fWidth = width;
326     fHeight = height;
327
328     const SkISize size = SkISize::Make(width, height);
329
330     if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) {
331         SkBBoxHierarchy* tree = this->createBBoxHierarchy();
332         SkASSERT(NULL != tree);
333         fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (this, size, recordingFlags, tree));
334         tree->unref();
335     } else {
336         fRecord = SkNEW_ARGS(SkPictureRecord, (this, size, recordingFlags));
337     }
338     fRecord->beginRecording();
339
340     return fRecord;
341 }
342
343 #endif
344
345 SkCanvas* SkPicture::beginRecording(int width, int height,
346                                     SkBBHFactory* bbhFactory,
347                                     uint32_t recordingFlags) {
348     if (fPlayback) {
349         SkDELETE(fPlayback);
350         fPlayback = NULL;
351     }
352     SkSafeUnref(fAccelData);
353     SkSafeSetNull(fRecord);
354     SkASSERT(NULL == fPathHeap);
355     fContentInfo.reset();
356
357     this->needsNewGenID();
358
359     fWidth = width;
360     fHeight = height;
361
362     const SkISize size = SkISize::Make(width, height);
363
364     if (NULL != bbhFactory) {
365         SkAutoTUnref<SkBBoxHierarchy> tree((*bbhFactory)(width, height));
366         SkASSERT(NULL != tree);
367         fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (this, size,
368                                                      recordingFlags|
369                                                      kOptimizeForClippedPlayback_RecordingFlag,
370                                                      tree.get()));
371     } else {
372         fRecord = SkNEW_ARGS(SkPictureRecord, (this, size, recordingFlags));
373     }
374     fRecord->beginRecording();
375
376     return fRecord;
377 }
378
379
380 #ifdef SK_SUPPORT_LEGACY_DERIVED_PICTURE_CLASSES
381
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.
386
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;
391
392     SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
393                                        SkIntToScalar(fHeight));
394     bool sortDraws = false;  // Do not sort draw calls when bulk loading.
395
396     return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
397                            aspectRatio, sortDraws);
398 }
399
400 #endif
401
402 SkCanvas* SkPicture::getRecordingCanvas() const {
403     // will be null if we are not recording
404     return fRecord;
405 }
406
407 void SkPicture::endRecording() {
408     if (NULL == fPlayback) {
409         if (NULL != fRecord) {
410             fRecord->endRecording();
411             SkPictInfo info;
412             this->createHeader(&info);
413             fPlayback = SkNEW_ARGS(SkPicturePlayback, (this, *fRecord, info));
414             SkSafeSetNull(fRecord);
415         }
416     }
417     SkASSERT(NULL == fRecord);
418 }
419
420 const SkPicture::OperationList& SkPicture::OperationList::InvalidList() {
421     static OperationList gInvalid;
422     return gInvalid;
423 }
424
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);
429     }
430     return OperationList::InvalidList();
431 }
432
433 size_t SkPicture::EXPERIMENTAL_curOpID() const {
434     if (NULL != fPlayback) {
435         return fPlayback->curOpID();
436     }
437     return 0;
438 }
439
440 void SkPicture::draw(SkCanvas* surface, SkDrawPictureCallback* callback) {
441     this->endRecording(); // TODO: remove eventually
442     if (NULL != fPlayback) {
443         fPlayback->draw(*surface, callback);
444     }
445 }
446
447 ///////////////////////////////////////////////////////////////////////////////
448
449 #include "SkStream.h"
450
451 static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
452
453 bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
454     if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
455         return false;
456     }
457
458     if (info.fVersion < MIN_PICTURE_VERSION ||
459         info.fVersion > CURRENT_PICTURE_VERSION) {
460         return false;
461     }
462
463     return true;
464 }
465
466 bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
467     if (NULL == stream) {
468         return false;
469     }
470
471     // Check magic bytes.
472     SkPictInfo info;
473     SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
474     if (!stream->read(&info, sizeof(info)) || !IsValidPictInfo(info)) {
475         return false;
476     }
477
478     if (pInfo != NULL) {
479         *pInfo = info;
480     }
481     return true;
482 }
483
484 bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer& buffer, SkPictInfo* pInfo) {
485     // Check magic bytes.
486     SkPictInfo info;
487     SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
488     if (!buffer.readByteArray(&info, sizeof(info)) || !IsValidPictInfo(info)) {
489         return false;
490     }
491
492     if (pInfo != NULL) {
493         *pInfo = info;
494     }
495     return true;
496 }
497
498 SkPicture::SkPicture(SkPicturePlayback* playback, int width, int height)
499     : fPlayback(playback)
500     , fRecord(NULL)
501     , fWidth(width)
502     , fHeight(height)
503     , fAccelData(NULL) {
504     this->needsNewGenID();
505 }
506
507 SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
508     SkPictInfo info;
509
510     if (!InternalOnly_StreamIsSKP(stream, &info)) {
511         return NULL;
512     }
513
514     SkPicture* newPict = SkNEW_ARGS(SkPicture, (NULL, info.fWidth, info.fHeight));
515
516     // Check to see if there is a playback to recreate.
517     if (stream->readBool()) {
518         SkPicturePlayback* playback = SkPicturePlayback::CreateFromStream(newPict, stream,
519                                                                           info, proc);
520         if (NULL == playback) {
521             SkDELETE(newPict);
522             return NULL;
523         }
524         newPict->fPlayback = playback;
525     }
526
527     return newPict;
528 }
529
530 SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
531     SkPictInfo info;
532
533     if (!InternalOnly_BufferIsSKP(buffer, &info)) {
534         return NULL;
535     }
536
537     SkPicture* newPict = SkNEW_ARGS(SkPicture, (NULL, info.fWidth, info.fHeight));
538
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) {
543             SkDELETE(newPict);
544             return NULL;
545         }
546         newPict->fPlayback = playback;
547     }
548
549     return newPict;
550 }
551
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));
557
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;
565
566     if (8 == sizeof(void*)) {
567         info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
568     }
569 }
570
571 void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
572     SkPicturePlayback* playback = fPlayback;
573
574     SkPictInfo info;
575     this->createHeader(&info);
576     if (NULL == playback && fRecord) {
577         playback = SkNEW_ARGS(SkPicturePlayback, (this, *fRecord, info));
578     }
579
580     stream->write(&info, sizeof(info));
581     if (playback) {
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) {
586             SkDELETE(playback);
587         }
588     } else {
589         stream->writeBool(false);
590     }
591 }
592
593 void SkPicture::WriteTagSize(SkWriteBuffer& buffer, uint32_t tag, size_t size) {
594     buffer.writeUInt(tag);
595     buffer.writeUInt(SkToU32(size));
596 }
597
598 void SkPicture::WriteTagSize(SkWStream* stream, uint32_t tag,  size_t size) {
599     stream->write32(tag);
600     stream->write32(SkToU32(size));
601 }
602
603 bool SkPicture::parseBufferTag(SkReadBuffer& buffer,
604                                uint32_t tag,
605                                uint32_t size) {
606     switch (tag) {
607         case SK_PICT_PATH_BUFFER_TAG:
608             if (size > 0) {
609                 fPathHeap.reset(SkNEW_ARGS(SkPathHeap, (buffer)));
610             }
611             break;
612         default:
613             // The tag was invalid.
614             return false;
615     }
616
617     return true;    // success
618 }
619
620 void SkPicture::flattenToBuffer(SkWriteBuffer& buffer) const {
621     int n;
622
623     if ((n = SafeCount(fPathHeap.get())) > 0) {
624         WriteTagSize(buffer, SK_PICT_PATH_BUFFER_TAG, n);
625         fPathHeap->flatten(buffer);
626     }
627 }
628
629 void SkPicture::flatten(SkWriteBuffer& buffer) const {
630     SkPicturePlayback* playback = fPlayback;
631
632     SkPictInfo info;
633     this->createHeader(&info);
634     if (NULL == playback && fRecord) {
635         playback = SkNEW_ARGS(SkPicturePlayback, (this, *fRecord, info));
636     }
637
638     buffer.writeByteArray(&info, sizeof(info));
639     if (playback) {
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) {
644             SkDELETE(playback);
645         }
646     } else {
647         buffer.writeBool(false);
648     }
649 }
650
651 #if SK_SUPPORT_GPU
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;
656
657     SkASSERT(this->numAAHairlineConcavePaths() <= this->numAAConcavePaths());
658
659     return this->numPaintWithPathEffectUses() < kNumPaintWithPathEffectUsesTol &&
660            (this->numAAConcavePaths()-this->numAAHairlineConcavePaths()) < kNumAAConcavePaths;
661 }
662 #endif
663
664 bool SkPicture::willPlayBackBitmaps() const {
665     if (!fPlayback) {
666         return false;
667     }
668     return fPlayback->containsBitmaps();
669 }
670
671 #ifdef SK_BUILD_FOR_ANDROID
672 void SkPicture::abortPlayback() {
673     if (NULL == fPlayback) {
674         return;
675     }
676     fPlayback->abort();
677 }
678 #endif
679
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
683     // return a 0
684     int32_t genID;
685     do {
686         genID = sk_atomic_inc(&gPictureGenerationID) + 1;
687     } while (SK_InvalidGenID == genID);
688     return genID;
689 }
690
691 uint32_t SkPicture::uniqueID() const {
692     if (NULL != fRecord) {
693         SkASSERT(NULL == fPlayback);
694         return SK_InvalidGenID;
695     }
696
697     if (SK_InvalidGenID == fUniqueID) {
698         fUniqueID = next_picture_generation_id();
699     }
700     return fUniqueID;
701 }