3 * Copyright 2007 The Android Open Source Project
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
10 #include "SkPictureFlat.h"
11 #include "SkPictureData.h"
12 #include "SkPicturePlayback.h"
13 #include "SkPictureRecord.h"
14 #include "SkPictureRecorder.h"
16 #include "SkBitmapDevice.h"
18 #include "SkChunkAlloc.h"
19 #include "SkDrawPictureCallback.h"
20 #include "SkPaintPriv.h"
21 #include "SkPathEffect.h"
22 #include "SkPicture.h"
26 #include "SkTDArray.h"
28 #include "SkTSearch.h"
31 #include "SkReader32.h"
32 #include "SkWriter32.h"
36 #include "GrContext.h"
40 #include "SkRecordDraw.h"
41 #include "SkRecordOpts.h"
42 #include "SkRecorder.h"
44 template <typename T> int SafeCount(const T* obj) {
45 return obj ? obj->count() : 0;
48 ///////////////////////////////////////////////////////////////////////////////
52 // Some commands have a paint, some have an optional paint. Either way, get back a pointer.
53 static const SkPaint* AsPtr(const SkPaint& p) { return &p; }
54 static const SkPaint* AsPtr(const SkRecords::Optional<SkPaint>& p) { return p; }
56 /** SkRecords visitor to determine whether an instance may require an
57 "external" bitmap to rasterize. May return false positives.
58 Does not return true for bitmap text.
60 Expected use is to determine whether images need to be decoded before
61 rasterizing a particular SkRecord.
64 // Helpers. These create HasMember_bitmap and HasMember_paint.
65 SK_CREATE_MEMBER_DETECTOR(bitmap);
66 SK_CREATE_MEMBER_DETECTOR(paint);
69 // Main entry for visitor:
70 // If the command is a DrawPicture, recurse.
71 // If the command has a bitmap directly, return true.
72 // If the command has a paint and the paint has a bitmap, return true.
73 // Otherwise, return false.
74 bool operator()(const SkRecords::DrawPicture& op) { return op.picture->willPlayBackBitmaps(); }
77 bool operator()(const T& r) { return CheckBitmap(r); }
80 // If the command has a bitmap, of course we're going to play back bitmaps.
82 static SK_WHEN(HasMember_bitmap<T>, bool) CheckBitmap(const T&) { return true; }
84 // If not, look for one in its paint (if it has a paint).
86 static SK_WHEN(!HasMember_bitmap<T>, bool) CheckBitmap(const T& r) { return CheckPaint(r); }
88 // If we have a paint, dig down into the effects looking for a bitmap.
90 static SK_WHEN(HasMember_paint<T>, bool) CheckPaint(const T& r) {
91 const SkPaint* paint = AsPtr(r.paint);
93 const SkShader* shader = paint->getShader();
95 shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
102 // If we don't have a paint, that non-paint has no bitmap.
103 template <typename T>
104 static SK_WHEN(!HasMember_paint<T>, bool) CheckPaint(const T&) { return false; }
107 bool WillPlaybackBitmaps(const SkRecord& record) {
109 for (unsigned i = 0; i < record.count(); i++) {
110 if (record.visit<bool>(i, tester)) {
117 // SkRecord visitor to find recorded text.
119 // All ops with text have that text as a char array member named "text".
120 SK_CREATE_MEMBER_DETECTOR(text);
121 bool operator()(const SkRecords::DrawPicture& op) { return op.picture->hasText(); }
122 template <typename T> SK_WHEN(HasMember_text<T>, bool) operator()(const T&) { return true; }
123 template <typename T> SK_WHEN(!HasMember_text<T>, bool) operator()(const T&) { return false; }
128 /** SkRecords visitor to determine heuristically whether or not a SkPicture
129 will be performant when rasterized on the GPU.
131 struct SkPicture::PathCounter {
132 SK_CREATE_MEMBER_DETECTOR(paint);
135 : numPaintWithPathEffectUses (0)
136 , numFastPathDashEffects (0)
137 , numAAConcavePaths (0)
138 , numAAHairlineConcavePaths (0)
139 , numAADFEligibleConcavePaths(0) {
142 // Recurse into nested pictures.
143 void operator()(const SkRecords::DrawPicture& op) {
144 const SkPicture::Analysis& analysis = op.picture->fAnalysis;
145 numPaintWithPathEffectUses += analysis.fNumPaintWithPathEffectUses;
146 numFastPathDashEffects += analysis.fNumFastPathDashEffects;
147 numAAConcavePaths += analysis.fNumAAConcavePaths;
148 numAAHairlineConcavePaths += analysis.fNumAAHairlineConcavePaths;
149 numAADFEligibleConcavePaths += analysis.fNumAADFEligibleConcavePaths;
152 void checkPaint(const SkPaint* paint) {
153 if (paint && paint->getPathEffect()) {
154 numPaintWithPathEffectUses++;
158 void operator()(const SkRecords::DrawPoints& op) {
159 this->checkPaint(&op.paint);
160 const SkPathEffect* effect = op.paint.getPathEffect();
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++;
171 void operator()(const SkRecords::DrawPath& op) {
172 this->checkPaint(&op.paint);
173 if (op.paint.isAntiAlias() && !op.path.isConvex()) {
176 SkPaint::Style paintStyle = op.paint.getStyle();
177 const SkRect& pathBounds = op.path.getBounds();
178 if (SkPaint::kStroke_Style == paintStyle &&
179 0 == op.paint.getStrokeWidth()) {
180 numAAHairlineConcavePaths++;
181 } else if (SkPaint::kFill_Style == paintStyle && pathBounds.width() < 64.f &&
182 pathBounds.height() < 64.f && !op.path.isVolatile()) {
183 numAADFEligibleConcavePaths++;
188 template <typename T>
189 SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
190 this->checkPaint(AsPtr(op.paint));
193 template <typename T>
194 SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ }
196 int numPaintWithPathEffectUses;
197 int numFastPathDashEffects;
198 int numAAConcavePaths;
199 int numAAHairlineConcavePaths;
200 int numAADFEligibleConcavePaths;
203 SkPicture::Analysis::Analysis(const SkRecord& record) {
204 fWillPlaybackBitmaps = WillPlaybackBitmaps(record);
207 for (unsigned i = 0; i < record.count(); i++) {
208 record.visit<void>(i, counter);
210 fNumPaintWithPathEffectUses = counter.numPaintWithPathEffectUses;
211 fNumFastPathDashEffects = counter.numFastPathDashEffects;
212 fNumAAConcavePaths = counter.numAAConcavePaths;
213 fNumAAHairlineConcavePaths = counter.numAAHairlineConcavePaths;
214 fNumAADFEligibleConcavePaths = counter.numAADFEligibleConcavePaths;
218 for (unsigned i = 0; i < record.count(); i++) {
219 if (record.visit<bool>(i, text)) {
226 bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason,
227 int sampleCount) const {
228 // TODO: the heuristic used here needs to be refined
229 static const int kNumPaintWithPathEffectsUsesTol = 1;
230 static const int kNumAAConcavePathsTol = 5;
232 int numNonDashedPathEffects = fNumPaintWithPathEffectUses -
233 fNumFastPathDashEffects;
234 bool suitableForDash = (0 == fNumPaintWithPathEffectUses) ||
235 (numNonDashedPathEffects < kNumPaintWithPathEffectsUsesTol
236 && 0 == sampleCount);
238 bool ret = suitableForDash &&
239 (fNumAAConcavePaths - fNumAAHairlineConcavePaths - fNumAADFEligibleConcavePaths)
240 < kNumAAConcavePathsTol;
242 if (!ret && reason) {
243 if (!suitableForDash) {
244 if (0 != sampleCount) {
245 *reason = "Can't use multisample on dash effect.";
247 *reason = "Too many non dashed path effects.";
249 } else if ((fNumAAConcavePaths - fNumAAHairlineConcavePaths - fNumAADFEligibleConcavePaths)
250 >= kNumAAConcavePathsTol)
251 *reason = "Too many anti-aliased concave paths.";
253 *reason = "Unknown reason for GPU unsuitability.";
258 ///////////////////////////////////////////////////////////////////////////////
261 SkPicture::SkPicture(SkScalar width, SkScalar height,
262 const SkPictureRecord& record,
265 , fCullHeight(height)
267 this->needsNewGenID();
270 this->createHeader(&info);
271 fData.reset(SkNEW_ARGS(SkPictureData, (record, info, deepCopyOps)));
274 // Create an SkPictureData-backed SkPicture from an SkRecord.
275 // This for compatibility with serialization code only. This is not cheap.
276 SkPicture* SkPicture::Backport(const SkRecord& src, const SkRect& cullRect) {
277 SkPictureRecord rec(SkISize::Make(cullRect.width(), cullRect.height()), 0/*flags*/);
278 rec.beginRecording();
279 SkRecordDraw(src, &rec, NULL/*bbh*/, NULL/*callback*/);
281 return SkNEW_ARGS(SkPicture, (cullRect.width(), cullRect.height(), rec, false/*deepCopyOps*/));
285 SkPicture::~SkPicture() {
286 this->callDeletionListeners();
290 void SkPicture::EXPERIMENTAL_addAccelData(const SkPicture::AccelData* data) const {
291 fAccelData.reset(SkRef(data));
295 const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData(
296 SkPicture::AccelData::Key key) const {
297 if (fAccelData.get() && fAccelData->getKey() == key) {
298 return fAccelData.get();
304 SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
305 static int32_t gNextID = 0;
307 int32_t id = sk_atomic_inc(&gNextID);
308 if (id >= 1 << (8 * sizeof(Domain))) {
312 return static_cast<Domain>(id);
315 ///////////////////////////////////////////////////////////////////////////////
318 void SkPicture::playback(SkCanvas* canvas, SkDrawPictureCallback* callback) const {
320 SkASSERT(fData.get() || fRecord.get());
323 SkPicturePlayback playback(this);
324 playback.draw(canvas, callback);
327 // If the query contains the whole picture, don't bother with the BBH.
328 SkRect clipBounds = { 0, 0, 0, 0 };
329 (void)canvas->getClipBounds(&clipBounds);
330 const bool useBBH = !clipBounds.contains(this->cullRect());
332 SkRecordDraw(*fRecord, canvas, useBBH ? fBBH.get() : NULL, callback);
336 ///////////////////////////////////////////////////////////////////////////////
338 #include "SkStream.h"
340 static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
343 bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
344 if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
348 if (info.fVersion < MIN_PICTURE_VERSION ||
349 info.fVersion > CURRENT_PICTURE_VERSION) {
357 bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
358 if (NULL == stream) {
362 // Check magic bytes.
364 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
366 if (!stream->read(&info.fMagic, sizeof(kMagic))) {
370 info.fVersion = stream->readU32();
372 #ifndef V35_COMPATIBILITY_CODE
373 if (info.fVersion < 35) {
374 info.fCullRect.fLeft = 0;
375 info.fCullRect.fTop = 0;
376 info.fCullRect.fRight = SkIntToScalar(stream->readU32());
377 info.fCullRect.fBottom = SkIntToScalar(stream->readU32());
380 info.fCullRect.fLeft = stream->readScalar();
381 info.fCullRect.fTop = stream->readScalar();
382 info.fCullRect.fRight = stream->readScalar();
383 info.fCullRect.fBottom = stream->readScalar();
384 #ifndef V35_COMPATIBILITY_CODE
388 info.fFlags = stream->readU32();
390 if (!IsValidPictInfo(info)) {
401 bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
402 // Check magic bytes.
404 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
406 if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) {
410 info.fVersion = buffer->readUInt();
412 #ifndef V35_COMPATIBILITY_CODE
413 if (info.fVersion < 35) {
414 info.fCullRect.fLeft = 0;
415 info.fCullRect.fTop = 0;
416 info.fCullRect.fRight = SkIntToScalar(buffer->readUInt());
417 info.fCullRect.fBottom = SkIntToScalar(buffer->readUInt());
420 buffer->readRect(&info.fCullRect);
421 #ifndef V35_COMPATIBILITY_CODE
425 info.fFlags = buffer->readUInt();
427 if (!IsValidPictInfo(info)) {
438 SkPicture::SkPicture(SkPictureData* data, SkScalar width, SkScalar height)
441 , fCullHeight(height)
443 this->needsNewGenID();
446 SkPicture* SkPicture::Forwardport(const SkPicture& src) {
447 SkAutoTDelete<SkRecord> record(SkNEW(SkRecord));
448 SkRecorder canvas(record.get(), src.cullRect().width(), src.cullRect().height());
449 src.playback(&canvas);
450 return SkNEW_ARGS(SkPicture, (src.cullRect().width(), src.cullRect().height(),
451 record.detach(), NULL/*bbh*/));
455 SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
458 if (!InternalOnly_StreamIsSKP(stream, &info)) {
462 // Check to see if there is a playback to recreate.
463 if (stream->readBool()) {
464 SkPictureData* data = SkPictureData::CreateFromStream(stream, info, proc);
468 const SkPicture src(data, info.fCullRect.width(), info.fCullRect.height());
469 return Forwardport(src);
476 SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
479 if (!InternalOnly_BufferIsSKP(&buffer, &info)) {
483 // Check to see if there is a playback to recreate.
484 if (buffer.readBool()) {
485 SkPictureData* data = SkPictureData::CreateFromBuffer(buffer, info);
489 const SkPicture src(data, info.fCullRect.width(), info.fCullRect.height());
490 return Forwardport(src);
497 void SkPicture::createHeader(SkPictInfo* info) const {
498 // Copy magic bytes at the beginning of the header
499 SkASSERT(sizeof(kMagic) == 8);
500 SkASSERT(sizeof(kMagic) == sizeof(info->fMagic));
501 memcpy(info->fMagic, kMagic, sizeof(kMagic));
503 // Set picture info after magic bytes in the header
504 info->fVersion = CURRENT_PICTURE_VERSION;
505 info->fCullRect = this->cullRect();
506 info->fFlags = SkPictInfo::kCrossProcess_Flag;
507 // TODO: remove this flag, since we're always float (now)
508 info->fFlags |= SkPictInfo::kScalarIsFloat_Flag;
510 if (8 == sizeof(void*)) {
511 info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
516 void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
517 const SkPictureData* data = fData.get();
519 // If we're a new-format picture, backport to old format for serialization.
520 SkAutoTDelete<SkPicture> oldFormat;
521 if (NULL == data && fRecord.get()) {
522 oldFormat.reset(Backport(*fRecord, this->cullRect()));
523 data = oldFormat->fData.get();
528 this->createHeader(&info);
529 SkASSERT(sizeof(SkPictInfo) == 32);
530 stream->write(&info, sizeof(info));
533 stream->writeBool(true);
534 data->serialize(stream, encoder);
536 stream->writeBool(false);
541 void SkPicture::flatten(SkWriteBuffer& buffer) const {
542 const SkPictureData* data = fData.get();
544 // If we're a new-format picture, backport to old format for serialization.
545 SkAutoTDelete<SkPicture> oldFormat;
546 if (NULL == data && fRecord.get()) {
547 oldFormat.reset(Backport(*fRecord, this->cullRect()));
548 data = oldFormat->fData.get();
553 this->createHeader(&info);
554 buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic));
555 buffer.writeUInt(info.fVersion);
556 buffer.writeRect(info.fCullRect);
557 buffer.writeUInt(info.fFlags);
560 buffer.writeBool(true);
561 data->flatten(buffer);
563 buffer.writeBool(false);
569 bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **reason) const {
571 return fAnalysis.suitableForGpuRasterization(reason, 0);
573 if (NULL == fData.get()) {
575 *reason = "Missing internal data.";
580 return fData->suitableForGpuRasterization(context, reason);
585 bool SkPicture::hasText() const {
587 return fAnalysis.fHasText;
590 return fData->hasText();
592 SkFAIL("Unreachable");
597 bool SkPicture::willPlayBackBitmaps() const {
599 return fAnalysis.fWillPlaybackBitmaps;
602 return fData->containsBitmaps();
604 SkFAIL("Unreachable");
609 static int32_t next_picture_generation_id() {
610 static int32_t gPictureGenerationID = 0;
611 // do a loop in case our global wraps around, as we never want to
615 genID = sk_atomic_inc(&gPictureGenerationID) + 1;
616 } while (SK_InvalidGenID == genID);
621 uint32_t SkPicture::uniqueID() const {
622 if (SK_InvalidGenID == fUniqueID) {
623 fUniqueID = next_picture_generation_id();
629 static SkRecord* optimized(SkRecord* r) {
635 SkPicture::SkPicture(SkScalar width, SkScalar height, SkRecord* record, SkBBoxHierarchy* bbh)
637 , fCullHeight(height)
638 , fRecord(optimized(record))
639 , fBBH(SkSafeRef(bbh))
640 , fAnalysis(*fRecord) {
641 // TODO: delay as much of this work until just before first playback?
643 SkRecordFillBounds(this->cullRect(), *fRecord, fBBH.get());
645 this->needsNewGenID();
648 // Note that we are assuming that this entry point will only be called from
649 // one thread. Currently the only client of this method is
650 // SkGpuDevice::EXPERIMENTAL_optimize which should be only called from a single
652 void SkPicture::addDeletionListener(DeletionListener* listener) const {
655 *fDeletionListeners.append() = SkRef(listener);
658 void SkPicture::callDeletionListeners() {
659 for (int i = 0; i < fDeletionListeners.count(); ++i) {
660 fDeletionListeners[i]->onDeletion(this->uniqueID());
663 fDeletionListeners.unrefAll();
667 int SkPicture::approximateOpCount() const {
668 SkASSERT(fRecord.get() || fData.get());
670 return fRecord->count();
673 return fData->opCount();