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        const SkPicture::Analysis& analysis = op.picture->fAnalysis;
146        numPaintWithPathEffectUses += analysis.fNumPaintWithPathEffectUses;
147        numFastPathDashEffects     += analysis.fNumFastPathDashEffects;
148        numAAConcavePaths          += analysis.fNumAAConcavePaths;
149        numAAHairlineConcavePaths  += analysis.fNumAAHairlineConcavePaths;
150    }
151
152    void checkPaint(const SkPaint* paint) {
153        if (paint && paint->getPathEffect()) {
154            numPaintWithPathEffectUses++;
155        }
156    }
157
158    void operator()(const SkRecords::DrawPoints& op) {
159        this->checkPaint(&op.paint);
160        const SkPathEffect* effect = op.paint.getPathEffect();
161        if (effect) {
162            SkPathEffect::DashInfo info;
163            SkPathEffect::DashType dashType = effect->asADash(&info);
164            if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() &&
165                SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) {
166                numFastPathDashEffects++;
167            }
168        }
169    }
170
171    void operator()(const SkRecords::DrawPath& op) {
172        this->checkPaint(&op.paint);
173        if (op.paint.isAntiAlias() && !op.path.isConvex()) {
174            numAAConcavePaths++;
175
176            if (SkPaint::kStroke_Style == op.paint.getStyle() &&
177                0 == op.paint.getStrokeWidth()) {
178                numAAHairlineConcavePaths++;
179            }
180        }
181    }
182
183    template <typename T>
184    SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
185        this->checkPaint(AsPtr(op.paint));
186    }
187
188    template <typename T>
189    SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ }
190
191    int numPaintWithPathEffectUses;
192    int numFastPathDashEffects;
193    int numAAConcavePaths;
194    int numAAHairlineConcavePaths;
195};
196
197SkPicture::Analysis::Analysis(const SkRecord& record) {
198    fWillPlaybackBitmaps = WillPlaybackBitmaps(record);
199
200    PathCounter counter;
201    for (unsigned i = 0; i < record.count(); i++) {
202        record.visit<void>(i, counter);
203    }
204    fNumPaintWithPathEffectUses = counter.numPaintWithPathEffectUses;
205    fNumFastPathDashEffects     = counter.numFastPathDashEffects;
206    fNumAAConcavePaths          = counter.numAAConcavePaths;
207    fNumAAHairlineConcavePaths  = counter.numAAHairlineConcavePaths;
208
209    fHasText = false;
210    TextHunter text;
211    for (unsigned i = 0; i < record.count(); i++) {
212        if (record.visit<bool>(i, text)) {
213            fHasText = true;
214            break;
215        }
216    }
217}
218
219bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason,
220                                                      int sampleCount) const {
221    // TODO: the heuristic used here needs to be refined
222    static const int kNumPaintWithPathEffectsUsesTol = 1;
223    static const int kNumAAConcavePathsTol = 5;
224
225    int numNonDashedPathEffects = fNumPaintWithPathEffectUses -
226                                  fNumFastPathDashEffects;
227    bool suitableForDash = (0 == fNumPaintWithPathEffectUses) ||
228                           (numNonDashedPathEffects < kNumPaintWithPathEffectsUsesTol
229                               && 0 == sampleCount);
230
231    bool ret = suitableForDash &&
232               (fNumAAConcavePaths - fNumAAHairlineConcavePaths)
233                   < kNumAAConcavePathsTol;
234
235    if (!ret && reason) {
236        if (!suitableForDash) {
237            if (0 != sampleCount) {
238                *reason = "Can't use multisample on dash effect.";
239            } else {
240                *reason = "Too many non dashed path effects.";
241            }
242        } else if ((fNumAAConcavePaths - fNumAAHairlineConcavePaths)
243                    >= kNumAAConcavePathsTol)
244            *reason = "Too many anti-aliased concave paths.";
245        else
246            *reason = "Unknown reason for GPU unsuitability.";
247    }
248    return ret;
249}
250
251///////////////////////////////////////////////////////////////////////////////
252
253// fRecord OK
254SkPicture::SkPicture(SkScalar width, SkScalar height,
255                     const SkPictureRecord& record,
256                     bool deepCopyOps)
257    : fCullWidth(width)
258    , fCullHeight(height)
259    , fAnalysis() {
260    this->needsNewGenID();
261
262    SkPictInfo info;
263    this->createHeader(&info);
264    fData.reset(SkNEW_ARGS(SkPictureData, (record, info, deepCopyOps)));
265}
266
267// Create an SkPictureData-backed SkPicture from an SkRecord.
268// This for compatibility with serialization code only.  This is not cheap.
269static SkPicture* backport(const SkRecord& src, const SkRect& cullRect) {
270    SkPictureRecorder recorder;
271    SkRecordDraw(src,
272                 recorder.DEPRECATED_beginRecording(cullRect.width(), cullRect.height()),
273                 NULL/*bbh*/, NULL/*callback*/);
274    return recorder.endRecording();
275}
276
277// fRecord OK
278SkPicture::~SkPicture() {
279    this->callDeletionListeners();
280}
281
282// fRecord OK
283#ifdef SK_SUPPORT_LEGACY_PICTURE_CLONE
284SkPicture* SkPicture::clone() const {
285    return SkRef(const_cast<SkPicture*>(this));
286}
287#endif//SK_SUPPORT_LEGACY_PICTURE_CLONE
288
289// fRecord OK
290void SkPicture::EXPERIMENTAL_addAccelData(const SkPicture::AccelData* data) const {
291    fAccelData.reset(SkRef(data));
292}
293
294// fRecord OK
295const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData(
296        SkPicture::AccelData::Key key) const {
297    if (fAccelData.get() && fAccelData->getKey() == key) {
298        return fAccelData.get();
299    }
300    return NULL;
301}
302
303// fRecord OK
304SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
305    static int32_t gNextID = 0;
306
307    int32_t id = sk_atomic_inc(&gNextID);
308    if (id >= 1 << (8 * sizeof(Domain))) {
309        SK_CRASH();
310    }
311
312    return static_cast<Domain>(id);
313}
314
315///////////////////////////////////////////////////////////////////////////////
316
317uint32_t SkPicture::OperationList::offset(int index) const {
318    SkASSERT(index < fOps.count());
319    return ((SkPictureStateTree::Draw*)fOps[index])->fOffset;
320}
321
322const SkMatrix& SkPicture::OperationList::matrix(int index) const {
323    SkASSERT(index < fOps.count());
324    return *((SkPictureStateTree::Draw*)fOps[index])->fMatrix;
325}
326
327// fRecord OK
328void SkPicture::playback(SkCanvas* canvas, SkDrawPictureCallback* callback) const {
329    SkASSERT(canvas);
330    SkASSERT(fData.get() || fRecord.get());
331
332    // If the query contains the whole picture, don't bother with the BBH.
333    SkRect clipBounds = { 0, 0, 0, 0 };
334    (void)canvas->getClipBounds(&clipBounds);
335    const bool useBBH = !clipBounds.contains(this->cullRect());
336
337    if (fData.get()) {
338        SkPicturePlayback playback(this);
339        playback.setUseBBH(useBBH);
340        playback.draw(canvas, callback);
341    }
342    if (fRecord.get()) {
343        SkRecordDraw(*fRecord, canvas, useBBH ? fBBH.get() : NULL, callback);
344    }
345}
346
347///////////////////////////////////////////////////////////////////////////////
348
349#include "SkStream.h"
350
351static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
352
353// fRecord OK
354bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
355    if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
356        return false;
357    }
358
359    if (info.fVersion < MIN_PICTURE_VERSION ||
360        info.fVersion > CURRENT_PICTURE_VERSION) {
361        return false;
362    }
363
364    return true;
365}
366
367// fRecord OK
368bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
369    if (NULL == stream) {
370        return false;
371    }
372
373    // Check magic bytes.
374    SkPictInfo info;
375    SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
376
377    if (!stream->read(&info.fMagic, sizeof(kMagic))) {
378        return false;
379    }
380
381    info.fVersion = stream->readU32();
382
383#ifndef V35_COMPATIBILITY_CODE
384    if (info.fVersion < 35) {
385        info.fCullRect.fLeft = 0;
386        info.fCullRect.fTop = 0;
387        info.fCullRect.fRight = SkIntToScalar(stream->readU32());
388        info.fCullRect.fBottom = SkIntToScalar(stream->readU32());
389    } else {
390#endif
391        info.fCullRect.fLeft = stream->readScalar();
392        info.fCullRect.fTop = stream->readScalar();
393        info.fCullRect.fRight = stream->readScalar();
394        info.fCullRect.fBottom = stream->readScalar();
395#ifndef V35_COMPATIBILITY_CODE
396    }
397#endif
398
399    info.fFlags = stream->readU32();
400
401    if (!IsValidPictInfo(info)) {
402        return false;
403    }
404
405    if (pInfo != NULL) {
406        *pInfo = info;
407    }
408    return true;
409}
410
411// fRecord OK
412bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
413    // Check magic bytes.
414    SkPictInfo info;
415    SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
416
417    if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) {
418        return false;
419    }
420
421    info.fVersion = buffer->readUInt();
422
423#ifndef V35_COMPATIBILITY_CODE
424    if (info.fVersion < 35) {
425        info.fCullRect.fLeft = 0;
426        info.fCullRect.fTop = 0;
427        info.fCullRect.fRight = SkIntToScalar(buffer->readUInt());
428        info.fCullRect.fBottom = SkIntToScalar(buffer->readUInt());
429    } else {
430#endif
431        buffer->readRect(&info.fCullRect);
432#ifndef V35_COMPATIBILITY_CODE
433    }
434#endif
435
436    info.fFlags = buffer->readUInt();
437
438    if (!IsValidPictInfo(info)) {
439        return false;
440    }
441
442    if (pInfo != NULL) {
443        *pInfo = info;
444    }
445    return true;
446}
447
448// fRecord OK
449SkPicture::SkPicture(SkPictureData* data, SkScalar width, SkScalar height)
450    : fData(data)
451    , fCullWidth(width)
452    , fCullHeight(height)
453    , fAnalysis() {
454    this->needsNewGenID();
455}
456
457SkPicture* SkPicture::Forwardport(const SkPicture& src) {
458    SkAutoTDelete<SkRecord> record(SkNEW(SkRecord));
459    SkRecorder canvas(record.get(), src.cullRect().width(), src.cullRect().height());
460    src.playback(&canvas);
461    return SkNEW_ARGS(SkPicture, (src.cullRect().width(), src.cullRect().height(),
462                                  record.detach(), NULL/*bbh*/));
463}
464
465// fRecord OK
466SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
467    SkPictInfo info;
468
469    if (!InternalOnly_StreamIsSKP(stream, &info)) {
470        return NULL;
471    }
472
473    // Check to see if there is a playback to recreate.
474    if (stream->readBool()) {
475        SkPictureData* data = SkPictureData::CreateFromStream(stream, info, proc);
476        if (NULL == data) {
477            return NULL;
478        }
479        const SkPicture src(data, info.fCullRect.width(), info.fCullRect.height());
480        return Forwardport(src);
481    }
482
483    return NULL;
484}
485
486// fRecord OK
487SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
488    SkPictInfo info;
489
490    if (!InternalOnly_BufferIsSKP(&buffer, &info)) {
491        return NULL;
492    }
493
494    // Check to see if there is a playback to recreate.
495    if (buffer.readBool()) {
496        SkPictureData* data = SkPictureData::CreateFromBuffer(buffer, info);
497        if (NULL == data) {
498            return NULL;
499        }
500        const SkPicture src(data, info.fCullRect.width(), info.fCullRect.height());
501        return Forwardport(src);
502    }
503
504    return NULL;
505}
506
507// fRecord OK
508void SkPicture::createHeader(SkPictInfo* info) const {
509    // Copy magic bytes at the beginning of the header
510    SkASSERT(sizeof(kMagic) == 8);
511    SkASSERT(sizeof(kMagic) == sizeof(info->fMagic));
512    memcpy(info->fMagic, kMagic, sizeof(kMagic));
513
514    // Set picture info after magic bytes in the header
515    info->fVersion = CURRENT_PICTURE_VERSION;
516    info->fCullRect = this->cullRect();
517    info->fFlags = SkPictInfo::kCrossProcess_Flag;
518    // TODO: remove this flag, since we're always float (now)
519    info->fFlags |= SkPictInfo::kScalarIsFloat_Flag;
520
521    if (8 == sizeof(void*)) {
522        info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
523    }
524}
525
526// fRecord OK
527void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
528    const SkPictureData* data = fData.get();
529
530    // If we're a new-format picture, backport to old format for serialization.
531    SkAutoTDelete<SkPicture> oldFormat;
532    if (NULL == data && fRecord.get()) {
533        oldFormat.reset(backport(*fRecord, this->cullRect()));
534        data = oldFormat->fData.get();
535        SkASSERT(data);
536    }
537
538    SkPictInfo info;
539    this->createHeader(&info);
540    SkASSERT(sizeof(SkPictInfo) == 32);
541    stream->write(&info, sizeof(info));
542
543    if (data) {
544        stream->writeBool(true);
545        data->serialize(stream, encoder);
546    } else {
547        stream->writeBool(false);
548    }
549}
550
551// fRecord OK
552void SkPicture::flatten(SkWriteBuffer& buffer) const {
553    const SkPictureData* data = fData.get();
554
555    // If we're a new-format picture, backport to old format for serialization.
556    SkAutoTDelete<SkPicture> oldFormat;
557    if (NULL == data && fRecord.get()) {
558        oldFormat.reset(backport(*fRecord, this->cullRect()));
559        data = oldFormat->fData.get();
560        SkASSERT(data);
561    }
562
563    SkPictInfo info;
564    this->createHeader(&info);
565    buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic));
566    buffer.writeUInt(info.fVersion);
567    buffer.writeRect(info.fCullRect);
568    buffer.writeUInt(info.fFlags);
569
570    if (data) {
571        buffer.writeBool(true);
572        data->flatten(buffer);
573    } else {
574        buffer.writeBool(false);
575    }
576}
577
578#if SK_SUPPORT_GPU
579// fRecord OK
580bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **reason) const {
581    if (fRecord.get()) {
582        return fAnalysis.suitableForGpuRasterization(reason, 0);
583    }
584    if (NULL == fData.get()) {
585        if (reason) {
586            *reason = "Missing internal data.";
587        }
588        return false;
589    }
590
591    return fData->suitableForGpuRasterization(context, reason);
592}
593#endif
594
595// fRecord OK
596bool SkPicture::hasText() const {
597    if (fRecord.get()) {
598        return fAnalysis.fHasText;
599    }
600    if (fData.get()) {
601        return fData->hasText();
602    }
603    SkFAIL("Unreachable");
604    return false;
605}
606
607// fRecord OK
608bool SkPicture::willPlayBackBitmaps() const {
609    if (fRecord.get()) {
610        return fAnalysis.fWillPlaybackBitmaps;
611    }
612    if (fData.get()) {
613        return fData->containsBitmaps();
614    }
615    SkFAIL("Unreachable");
616    return false;
617}
618
619// fRecord OK
620static int32_t next_picture_generation_id() {
621    static int32_t  gPictureGenerationID = 0;
622    // do a loop in case our global wraps around, as we never want to
623    // return a 0
624    int32_t genID;
625    do {
626        genID = sk_atomic_inc(&gPictureGenerationID) + 1;
627    } while (SK_InvalidGenID == genID);
628    return genID;
629}
630
631// fRecord OK
632uint32_t SkPicture::uniqueID() const {
633    if (SK_InvalidGenID == fUniqueID) {
634        fUniqueID = next_picture_generation_id();
635    }
636    return fUniqueID;
637}
638
639
640static SkRecord* optimized(SkRecord* r) {
641#ifdef SK_PICTURE_OPTIMIZE_SK_RECORD
642    SkRecordOptimize(r);
643#endif
644    return r;
645}
646
647// fRecord OK
648SkPicture::SkPicture(SkScalar width, SkScalar height, SkRecord* record, SkBBoxHierarchy* bbh)
649    : fCullWidth(width)
650    , fCullHeight(height)
651    , fRecord(optimized(record))
652    , fBBH(SkSafeRef(bbh))
653    , fAnalysis(*fRecord) {
654    // TODO: delay as much of this work until just before first playback?
655    if (fBBH.get()) {
656        SkRecordFillBounds(*fRecord, fBBH.get());
657    }
658    this->needsNewGenID();
659}
660
661// Note that we are assuming that this entry point will only be called from
662// one thread. Currently the only client of this method is
663// SkGpuDevice::EXPERIMENTAL_optimize which should be only called from a single
664// thread.
665void SkPicture::addDeletionListener(DeletionListener* listener) const {
666    SkASSERT(listener);
667
668    *fDeletionListeners.append() = SkRef(listener);
669}
670
671void SkPicture::callDeletionListeners() {
672    for (int i = 0; i < fDeletionListeners.count(); ++i) {
673        fDeletionListeners[i]->onDeletion(this->uniqueID());
674    }
675
676    fDeletionListeners.unrefAll();
677}
678
679// fRecord OK
680int SkPicture::approximateOpCount() const {
681    SkASSERT(fRecord.get() || fData.get());
682    if (fRecord.get()) {
683        return fRecord->count();
684    }
685    if (fData.get()) {
686        return fData->opCount();
687    }
688    return 0;
689}
690