SkPicture.cpp revision 81940de68893e6a643301f9930db630764729ea8
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 "SkPictureData.h"
12#include "SkPicturePlayback.h"
13#include "SkPictureRecord.h"
14#include "SkPictureRecorder.h"
15#include "SkPictureStateTree.h"
16
17#include "SkBitmapDevice.h"
18#include "SkCanvas.h"
19#include "SkChunkAlloc.h"
20#include "SkDrawPictureCallback.h"
21#include "SkPaintPriv.h"
22#include "SkPathEffect.h"
23#include "SkPicture.h"
24#include "SkRegion.h"
25#include "SkShader.h"
26#include "SkStream.h"
27#include "SkTDArray.h"
28#include "SkTLogic.h"
29#include "SkTSearch.h"
30#include "SkTime.h"
31
32#include "SkReader32.h"
33#include "SkWriter32.h"
34#include "SkRTree.h"
35#include "SkBBoxHierarchyRecord.h"
36
37#if SK_SUPPORT_GPU
38#include "GrContext.h"
39#endif
40
41#include "SkRecord.h"
42#include "SkRecordDraw.h"
43#include "SkRecordOpts.h"
44#include "SkRecorder.h"
45
46template <typename T> int SafeCount(const T* obj) {
47    return obj ? obj->count() : 0;
48}
49
50///////////////////////////////////////////////////////////////////////////////
51
52namespace {
53
54// Some commands have a paint, some have an optional paint.  Either way, get back a pointer.
55static const SkPaint* AsPtr(const SkPaint& p) { return &p; }
56static const SkPaint* AsPtr(const SkRecords::Optional<SkPaint>& p) { return p; }
57
58/** SkRecords visitor to determine whether an instance may require an
59    "external" bitmap to rasterize. May return false positives.
60    Does not return true for bitmap text.
61
62    Expected use is to determine whether images need to be decoded before
63    rasterizing a particular SkRecord.
64 */
65struct BitmapTester {
66    // Helpers.  These create HasMember_bitmap and HasMember_paint.
67    SK_CREATE_MEMBER_DETECTOR(bitmap);
68    SK_CREATE_MEMBER_DETECTOR(paint);
69
70
71    // Main entry for visitor:
72    // If the command is a DrawPicture, recurse.
73    // If the command has a bitmap directly, return true.
74    // If the command has a paint and the paint has a bitmap, return true.
75    // Otherwise, return false.
76    bool operator()(const SkRecords::DrawPicture& op) { return op.picture->willPlayBackBitmaps(); }
77
78    template <typename T>
79    bool operator()(const T& r) { return CheckBitmap(r); }
80
81
82    // If the command has a bitmap, of course we're going to play back bitmaps.
83    template <typename T>
84    static SK_WHEN(HasMember_bitmap<T>, bool) CheckBitmap(const T&) { return true; }
85
86    // If not, look for one in its paint (if it has a paint).
87    template <typename T>
88    static SK_WHEN(!HasMember_bitmap<T>, bool) CheckBitmap(const T& r) { return CheckPaint(r); }
89
90    // If we have a paint, dig down into the effects looking for a bitmap.
91    template <typename T>
92    static SK_WHEN(HasMember_paint<T>, bool) CheckPaint(const T& r) {
93        const SkPaint* paint = AsPtr(r.paint);
94        if (paint) {
95            const SkShader* shader = paint->getShader();
96            if (shader &&
97                shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
98                return true;
99            }
100        }
101        return false;
102    }
103
104    // If we don't have a paint, that non-paint has no bitmap.
105    template <typename T>
106    static SK_WHEN(!HasMember_paint<T>, bool) CheckPaint(const T&) { return false; }
107};
108
109bool WillPlaybackBitmaps(const SkRecord& record) {
110    BitmapTester tester;
111    for (unsigned i = 0; i < record.count(); i++) {
112        if (record.visit<bool>(i, tester)) {
113            return true;
114        }
115    }
116    return false;
117}
118
119// SkRecord visitor to find recorded text.
120struct TextHunter {
121    // All ops with text have that text as a char array member named "text".
122    SK_CREATE_MEMBER_DETECTOR(text);
123    bool operator()(const SkRecords::DrawPicture& op) { return op.picture->hasText(); }
124    template <typename T> SK_WHEN(HasMember_text<T>,  bool) operator()(const T&) { return true;  }
125    template <typename T> SK_WHEN(!HasMember_text<T>, bool) operator()(const T&) { return false; }
126};
127
128} // namespace
129
130/** SkRecords visitor to determine heuristically whether or not a SkPicture
131    will be performant when rasterized on the GPU.
132 */
133struct SkPicture::PathCounter {
134    SK_CREATE_MEMBER_DETECTOR(paint);
135
136    PathCounter()
137        : numPaintWithPathEffectUses (0)
138        , numFastPathDashEffects (0)
139        , numAAConcavePaths (0)
140        , numAAHairlineConcavePaths (0) {
141    }
142
143    // Recurse into nested pictures.
144    void operator()(const SkRecords::DrawPicture& op) {
145        // If you're not also SkRecord-backed, tough luck.  Get on the bandwagon.
146        if (op.picture->fRecord.get() == NULL) {
147            return;
148        }
149        const SkRecord& nested = *op.picture->fRecord;
150        for (unsigned i = 0; i < nested.count(); i++) {
151            nested.visit<void>(i, *this);
152        }
153    }
154
155    void checkPaint(const SkPaint* paint) {
156        if (paint && paint->getPathEffect()) {
157            numPaintWithPathEffectUses++;
158        }
159    }
160
161    void operator()(const SkRecords::DrawPoints& op) {
162        this->checkPaint(&op.paint);
163        const SkPathEffect* effect = op.paint.getPathEffect();
164        if (effect) {
165            SkPathEffect::DashInfo info;
166            SkPathEffect::DashType dashType = effect->asADash(&info);
167            if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() &&
168                SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) {
169                numFastPathDashEffects++;
170            }
171        }
172    }
173
174    void operator()(const SkRecords::DrawPath& op) {
175        this->checkPaint(&op.paint);
176        if (op.paint.isAntiAlias() && !op.path.isConvex()) {
177            numAAConcavePaths++;
178
179            if (SkPaint::kStroke_Style == op.paint.getStyle() &&
180                0 == op.paint.getStrokeWidth()) {
181                numAAHairlineConcavePaths++;
182            }
183        }
184    }
185
186    template <typename T>
187    SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
188        this->checkPaint(AsPtr(op.paint));
189    }
190
191    template <typename T>
192    SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ }
193
194    int numPaintWithPathEffectUses;
195    int numFastPathDashEffects;
196    int numAAConcavePaths;
197    int numAAHairlineConcavePaths;
198};
199
200SkPicture::Analysis::Analysis(const SkRecord& record) {
201    fWillPlaybackBitmaps = WillPlaybackBitmaps(record);
202
203    PathCounter counter;
204    for (unsigned i = 0; i < record.count(); i++) {
205        record.visit<void>(i, counter);
206    }
207    fNumPaintWithPathEffectUses = counter.numPaintWithPathEffectUses;
208    fNumFastPathDashEffects     = counter.numFastPathDashEffects;
209    fNumAAConcavePaths          = counter.numAAConcavePaths;
210    fNumAAHairlineConcavePaths  = counter.numAAHairlineConcavePaths;
211
212    fHasText = false;
213    TextHunter text;
214    for (unsigned i = 0; i < record.count(); i++) {
215        if (record.visit<bool>(i, text)) {
216            fHasText = true;
217            break;
218        }
219    }
220}
221
222bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason,
223                                                      int sampleCount) const {
224    // TODO: the heuristic used here needs to be refined
225    static const int kNumPaintWithPathEffectsUsesTol = 1;
226    static const int kNumAAConcavePathsTol = 5;
227
228    int numNonDashedPathEffects = fNumPaintWithPathEffectUses -
229                                  fNumFastPathDashEffects;
230    bool suitableForDash = (0 == fNumPaintWithPathEffectUses) ||
231                           (numNonDashedPathEffects < kNumPaintWithPathEffectsUsesTol
232                               && 0 == sampleCount);
233
234    bool ret = suitableForDash &&
235               (fNumAAConcavePaths - fNumAAHairlineConcavePaths)
236                   < kNumAAConcavePathsTol;
237
238    if (!ret && reason) {
239        if (!suitableForDash) {
240            if (0 != sampleCount) {
241                *reason = "Can't use multisample on dash effect.";
242            } else {
243                *reason = "Too many non dashed path effects.";
244            }
245        } else if ((fNumAAConcavePaths - fNumAAHairlineConcavePaths)
246                    >= kNumAAConcavePathsTol)
247            *reason = "Too many anti-aliased concave paths.";
248        else
249            *reason = "Unknown reason for GPU unsuitability.";
250    }
251    return ret;
252}
253
254///////////////////////////////////////////////////////////////////////////////
255
256// fRecord OK
257SkPicture::SkPicture(SkScalar width, SkScalar height,
258                     const SkPictureRecord& record,
259                     bool deepCopyOps)
260    : fCullWidth(width)
261    , fCullHeight(height)
262    , fAnalysis() {
263    this->needsNewGenID();
264
265    SkPictInfo info;
266    this->createHeader(&info);
267    fData.reset(SkNEW_ARGS(SkPictureData, (record, info, deepCopyOps)));
268}
269
270// Create an SkPictureData-backed SkPicture from an SkRecord.
271// This for compatibility with serialization code only.  This is not cheap.
272static SkPicture* backport(const SkRecord& src, const SkRect& cullRect) {
273    SkPictureRecorder recorder;
274    SkRecordDraw(src,
275                 recorder.DEPRECATED_beginRecording(cullRect.width(), cullRect.height()),
276                 NULL/*bbh*/, NULL/*callback*/);
277    return recorder.endRecording();
278}
279
280// fRecord OK
281SkPicture::~SkPicture() {
282    this->callDeletionListeners();
283}
284
285// fRecord OK
286#ifdef SK_SUPPORT_LEGACY_PICTURE_CLONE
287SkPicture* SkPicture::clone() const {
288    return SkRef(const_cast<SkPicture*>(this));
289}
290#endif//SK_SUPPORT_LEGACY_PICTURE_CLONE
291
292// fRecord OK
293void SkPicture::EXPERIMENTAL_addAccelData(const SkPicture::AccelData* data) const {
294    fAccelData.reset(SkRef(data));
295}
296
297// fRecord OK
298const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData(
299        SkPicture::AccelData::Key key) const {
300    if (fAccelData.get() && fAccelData->getKey() == key) {
301        return fAccelData.get();
302    }
303    return NULL;
304}
305
306// fRecord OK
307SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
308    static int32_t gNextID = 0;
309
310    int32_t id = sk_atomic_inc(&gNextID);
311    if (id >= 1 << (8 * sizeof(Domain))) {
312        SK_CRASH();
313    }
314
315    return static_cast<Domain>(id);
316}
317
318///////////////////////////////////////////////////////////////////////////////
319
320uint32_t SkPicture::OperationList::offset(int index) const {
321    SkASSERT(index < fOps.count());
322    return ((SkPictureStateTree::Draw*)fOps[index])->fOffset;
323}
324
325const SkMatrix& SkPicture::OperationList::matrix(int index) const {
326    SkASSERT(index < fOps.count());
327    return *((SkPictureStateTree::Draw*)fOps[index])->fMatrix;
328}
329
330// fRecord OK
331void SkPicture::playback(SkCanvas* canvas, SkDrawPictureCallback* callback) const {
332    SkASSERT(canvas);
333    SkASSERT(fData.get() || fRecord.get());
334
335    // If the query contains the whole picture, don't bother with the BBH.
336    SkRect clipBounds = { 0, 0, 0, 0 };
337    (void)canvas->getClipBounds(&clipBounds);
338    const bool useBBH = !clipBounds.contains(this->cullRect());
339
340    if (fData.get()) {
341        SkPicturePlayback playback(this);
342        playback.setUseBBH(useBBH);
343        playback.draw(canvas, callback);
344    }
345    if (fRecord.get()) {
346        SkRecordDraw(*fRecord, canvas, useBBH ? fBBH.get() : NULL, callback);
347    }
348}
349
350///////////////////////////////////////////////////////////////////////////////
351
352#include "SkStream.h"
353
354static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
355
356// fRecord OK
357bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
358    if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
359        return false;
360    }
361
362    if (info.fVersion < MIN_PICTURE_VERSION ||
363        info.fVersion > CURRENT_PICTURE_VERSION) {
364        return false;
365    }
366
367    return true;
368}
369
370// fRecord OK
371bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
372    if (NULL == stream) {
373        return false;
374    }
375
376    // Check magic bytes.
377    SkPictInfo info;
378    SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
379
380    if (!stream->read(&info.fMagic, sizeof(kMagic))) {
381        return false;
382    }
383
384    info.fVersion = stream->readU32();
385
386#ifndef V35_COMPATIBILITY_CODE
387    if (info.fVersion < 35) {
388        info.fCullRect.fLeft = 0;
389        info.fCullRect.fTop = 0;
390        info.fCullRect.fRight = SkIntToScalar(stream->readU32());
391        info.fCullRect.fBottom = SkIntToScalar(stream->readU32());
392    } else {
393#endif
394        info.fCullRect.fLeft = stream->readScalar();
395        info.fCullRect.fTop = stream->readScalar();
396        info.fCullRect.fRight = stream->readScalar();
397        info.fCullRect.fBottom = stream->readScalar();
398#ifndef V35_COMPATIBILITY_CODE
399    }
400#endif
401
402    info.fFlags = stream->readU32();
403
404    if (!IsValidPictInfo(info)) {
405        return false;
406    }
407
408    if (pInfo != NULL) {
409        *pInfo = info;
410    }
411    return true;
412}
413
414// fRecord OK
415bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
416    // Check magic bytes.
417    SkPictInfo info;
418    SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
419
420    if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) {
421        return false;
422    }
423
424    info.fVersion = buffer->readUInt();
425
426#ifndef V35_COMPATIBILITY_CODE
427    if (info.fVersion < 35) {
428        info.fCullRect.fLeft = 0;
429        info.fCullRect.fTop = 0;
430        info.fCullRect.fRight = SkIntToScalar(buffer->readUInt());
431        info.fCullRect.fBottom = SkIntToScalar(buffer->readUInt());
432    } else {
433#endif
434        buffer->readRect(&info.fCullRect);
435#ifndef V35_COMPATIBILITY_CODE
436    }
437#endif
438
439    info.fFlags = buffer->readUInt();
440
441    if (!IsValidPictInfo(info)) {
442        return false;
443    }
444
445    if (pInfo != NULL) {
446        *pInfo = info;
447    }
448    return true;
449}
450
451// fRecord OK
452SkPicture::SkPicture(SkPictureData* data, SkScalar width, SkScalar height)
453    : fData(data)
454    , fCullWidth(width)
455    , fCullHeight(height)
456    , fAnalysis() {
457    this->needsNewGenID();
458}
459
460SkPicture* SkPicture::Forwardport(const SkPicture& src) {
461    SkAutoTDelete<SkRecord> record(SkNEW(SkRecord));
462    SkRecorder canvas(record.get(), src.cullRect().width(), src.cullRect().height());
463    src.playback(&canvas);
464    return SkNEW_ARGS(SkPicture, (src.cullRect().width(), src.cullRect().height(),
465                                  record.detach(), NULL/*bbh*/));
466}
467
468// fRecord OK
469SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
470    SkPictInfo info;
471
472    if (!InternalOnly_StreamIsSKP(stream, &info)) {
473        return NULL;
474    }
475
476    // Check to see if there is a playback to recreate.
477    if (stream->readBool()) {
478        SkPictureData* data = SkPictureData::CreateFromStream(stream, info, proc);
479        if (NULL == data) {
480            return NULL;
481        }
482        const SkPicture src(data, info.fCullRect.width(), info.fCullRect.height());
483        return Forwardport(src);
484    }
485
486    return NULL;
487}
488
489// fRecord OK
490SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
491    SkPictInfo info;
492
493    if (!InternalOnly_BufferIsSKP(&buffer, &info)) {
494        return NULL;
495    }
496
497    // Check to see if there is a playback to recreate.
498    if (buffer.readBool()) {
499        SkPictureData* data = SkPictureData::CreateFromBuffer(buffer, info);
500        if (NULL == data) {
501            return NULL;
502        }
503        const SkPicture src(data, info.fCullRect.width(), info.fCullRect.height());
504        return Forwardport(src);
505    }
506
507    return NULL;
508}
509
510// fRecord OK
511void SkPicture::createHeader(SkPictInfo* info) const {
512    // Copy magic bytes at the beginning of the header
513    SkASSERT(sizeof(kMagic) == 8);
514    SkASSERT(sizeof(kMagic) == sizeof(info->fMagic));
515    memcpy(info->fMagic, kMagic, sizeof(kMagic));
516
517    // Set picture info after magic bytes in the header
518    info->fVersion = CURRENT_PICTURE_VERSION;
519    info->fCullRect = this->cullRect();
520    info->fFlags = SkPictInfo::kCrossProcess_Flag;
521    // TODO: remove this flag, since we're always float (now)
522    info->fFlags |= SkPictInfo::kScalarIsFloat_Flag;
523
524    if (8 == sizeof(void*)) {
525        info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
526    }
527}
528
529// fRecord OK
530void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
531    const SkPictureData* data = fData.get();
532
533    // If we're a new-format picture, backport to old format for serialization.
534    SkAutoTDelete<SkPicture> oldFormat;
535    if (NULL == data && fRecord.get()) {
536        oldFormat.reset(backport(*fRecord, this->cullRect()));
537        data = oldFormat->fData.get();
538        SkASSERT(data);
539    }
540
541    SkPictInfo info;
542    this->createHeader(&info);
543    SkASSERT(sizeof(SkPictInfo) == 32);
544    stream->write(&info, sizeof(info));
545
546    if (data) {
547        stream->writeBool(true);
548        data->serialize(stream, encoder);
549    } else {
550        stream->writeBool(false);
551    }
552}
553
554// fRecord OK
555void SkPicture::flatten(SkWriteBuffer& buffer) const {
556    const SkPictureData* data = fData.get();
557
558    // If we're a new-format picture, backport to old format for serialization.
559    SkAutoTDelete<SkPicture> oldFormat;
560    if (NULL == data && fRecord.get()) {
561        oldFormat.reset(backport(*fRecord, this->cullRect()));
562        data = oldFormat->fData.get();
563        SkASSERT(data);
564    }
565
566    SkPictInfo info;
567    this->createHeader(&info);
568    buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic));
569    buffer.writeUInt(info.fVersion);
570    buffer.writeRect(info.fCullRect);
571    buffer.writeUInt(info.fFlags);
572
573    if (data) {
574        buffer.writeBool(true);
575        data->flatten(buffer);
576    } else {
577        buffer.writeBool(false);
578    }
579}
580
581#if SK_SUPPORT_GPU
582// fRecord OK
583bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **reason) const {
584    if (fRecord.get()) {
585        return fAnalysis.suitableForGpuRasterization(reason, 0);
586    }
587    if (NULL == fData.get()) {
588        if (reason) {
589            *reason = "Missing internal data.";
590        }
591        return false;
592    }
593
594    return fData->suitableForGpuRasterization(context, reason);
595}
596#endif
597
598// fRecord OK
599bool SkPicture::hasText() const {
600    if (fRecord.get()) {
601        return fAnalysis.fHasText;
602    }
603    if (fData.get()) {
604        return fData->hasText();
605    }
606    SkFAIL("Unreachable");
607    return false;
608}
609
610// fRecord OK
611bool SkPicture::willPlayBackBitmaps() const {
612    if (fRecord.get()) {
613        return fAnalysis.fWillPlaybackBitmaps;
614    }
615    if (fData.get()) {
616        return fData->containsBitmaps();
617    }
618    SkFAIL("Unreachable");
619    return false;
620}
621
622// fRecord OK
623static int32_t next_picture_generation_id() {
624    static int32_t  gPictureGenerationID = 0;
625    // do a loop in case our global wraps around, as we never want to
626    // return a 0
627    int32_t genID;
628    do {
629        genID = sk_atomic_inc(&gPictureGenerationID) + 1;
630    } while (SK_InvalidGenID == genID);
631    return genID;
632}
633
634// fRecord OK
635uint32_t SkPicture::uniqueID() const {
636    if (SK_InvalidGenID == fUniqueID) {
637        fUniqueID = next_picture_generation_id();
638    }
639    return fUniqueID;
640}
641
642
643static SkRecord* optimized(SkRecord* r) {
644#ifdef SK_PICTURE_OPTIMIZE_SK_RECORD
645    SkRecordOptimize(r);
646#endif
647    return r;
648}
649
650// fRecord OK
651SkPicture::SkPicture(SkScalar width, SkScalar height, SkRecord* record, SkBBoxHierarchy* bbh)
652    : fCullWidth(width)
653    , fCullHeight(height)
654    , fRecord(optimized(record))
655    , fBBH(SkSafeRef(bbh))
656    , fAnalysis(*fRecord) {
657    // TODO: delay as much of this work until just before first playback?
658    if (fBBH.get()) {
659        SkRecordFillBounds(*fRecord, fBBH.get());
660    }
661    this->needsNewGenID();
662}
663
664// Note that we are assuming that this entry point will only be called from
665// one thread. Currently the only client of this method is
666// SkGpuDevice::EXPERIMENTAL_optimize which should be only called from a single
667// thread.
668void SkPicture::addDeletionListener(DeletionListener* listener) const {
669    SkASSERT(listener);
670
671    *fDeletionListeners.append() = SkRef(listener);
672}
673
674void SkPicture::callDeletionListeners() {
675    for (int i = 0; i < fDeletionListeners.count(); ++i) {
676        fDeletionListeners[i]->onDeletion(this->uniqueID());
677    }
678
679    fDeletionListeners.unrefAll();
680}
681
682// fRecord OK
683int SkPicture::approximateOpCount() const {
684    SkASSERT(fRecord.get() || fData.get());
685    if (fRecord.get()) {
686        return fRecord->count();
687    }
688    if (fData.get()) {
689        return fData->opCount();
690    }
691    return 0;
692}
693