1/*
2 * Copyright 2007 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8
9#include "SkPictureFlat.h"
10#include "SkPictureData.h"
11#include "SkPicturePlayback.h"
12#include "SkPictureRecord.h"
13#include "SkPictureRecorder.h"
14
15#include "SkAtomics.h"
16#include "SkBitmapDevice.h"
17#include "SkCanvas.h"
18#include "SkChunkAlloc.h"
19#include "SkMessageBus.h"
20#include "SkPaintPriv.h"
21#include "SkPathEffect.h"
22#include "SkPicture.h"
23#include "SkRegion.h"
24#include "SkShader.h"
25#include "SkStream.h"
26#include "SkTDArray.h"
27#include "SkTLogic.h"
28#include "SkTSearch.h"
29#include "SkTime.h"
30
31#include "SkReader32.h"
32#include "SkWriter32.h"
33#include "SkRTree.h"
34
35#if SK_SUPPORT_GPU
36#include "GrContext.h"
37#endif
38
39#include "SkRecord.h"
40#include "SkRecordDraw.h"
41#include "SkRecordOpts.h"
42#include "SkRecorder.h"
43
44DECLARE_SKMESSAGEBUS_MESSAGE(SkPicture::DeletionMessage);
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() : fNumSlowPathsAndDashEffects(0) {}
137
138    // Recurse into nested pictures.
139    void operator()(const SkRecords::DrawPicture& op) {
140        const SkPicture::Analysis& analysis = op.picture->analysis();
141        fNumSlowPathsAndDashEffects += analysis.fNumSlowPathsAndDashEffects;
142    }
143
144    void checkPaint(const SkPaint* paint) {
145        if (paint && paint->getPathEffect()) {
146            // Initially assume it's slow.
147            fNumSlowPathsAndDashEffects++;
148        }
149    }
150
151    void operator()(const SkRecords::DrawPoints& op) {
152        this->checkPaint(&op.paint);
153        const SkPathEffect* effect = op.paint.getPathEffect();
154        if (effect) {
155            SkPathEffect::DashInfo info;
156            SkPathEffect::DashType dashType = effect->asADash(&info);
157            if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() &&
158                SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) {
159                fNumSlowPathsAndDashEffects--;
160            }
161        }
162    }
163
164    void operator()(const SkRecords::DrawPath& op) {
165        this->checkPaint(&op.paint);
166        if (op.paint.isAntiAlias() && !op.path.isConvex()) {
167            SkPaint::Style paintStyle = op.paint.getStyle();
168            const SkRect& pathBounds = op.path.getBounds();
169            if (SkPaint::kStroke_Style == paintStyle &&
170                0 == op.paint.getStrokeWidth()) {
171                // AA hairline concave path is not slow.
172            } else if (SkPaint::kFill_Style == paintStyle && pathBounds.width() < 64.f &&
173                       pathBounds.height() < 64.f && !op.path.isVolatile()) {
174                // AADF eligible concave path is not slow.
175            } else {
176                fNumSlowPathsAndDashEffects++;
177            }
178        }
179    }
180
181    template <typename T>
182    SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
183        this->checkPaint(AsPtr(op.paint));
184    }
185
186    template <typename T>
187    SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ }
188
189    int fNumSlowPathsAndDashEffects;
190};
191
192SkPicture::Analysis::Analysis(const SkRecord& record) {
193    fWillPlaybackBitmaps = WillPlaybackBitmaps(record);
194
195    PathCounter counter;
196    for (unsigned i = 0; i < record.count(); i++) {
197        record.visit<void>(i, counter);
198    }
199    fNumSlowPathsAndDashEffects = SkTMin<int>(counter.fNumSlowPathsAndDashEffects, 255);
200
201    fHasText = false;
202    TextHunter text;
203    for (unsigned i = 0; i < record.count(); i++) {
204        if (record.visit<bool>(i, text)) {
205            fHasText = true;
206            break;
207        }
208    }
209}
210
211bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason,
212                                                      int sampleCount) const {
213    // TODO: the heuristic used here needs to be refined
214    static const int kNumSlowPathsTol = 6;
215
216    bool ret = fNumSlowPathsAndDashEffects < kNumSlowPathsTol;
217
218    if (!ret && reason) {
219        *reason = "Too many slow paths (either concave or dashed).";
220    }
221    return ret;
222}
223
224///////////////////////////////////////////////////////////////////////////////
225
226int SkPicture::drawableCount() const {
227    return fDrawablePicts.get() ? fDrawablePicts->count() : 0;
228}
229
230SkPicture const* const* SkPicture::drawablePicts() const {
231    return fDrawablePicts.get() ? fDrawablePicts->begin() : NULL;
232}
233
234SkPicture::~SkPicture() {
235    // If the ID is still zero, no one has read it, so no need to send this message.
236    uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed);
237    if (id != 0) {
238        SkPicture::DeletionMessage msg;
239        msg.fUniqueID = id;
240        SkMessageBus<SkPicture::DeletionMessage>::Post(msg);
241    }
242}
243
244const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData(
245        SkPicture::AccelData::Key key) const {
246    if (fAccelData.get() && fAccelData->getKey() == key) {
247        return fAccelData.get();
248    }
249    return NULL;
250}
251
252SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
253    static int32_t gNextID = 0;
254
255    int32_t id = sk_atomic_inc(&gNextID);
256    if (id >= 1 << (8 * sizeof(Domain))) {
257        SK_CRASH();
258    }
259
260    return static_cast<Domain>(id);
261}
262
263///////////////////////////////////////////////////////////////////////////////
264
265void SkPicture::playback(SkCanvas* canvas, AbortCallback* callback) const {
266    SkASSERT(canvas);
267
268    // If the query contains the whole picture, don't bother with the BBH.
269    SkRect clipBounds = { 0, 0, 0, 0 };
270    (void)canvas->getClipBounds(&clipBounds);
271    const bool useBBH = !clipBounds.contains(this->cullRect());
272
273    SkRecordDraw(*fRecord, canvas, this->drawablePicts(), NULL, this->drawableCount(),
274                 useBBH ? fBBH.get() : NULL, callback);
275}
276
277///////////////////////////////////////////////////////////////////////////////
278
279#include "SkStream.h"
280
281static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
282
283bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
284    if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
285        return false;
286    }
287
288    if (info.fVersion < MIN_PICTURE_VERSION ||
289        info.fVersion > CURRENT_PICTURE_VERSION) {
290        return false;
291    }
292
293    return true;
294}
295
296bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
297    if (NULL == stream) {
298        return false;
299    }
300
301    // Check magic bytes.
302    SkPictInfo info;
303    SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
304
305    if (!stream->read(&info.fMagic, sizeof(kMagic))) {
306        return false;
307    }
308
309    info.fVersion = stream->readU32();
310    info.fCullRect.fLeft = stream->readScalar();
311    info.fCullRect.fTop = stream->readScalar();
312    info.fCullRect.fRight = stream->readScalar();
313    info.fCullRect.fBottom = stream->readScalar();
314
315    info.fFlags = stream->readU32();
316
317    if (!IsValidPictInfo(info)) {
318        return false;
319    }
320
321    if (pInfo != NULL) {
322        *pInfo = info;
323    }
324    return true;
325}
326
327bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
328    // Check magic bytes.
329    SkPictInfo info;
330    SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
331
332    if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) {
333        return false;
334    }
335
336    info.fVersion = buffer->readUInt();
337    buffer->readRect(&info.fCullRect);
338    info.fFlags = buffer->readUInt();
339
340    if (!IsValidPictInfo(info)) {
341        return false;
342    }
343
344    if (pInfo != NULL) {
345        *pInfo = info;
346    }
347    return true;
348}
349
350SkPicture* SkPicture::Forwardport(const SkPictInfo& info, const SkPictureData* data) {
351    if (!data) {
352        return NULL;
353    }
354    SkPicturePlayback playback(data);
355    SkPictureRecorder r;
356    playback.draw(r.beginRecording(SkScalarCeilToInt(info.fCullRect.width()),
357                                   SkScalarCeilToInt(info.fCullRect.height())),
358                  NULL/*no callback*/);
359    return r.endRecording();
360}
361
362SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
363    SkPictInfo info;
364    if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) {
365        return NULL;
366    }
367    SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromStream(stream, info, proc));
368    return Forwardport(info, data);
369}
370
371SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
372    SkPictInfo info;
373    if (!InternalOnly_BufferIsSKP(&buffer, &info) || !buffer.readBool()) {
374        return NULL;
375    }
376    SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromBuffer(buffer, info));
377    return Forwardport(info, data);
378}
379
380void SkPicture::createHeader(SkPictInfo* info) const {
381    // Copy magic bytes at the beginning of the header
382    SkASSERT(sizeof(kMagic) == 8);
383    SkASSERT(sizeof(kMagic) == sizeof(info->fMagic));
384    memcpy(info->fMagic, kMagic, sizeof(kMagic));
385
386    // Set picture info after magic bytes in the header
387    info->fVersion = CURRENT_PICTURE_VERSION;
388    info->fCullRect = this->cullRect();
389    info->fFlags = SkPictInfo::kCrossProcess_Flag;
390    // TODO: remove this flag, since we're always float (now)
391    info->fFlags |= SkPictInfo::kScalarIsFloat_Flag;
392
393    if (8 == sizeof(void*)) {
394        info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
395    }
396}
397
398// This for compatibility with serialization code only.  This is not cheap.
399SkPictureData* SkPicture::Backport(const SkRecord& src, const SkPictInfo& info,
400                                   SkPicture const* const drawablePicts[], int drawableCount) {
401    SkPictureRecord rec(SkISize::Make(info.fCullRect.width(), info.fCullRect.height()), 0/*flags*/);
402    rec.beginRecording();
403        SkRecordDraw(src, &rec, drawablePicts, NULL, drawableCount, NULL/*bbh*/, NULL/*callback*/);
404    rec.endRecording();
405    return SkNEW_ARGS(SkPictureData, (rec, info, false/*deep copy ops?*/));
406}
407
408void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) const {
409    SkPictInfo info;
410    this->createHeader(&info);
411    SkAutoTDelete<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(),
412                                               this->drawableCount()));
413
414    stream->write(&info, sizeof(info));
415    if (data) {
416        stream->writeBool(true);
417        data->serialize(stream, pixelSerializer);
418    } else {
419        stream->writeBool(false);
420    }
421}
422
423void SkPicture::flatten(SkWriteBuffer& buffer) const {
424    SkPictInfo info;
425    this->createHeader(&info);
426    SkAutoTDelete<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(),
427                                               this->drawableCount()));
428
429    buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic));
430    buffer.writeUInt(info.fVersion);
431    buffer.writeRect(info.fCullRect);
432    buffer.writeUInt(info.fFlags);
433    if (data) {
434        buffer.writeBool(true);
435        data->flatten(buffer);
436    } else {
437        buffer.writeBool(false);
438    }
439}
440
441const SkPicture::Analysis& SkPicture::analysis() const {
442    auto create = [&](){ return SkNEW_ARGS(Analysis, (*fRecord)); };
443    return *fAnalysis.get(create);
444}
445
446#if SK_SUPPORT_GPU
447bool SkPicture::suitableForGpuRasterization(GrContext*, const char **reason) const {
448    return this->analysis().suitableForGpuRasterization(reason, 0);
449}
450#endif
451
452bool SkPicture::hasText()             const { return this->analysis().fHasText; }
453bool SkPicture::willPlayBackBitmaps() const { return this->analysis().fWillPlaybackBitmaps; }
454int  SkPicture::approximateOpCount()  const { return fRecord->count(); }
455
456SkPicture::SkPicture(const SkRect& cullRect,
457                     SkRecord* record,
458                     SnapshotArray* drawablePicts,
459                     SkBBoxHierarchy* bbh,
460                     AccelData* accelData,
461                     size_t approxBytesUsedBySubPictures)
462    : fUniqueID(0)
463    , fCullRect(cullRect)
464    , fRecord(record)               // Take ownership of caller's ref.
465    , fDrawablePicts(drawablePicts) // Take ownership.
466    , fBBH(bbh)                     // Take ownership of caller's ref.
467    , fAccelData(accelData)         // Take ownership of caller's ref.
468    , fApproxBytesUsedBySubPictures(approxBytesUsedBySubPictures)
469{}
470
471
472static uint32_t gNextID = 1;
473uint32_t SkPicture::uniqueID() const {
474    uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed);
475    while (id == 0) {
476        uint32_t next = sk_atomic_fetch_add(&gNextID, 1u);
477        if (sk_atomic_compare_exchange(&fUniqueID, &id, next,
478                                       sk_memory_order_relaxed,
479                                       sk_memory_order_relaxed)) {
480            id = next;
481        } else {
482            // sk_atomic_compare_exchange replaced id with the current value of fUniqueID.
483        }
484    }
485    return id;
486}
487