SkPicture.cpp revision 6b392b6111fad269437517494b14be484cd762f6
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
35template <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
57const 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
107static 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
126SkPicture::SkPicture()
127    : fAccelData(NULL) {
128    this->needsNewGenID();
129    fRecord = NULL;
130    fPlayback = NULL;
131    fWidth = fHeight = 0;
132}
133
134SkPicture::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
163const SkPath& SkPicture::getPath(int index) const {
164    return (*fPathHeap.get())[index];
165}
166
167int 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
178void 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
187void SkPicture::dumpSize() const {
188    SkDebugf("--- picture size: paths=%d\n",
189             SafeCount(fPathHeap.get()));
190}
191
192SkPicture::~SkPicture() {
193    SkSafeUnref(fRecord);
194    SkDELETE(fPlayback);
195    SkSafeUnref(fAccelData);
196}
197
198void SkPicture::internalOnly_EnableOpts(bool enableOpts) {
199    if (NULL != fRecord) {
200        fRecord->internalOnly_EnableOpts(enableOpts);
201    }
202}
203
204void 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
215SkPicture* SkPicture::clone() const {
216    SkPicture* clonedPicture = SkNEW(SkPicture);
217    this->clone(clonedPicture, 1);
218    return clonedPicture;
219}
220
221void 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
297SkPicture::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
312SkCanvas* 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
345SkCanvas* 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
382SkBBoxHierarchy* 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
402SkCanvas* SkPicture::getRecordingCanvas() const {
403    // will be null if we are not recording
404    return fRecord;
405}
406
407void 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
420const SkPicture::OperationList& SkPicture::OperationList::InvalidList() {
421    static OperationList gInvalid;
422    return gInvalid;
423}
424
425const 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
433size_t SkPicture::EXPERIMENTAL_curOpID() const {
434    if (NULL != fPlayback) {
435        return fPlayback->curOpID();
436    }
437    return 0;
438}
439
440void 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
451static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
452
453bool 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
466bool 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
484bool 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
498SkPicture::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
507SkPicture* 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
530SkPicture* 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
552void 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
571void 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
593void SkPicture::WriteTagSize(SkWriteBuffer& buffer, uint32_t tag, size_t size) {
594    buffer.writeUInt(tag);
595    buffer.writeUInt(SkToU32(size));
596}
597
598void SkPicture::WriteTagSize(SkWStream* stream, uint32_t tag,  size_t size) {
599    stream->write32(tag);
600    stream->write32(SkToU32(size));
601}
602
603bool 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
620void 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
629void 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
652bool 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
664bool SkPicture::willPlayBackBitmaps() const {
665    if (!fPlayback) {
666        return false;
667    }
668    return fPlayback->containsBitmaps();
669}
670
671#ifdef SK_BUILD_FOR_ANDROID
672void SkPicture::abortPlayback() {
673    if (NULL == fPlayback) {
674        return;
675    }
676    fPlayback->abort();
677}
678#endif
679
680static 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
691uint32_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}
702