blob: 6239763c3ddef17d32e245f8f58a03029f676bd3 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * 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
scroggo@google.com2983ddd2013-05-07 14:45:40 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkPictureFlat.h"
robertphillipsdb539902014-07-01 08:47:04 -070011#include "SkPictureData.h"
robertphillipsce4dd3d2014-07-07 13:46:35 -070012#include "SkPicturePlayback.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkPictureRecord.h"
mtklein73734562014-06-24 12:28:34 -070014#include "SkPictureRecorder.h"
robertphillips3afef1f2014-07-08 06:12:22 -070015#include "SkPictureStateTree.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000017#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkCanvas.h"
19#include "SkChunkAlloc.h"
Mike Kleinc11530e2014-06-24 11:29:06 -040020#include "SkDrawPictureCallback.h"
commit-bot@chromium.org0205aba2014-05-06 12:02:22 +000021#include "SkPaintPriv.h"
tomhudson3a0f2792014-08-20 05:29:41 -070022#include "SkPathEffect.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkPicture.h"
24#include "SkRegion.h"
tomhudson3a0f2792014-08-20 05:29:41 -070025#include "SkShader.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkStream.h"
27#include "SkTDArray.h"
tomhudson3a0f2792014-08-20 05:29:41 -070028#include "SkTLogic.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029#include "SkTSearch.h"
30#include "SkTime.h"
31
32#include "SkReader32.h"
33#include "SkWriter32.h"
rileya@google.com8515e792012-09-13 21:41:51 +000034#include "SkRTree.h"
35#include "SkBBoxHierarchyRecord.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000036
commit-bot@chromium.orgeb9547c2014-03-19 21:24:25 +000037#if SK_SUPPORT_GPU
38#include "GrContext.h"
39#endif
40
Mike Klein744fb732014-06-23 15:13:26 -040041#include "SkRecord.h"
42#include "SkRecordDraw.h"
Mike Kleinc11530e2014-06-24 11:29:06 -040043#include "SkRecorder.h"
Mike Klein744fb732014-06-23 15:13:26 -040044
commit-bot@chromium.org8f831f22014-04-23 22:35:42 +000045template <typename T> int SafeCount(const T* obj) {
46 return obj ? obj->count() : 0;
47}
48
reed@android.com8a1c16f2008-12-17 15:59:43 +000049///////////////////////////////////////////////////////////////////////////////
50
tomhudson3a0f2792014-08-20 05:29:41 -070051namespace {
52
53// Some commands have a paint, some have an optional paint. Either way, get back a pointer.
54static const SkPaint* AsPtr(const SkPaint& p) { return &p; }
55static const SkPaint* AsPtr(const SkRecords::Optional<SkPaint>& p) { return p; }
56
57/** SkRecords visitor to determine whether an instance may require an
58 "external" bitmap to rasterize. May return false positives.
59 Does not return true for bitmap text.
60
61 Expected use is to determine whether images need to be decoded before
62 rasterizing a particular SkRecord.
63 */
64struct BitmapTester {
65 // Helpers. These create HasMember_bitmap and HasMember_paint.
66 SK_CREATE_MEMBER_DETECTOR(bitmap);
67 SK_CREATE_MEMBER_DETECTOR(paint);
68
69
70 // Main entry for visitor:
mtklein53fecfb2014-08-21 09:11:37 -070071 // If the command is a DrawPicture, recurse.
tomhudson3a0f2792014-08-20 05:29:41 -070072 // If the command has a bitmap directly, return true.
73 // If the command has a paint and the paint has a bitmap, return true.
74 // Otherwise, return false.
mtklein53fecfb2014-08-21 09:11:37 -070075 bool operator()(const SkRecords::DrawPicture& op) { return op.picture->willPlayBackBitmaps(); }
76
tomhudson3a0f2792014-08-20 05:29:41 -070077 template <typename T>
78 bool operator()(const T& r) { return CheckBitmap(r); }
79
80
81 // If the command has a bitmap, of course we're going to play back bitmaps.
82 template <typename T>
83 static SK_WHEN(HasMember_bitmap<T>, bool) CheckBitmap(const T&) { return true; }
84
85 // If not, look for one in its paint (if it has a paint).
86 template <typename T>
87 static SK_WHEN(!HasMember_bitmap<T>, bool) CheckBitmap(const T& r) { return CheckPaint(r); }
88
89 // If we have a paint, dig down into the effects looking for a bitmap.
90 template <typename T>
91 static SK_WHEN(HasMember_paint<T>, bool) CheckPaint(const T& r) {
92 const SkPaint* paint = AsPtr(r.paint);
93 if (paint) {
94 const SkShader* shader = paint->getShader();
95 if (shader &&
96 shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
97 return true;
98 }
99 }
100 return false;
101 }
102
103 // If we don't have a paint, that non-paint has no bitmap.
104 template <typename T>
105 static SK_WHEN(!HasMember_paint<T>, bool) CheckPaint(const T&) { return false; }
106};
107
108bool WillPlaybackBitmaps(const SkRecord& record) {
109 BitmapTester tester;
110 for (unsigned i = 0; i < record.count(); i++) {
111 if (record.visit<bool>(i, tester)) {
112 return true;
113 }
114 }
115 return false;
116}
117
mtklein53fecfb2014-08-21 09:11:37 -0700118// SkRecord visitor to find recorded text.
119struct TextHunter {
120 // All ops with text have that text as a char array member named "text".
121 SK_CREATE_MEMBER_DETECTOR(text);
122 bool operator()(const SkRecords::DrawPicture& op) { return op.picture->hasText(); }
123 template <typename T> SK_WHEN(HasMember_text<T>, bool) operator()(const T&) { return true; }
124 template <typename T> SK_WHEN(!HasMember_text<T>, bool) operator()(const T&) { return false; }
125};
126
127} // namespace
128
tomhudson3a0f2792014-08-20 05:29:41 -0700129/** SkRecords visitor to determine heuristically whether or not a SkPicture
130 will be performant when rasterized on the GPU.
131 */
mtklein53fecfb2014-08-21 09:11:37 -0700132struct SkPicture::PathCounter {
tomhudson3a0f2792014-08-20 05:29:41 -0700133 SK_CREATE_MEMBER_DETECTOR(paint);
134
135 PathCounter()
136 : numPaintWithPathEffectUses (0)
137 , numFastPathDashEffects (0)
138 , numAAConcavePaths (0)
139 , numAAHairlineConcavePaths (0) {
140 }
141
mtklein53fecfb2014-08-21 09:11:37 -0700142 // Recurse into nested pictures.
143 void operator()(const SkRecords::DrawPicture& op) {
144 // If you're not also SkRecord-backed, tough luck. Get on the bandwagon.
145 if (op.picture->fRecord.get() == NULL) {
146 return;
147 }
148 const SkRecord& nested = *op.picture->fRecord;
149 for (unsigned i = 0; i < nested.count(); i++) {
150 nested.visit<void>(i, *this);
151 }
152 }
153
tomhudson3a0f2792014-08-20 05:29:41 -0700154 void checkPaint(const SkPaint* paint) {
155 if (paint && paint->getPathEffect()) {
156 numPaintWithPathEffectUses++;
157 }
158 }
159
160 void operator()(const SkRecords::DrawPoints& op) {
161 this->checkPaint(&op.paint);
162 const SkPathEffect* effect = op.paint.getPathEffect();
163 if (effect) {
164 SkPathEffect::DashInfo info;
165 SkPathEffect::DashType dashType = effect->asADash(&info);
166 if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() &&
167 SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) {
168 numFastPathDashEffects++;
169 }
170 }
171 }
172
173 void operator()(const SkRecords::DrawPath& op) {
174 this->checkPaint(&op.paint);
175 if (op.paint.isAntiAlias() && !op.path.isConvex()) {
176 numAAConcavePaths++;
177
178 if (SkPaint::kStroke_Style == op.paint.getStyle() &&
179 0 == op.paint.getStrokeWidth()) {
180 numAAHairlineConcavePaths++;
181 }
182 }
183 }
184
185 template <typename T>
186 SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
187 this->checkPaint(AsPtr(op.paint));
188 }
189
190 template <typename T>
191 SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ }
192
tomhudson3a0f2792014-08-20 05:29:41 -0700193 int numPaintWithPathEffectUses;
194 int numFastPathDashEffects;
195 int numAAConcavePaths;
196 int numAAHairlineConcavePaths;
197};
198
mtkleinc551d9f2014-08-20 08:09:46 -0700199SkPicture::Analysis::Analysis(const SkRecord& record) {
tomhudson3a0f2792014-08-20 05:29:41 -0700200 fWillPlaybackBitmaps = WillPlaybackBitmaps(record);
201
202 PathCounter counter;
203 for (unsigned i = 0; i < record.count(); i++) {
204 record.visit<void>(i, counter);
205 }
206 fNumPaintWithPathEffectUses = counter.numPaintWithPathEffectUses;
mtkleinc551d9f2014-08-20 08:09:46 -0700207 fNumFastPathDashEffects = counter.numFastPathDashEffects;
208 fNumAAConcavePaths = counter.numAAConcavePaths;
209 fNumAAHairlineConcavePaths = counter.numAAHairlineConcavePaths;
210
211 fHasText = false;
212 TextHunter text;
213 for (unsigned i = 0; i < record.count(); i++) {
214 if (record.visit<bool>(i, text)) {
215 fHasText = true;
216 break;
217 }
218 }
tomhudson3a0f2792014-08-20 05:29:41 -0700219}
220
221bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason,
222 int sampleCount) const {
223 // TODO: the heuristic used here needs to be refined
224 static const int kNumPaintWithPathEffectsUsesTol = 1;
225 static const int kNumAAConcavePathsTol = 5;
226
227 int numNonDashedPathEffects = fNumPaintWithPathEffectUses -
228 fNumFastPathDashEffects;
229 bool suitableForDash = (0 == fNumPaintWithPathEffectUses) ||
230 (numNonDashedPathEffects < kNumPaintWithPathEffectsUsesTol
231 && 0 == sampleCount);
232
233 bool ret = suitableForDash &&
234 (fNumAAConcavePaths - fNumAAHairlineConcavePaths)
235 < kNumAAConcavePathsTol;
236
237 if (!ret && NULL != reason) {
238 if (!suitableForDash) {
239 if (0 != sampleCount) {
240 *reason = "Can't use multisample on dash effect.";
241 } else {
242 *reason = "Too many non dashed path effects.";
243 }
244 } else if ((fNumAAConcavePaths - fNumAAHairlineConcavePaths)
245 >= kNumAAConcavePathsTol)
246 *reason = "Too many anti-aliased concave paths.";
247 else
248 *reason = "Unknown reason for GPU unsuitability.";
249 }
250 return ret;
251}
252
253///////////////////////////////////////////////////////////////////////////////
254
Robert Phillipscfaeec42014-07-13 12:00:50 -0400255#ifdef SK_SUPPORT_LEGACY_DEFAULT_PICTURE_CTOR
Mike Klein744fb732014-06-23 15:13:26 -0400256// fRecord OK
commit-bot@chromium.orge2cb12a2014-04-24 21:53:13 +0000257SkPicture::SkPicture()
Mike Klein744fb732014-06-23 15:13:26 -0400258 : fWidth(0)
tomhudson3a0f2792014-08-20 05:29:41 -0700259 , fHeight(0) {
robertphillips@google.comd5500882014-04-02 23:51:13 +0000260 this->needsNewGenID();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261}
Robert Phillipscfaeec42014-07-13 12:00:50 -0400262#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263
Mike Klein744fb732014-06-23 15:13:26 -0400264// fRecord OK
robertphillips0bdbea72014-06-11 11:37:55 -0700265SkPicture::SkPicture(int width, int height,
robertphillipse26e65e2014-06-12 05:51:22 -0700266 const SkPictureRecord& record,
robertphillips0bdbea72014-06-11 11:37:55 -0700267 bool deepCopyOps)
268 : fWidth(width)
Mike Klein27dc17c2014-08-18 18:35:16 -0400269 , fHeight(height)
tomhudson3a0f2792014-08-20 05:29:41 -0700270 , fAnalysis() {
robertphillips0bdbea72014-06-11 11:37:55 -0700271 this->needsNewGenID();
robertphillips9058d602014-06-10 11:45:46 -0700272
robertphillips0bdbea72014-06-11 11:37:55 -0700273 SkPictInfo info;
274 this->createHeader(&info);
robertphillipsdb539902014-07-01 08:47:04 -0700275 fData.reset(SkNEW_ARGS(SkPictureData, (record, info, deepCopyOps)));
commit-bot@chromium.org2ee3c2c2014-05-19 12:36:15 +0000276}
277
robertphillipsdb539902014-07-01 08:47:04 -0700278// Create an SkPictureData-backed SkPicture from an SkRecord.
mtklein73734562014-06-24 12:28:34 -0700279// This for compatibility with serialization code only. This is not cheap.
280static SkPicture* backport(const SkRecord& src, int width, int height) {
281 SkPictureRecorder recorder;
mtkleinc92e5502014-08-21 13:07:27 -0700282 SkRecordDraw(src,
283 recorder.DEPRECATED_beginRecording(width, height), NULL/*bbh*/, NULL/*callback*/);
mtklein73734562014-06-24 12:28:34 -0700284 return recorder.endRecording();
285}
286
Mike Kleinc11530e2014-06-24 11:29:06 -0400287// fRecord OK
robertphillipsd771f6b2014-07-22 10:18:06 -0700288SkPicture::~SkPicture() {
289 this->callDeletionListeners();
290}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291
mtklein7b705bb2014-08-20 14:22:58 -0700292// fRecord OK
mtkleind3e474e2014-06-27 12:34:44 -0700293#ifdef SK_SUPPORT_LEGACY_PICTURE_CLONE
djsollen@google.comc9ab9872012-08-29 18:52:07 +0000294SkPicture* SkPicture::clone() const {
mtklein3ba15ae2014-08-06 11:57:00 -0700295 return SkRef(const_cast<SkPicture*>(this));
djsollen@google.comc9ab9872012-08-29 18:52:07 +0000296}
mtkleind3e474e2014-06-27 12:34:44 -0700297#endif//SK_SUPPORT_LEGACY_PICTURE_CLONE
djsollen@google.comc9ab9872012-08-29 18:52:07 +0000298
Mike Klein744fb732014-06-23 15:13:26 -0400299// fRecord OK
300void SkPicture::EXPERIMENTAL_addAccelData(const SkPicture::AccelData* data) const {
301 fAccelData.reset(SkRef(data));
302}
303
304// fRecord OK
305const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData(
306 SkPicture::AccelData::Key key) const {
307 if (NULL != fAccelData.get() && fAccelData->getKey() == key) {
308 return fAccelData.get();
309 }
310 return NULL;
311}
312
313// fRecord OK
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000314SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
315 static int32_t gNextID = 0;
316
317 int32_t id = sk_atomic_inc(&gNextID);
318 if (id >= 1 << (8 * sizeof(Domain))) {
319 SK_CRASH();
320 }
321
322 return static_cast<Domain>(id);
323}
324
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325///////////////////////////////////////////////////////////////////////////////
326
robertphillips3afef1f2014-07-08 06:12:22 -0700327uint32_t SkPicture::OperationList::offset(int index) const {
328 SkASSERT(index < fOps.count());
329 return ((SkPictureStateTree::Draw*)fOps[index])->fOffset;
330}
331
332const SkMatrix& SkPicture::OperationList::matrix(int index) const {
333 SkASSERT(index < fOps.count());
334 return *((SkPictureStateTree::Draw*)fOps[index])->fMatrix;
335}
336
mtklein7b705bb2014-08-20 14:22:58 -0700337// fRecord TODO(robert) / kind of OK in a non-optimal sense
robertphillipsce4dd3d2014-07-07 13:46:35 -0700338const SkPicture::OperationList* SkPicture::EXPERIMENTAL_getActiveOps(const SkIRect& queryRect) const {
robertphillipsdb539902014-07-01 08:47:04 -0700339 SkASSERT(NULL != fData.get());
340 if (NULL != fData.get()) {
341 return fData->getActiveOps(queryRect);
commit-bot@chromium.org70512af2014-03-18 17:45:32 +0000342 }
robertphillipsce4dd3d2014-07-07 13:46:35 -0700343 return NULL;
commit-bot@chromium.org75cf29b2014-03-24 19:40:49 +0000344}
345
Mike Klein744fb732014-06-23 15:13:26 -0400346// fRecord OK
347void SkPicture::draw(SkCanvas* canvas, SkDrawPictureCallback* callback) const {
348 SkASSERT(NULL != canvas);
robertphillipsdb539902014-07-01 08:47:04 -0700349 SkASSERT(NULL != fData.get() || NULL != fRecord.get());
Mike Klein744fb732014-06-23 15:13:26 -0400350
mtklein3e8232b2014-08-18 13:39:11 -0700351 // If the query contains the whole picture, don't bother with the BBH.
352 SkRect clipBounds = { 0, 0, 0, 0 };
353 (void)canvas->getClipBounds(&clipBounds);
354 const bool useBBH = !clipBounds.contains(SkRect::MakeWH(this->width(), this->height()));
355
robertphillipsdb539902014-07-01 08:47:04 -0700356 if (NULL != fData.get()) {
robertphillipsce4dd3d2014-07-07 13:46:35 -0700357 SkPicturePlayback playback(this);
mtklein3e8232b2014-08-18 13:39:11 -0700358 playback.setUseBBH(useBBH);
robertphillipsce4dd3d2014-07-07 13:46:35 -0700359 playback.draw(canvas, callback);
Mike Klein744fb732014-06-23 15:13:26 -0400360 }
361 if (NULL != fRecord.get()) {
mtklein3e8232b2014-08-18 13:39:11 -0700362 SkRecordDraw(*fRecord, canvas, useBBH ? fBBH.get() : NULL, callback);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363 }
364}
365
366///////////////////////////////////////////////////////////////////////////////
367
368#include "SkStream.h"
369
rmistry@google.comd6bab022013-12-02 13:50:38 +0000370static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000371
Mike Klein744fb732014-06-23 15:13:26 -0400372// fRecord OK
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000373bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
374 if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
375 return false;
376 }
377
378 if (info.fVersion < MIN_PICTURE_VERSION ||
379 info.fVersion > CURRENT_PICTURE_VERSION) {
380 return false;
381 }
382
383 return true;
384}
rmistry@google.comd6bab022013-12-02 13:50:38 +0000385
Mike Klein744fb732014-06-23 15:13:26 -0400386// fRecord OK
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000387bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000388 if (NULL == stream) {
389 return false;
borenet@google.com66bcbd12012-09-17 18:26:06 +0000390 }
reed@google.com67562092012-06-22 15:38:39 +0000391
rmistry@google.comd6bab022013-12-02 13:50:38 +0000392 // Check magic bytes.
reed@google.com67562092012-06-22 15:38:39 +0000393 SkPictInfo info;
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000394 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
395 if (!stream->read(&info, sizeof(info)) || !IsValidPictInfo(info)) {
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000396 return false;
reed@google.com67562092012-06-22 15:38:39 +0000397 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000399 if (pInfo != NULL) {
400 *pInfo = info;
401 }
402 return true;
403}
404
Mike Klein744fb732014-06-23 15:13:26 -0400405// fRecord OK
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000406bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer& buffer, SkPictInfo* pInfo) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000407 // Check magic bytes.
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000408 SkPictInfo info;
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000409 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
410 if (!buffer.readByteArray(&info, sizeof(info)) || !IsValidPictInfo(info)) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000411 return false;
412 }
413
414 if (pInfo != NULL) {
415 *pInfo = info;
416 }
417 return true;
418}
419
Mike Klein744fb732014-06-23 15:13:26 -0400420// fRecord OK
robertphillipsdb539902014-07-01 08:47:04 -0700421SkPicture::SkPicture(SkPictureData* data, int width, int height)
422 : fData(data)
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000423 , fWidth(width)
Mike Klein27dc17c2014-08-18 18:35:16 -0400424 , fHeight(height)
tomhudson3a0f2792014-08-20 05:29:41 -0700425 , fAnalysis() {
robertphillips@google.comd5500882014-04-02 23:51:13 +0000426 this->needsNewGenID();
427}
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000428
mtklein7b705bb2014-08-20 14:22:58 -0700429SkPicture* SkPicture::Forwardport(const SkPicture& src) {
430 SkAutoTDelete<SkRecord> record(SkNEW(SkRecord));
431 SkRecorder canvas(record.get(), src.width(), src.height());
432 src.draw(&canvas);
433 return SkNEW_ARGS(SkPicture, (src.width(), src.height(), record.detach(), NULL/*bbh*/));
434}
435
Mike Klein744fb732014-06-23 15:13:26 -0400436// fRecord OK
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000437SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
438 SkPictInfo info;
439
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000440 if (!InternalOnly_StreamIsSKP(stream, &info)) {
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000441 return NULL;
442 }
443
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000444 // Check to see if there is a playback to recreate.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 if (stream->readBool()) {
robertphillipsdb539902014-07-01 08:47:04 -0700446 SkPictureData* data = SkPictureData::CreateFromStream(stream, info, proc);
447 if (NULL == data) {
scroggo@google.com12705322013-10-01 15:30:46 +0000448 return NULL;
449 }
mtklein1b523ba2014-08-20 15:50:45 -0700450 const SkPicture src(data, info.fWidth, info.fHeight);
451 return Forwardport(src);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 }
reed@google.com67562092012-06-22 15:38:39 +0000453
robertphillipse26e65e2014-06-12 05:51:22 -0700454 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455}
456
Mike Klein744fb732014-06-23 15:13:26 -0400457// fRecord OK
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000458SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
459 SkPictInfo info;
460
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000461 if (!InternalOnly_BufferIsSKP(buffer, &info)) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000462 return NULL;
463 }
464
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000465 // Check to see if there is a playback to recreate.
466 if (buffer.readBool()) {
robertphillipsdb539902014-07-01 08:47:04 -0700467 SkPictureData* data = SkPictureData::CreateFromBuffer(buffer, info);
468 if (NULL == data) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000469 return NULL;
470 }
mtklein1b523ba2014-08-20 15:50:45 -0700471 const SkPicture src(data, info.fWidth, info.fHeight);
472 return Forwardport(src);
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000473 }
474
robertphillipse26e65e2014-06-12 05:51:22 -0700475 return NULL;
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000476}
477
Mike Klein744fb732014-06-23 15:13:26 -0400478// fRecord OK
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000479void SkPicture::createHeader(SkPictInfo* info) const {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000480 // Copy magic bytes at the beginning of the header
481 SkASSERT(sizeof(kMagic) == 8);
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000482 SkASSERT(sizeof(kMagic) == sizeof(info->fMagic));
483 memcpy(info->fMagic, kMagic, sizeof(kMagic));
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000484
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000485 // Set picture info after magic bytes in the header
commit-bot@chromium.orge8d96142014-02-25 02:16:10 +0000486 info->fVersion = CURRENT_PICTURE_VERSION;
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000487 info->fWidth = fWidth;
488 info->fHeight = fHeight;
489 info->fFlags = SkPictInfo::kCrossProcess_Flag;
490 // TODO: remove this flag, since we're always float (now)
491 info->fFlags |= SkPictInfo::kScalarIsFloat_Flag;
492
493 if (8 == sizeof(void*)) {
494 info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
495 }
496}
497
mtklein73734562014-06-24 12:28:34 -0700498// fRecord OK
scroggo@google.com32ef1312013-02-22 22:04:19 +0000499void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
robertphillipsdb539902014-07-01 08:47:04 -0700500 const SkPictureData* data = fData.get();
mtklein73734562014-06-24 12:28:34 -0700501
502 // If we're a new-format picture, backport to old format for serialization.
503 SkAutoTDelete<SkPicture> oldFormat;
robertphillipsdb539902014-07-01 08:47:04 -0700504 if (NULL == data && NULL != fRecord.get()) {
mtklein73734562014-06-24 12:28:34 -0700505 oldFormat.reset(backport(*fRecord, fWidth, fHeight));
robertphillipsdb539902014-07-01 08:47:04 -0700506 data = oldFormat->fData.get();
507 SkASSERT(NULL != data);
mtklein73734562014-06-24 12:28:34 -0700508 }
509
commit-bot@chromium.org2ee3c2c2014-05-19 12:36:15 +0000510 SkPictInfo info;
511 this->createHeader(&info);
commit-bot@chromium.org0943f5f2014-03-28 18:05:47 +0000512 stream->write(&info, sizeof(info));
mtklein40684ba2014-06-24 10:12:39 -0700513
robertphillipsdb539902014-07-01 08:47:04 -0700514 if (NULL != data) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 stream->writeBool(true);
robertphillipsdb539902014-07-01 08:47:04 -0700516 data->serialize(stream, encoder);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 } else {
518 stream->writeBool(false);
519 }
520}
521
Mike Klein744fb732014-06-23 15:13:26 -0400522// fRecord OK
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000523void SkPicture::flatten(SkWriteBuffer& buffer) const {
robertphillipsdb539902014-07-01 08:47:04 -0700524 const SkPictureData* data = fData.get();
mtklein73734562014-06-24 12:28:34 -0700525
526 // If we're a new-format picture, backport to old format for serialization.
527 SkAutoTDelete<SkPicture> oldFormat;
robertphillipsdb539902014-07-01 08:47:04 -0700528 if (NULL == data && NULL != fRecord.get()) {
mtklein73734562014-06-24 12:28:34 -0700529 oldFormat.reset(backport(*fRecord, fWidth, fHeight));
robertphillipsdb539902014-07-01 08:47:04 -0700530 data = oldFormat->fData.get();
531 SkASSERT(NULL != data);
mtklein73734562014-06-24 12:28:34 -0700532 }
533
commit-bot@chromium.org2ee3c2c2014-05-19 12:36:15 +0000534 SkPictInfo info;
535 this->createHeader(&info);
commit-bot@chromium.org0943f5f2014-03-28 18:05:47 +0000536 buffer.writeByteArray(&info, sizeof(info));
mtklein40684ba2014-06-24 10:12:39 -0700537
robertphillipsdb539902014-07-01 08:47:04 -0700538 if (NULL != data) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000539 buffer.writeBool(true);
robertphillipsdb539902014-07-01 08:47:04 -0700540 data->flatten(buffer);
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000541 } else {
542 buffer.writeBool(false);
543 }
544}
545
commit-bot@chromium.org1c308182014-03-19 22:54:40 +0000546#if SK_SUPPORT_GPU
tomhudson3a0f2792014-08-20 05:29:41 -0700547// fRecord OK
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000548bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **reason) const {
tomhudson3a0f2792014-08-20 05:29:41 -0700549 if (fRecord.get()) {
550 return fAnalysis.suitableForGpuRasterization(reason, 0);
551 }
robertphillipsdb539902014-07-01 08:47:04 -0700552 if (NULL == fData.get()) {
robertphillips0bdbea72014-06-11 11:37:55 -0700553 if (NULL != reason) {
robertphillipsdb539902014-07-01 08:47:04 -0700554 *reason = "Missing internal data.";
robertphillips0bdbea72014-06-11 11:37:55 -0700555 }
556 return false;
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000557 }
robertphillips0bdbea72014-06-11 11:37:55 -0700558
robertphillipsdb539902014-07-01 08:47:04 -0700559 return fData->suitableForGpuRasterization(context, reason);
commit-bot@chromium.orgeb9547c2014-03-19 21:24:25 +0000560}
commit-bot@chromium.org1c308182014-03-19 22:54:40 +0000561#endif
commit-bot@chromium.orgeb9547c2014-03-19 21:24:25 +0000562
mtkleinc551d9f2014-08-20 08:09:46 -0700563// fRecord OK
ajuma750ae262014-08-18 12:59:55 -0700564bool SkPicture::hasText() const {
mtkleinc551d9f2014-08-20 08:09:46 -0700565 if (fRecord.get()) {
566 return fAnalysis.fHasText;
567 }
568 if (fData.get()) {
569 return fData->hasText();
570 }
571 SkFAIL("Unreachable");
572 return false;
ajuma750ae262014-08-18 12:59:55 -0700573}
574
tomhudsond9305112014-07-05 13:37:53 -0700575// fRecord OK
tomhudson@google.com381010e2013-10-24 11:12:47 +0000576bool SkPicture::willPlayBackBitmaps() const {
tomhudsond9305112014-07-05 13:37:53 -0700577 if (fRecord.get()) {
tomhudson3a0f2792014-08-20 05:29:41 -0700578 return fAnalysis.fWillPlaybackBitmaps;
tomhudsond9305112014-07-05 13:37:53 -0700579 }
mtkleinc551d9f2014-08-20 08:09:46 -0700580 if (fData.get()) {
581 return fData->containsBitmaps();
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000582 }
mtkleinc551d9f2014-08-20 08:09:46 -0700583 SkFAIL("Unreachable");
584 return false;
tomhudson@google.com381010e2013-10-24 11:12:47 +0000585}
586
Mike Klein744fb732014-06-23 15:13:26 -0400587// fRecord OK
robertphillips@google.comd5500882014-04-02 23:51:13 +0000588static int32_t next_picture_generation_id() {
589 static int32_t gPictureGenerationID = 0;
590 // do a loop in case our global wraps around, as we never want to
591 // return a 0
592 int32_t genID;
593 do {
594 genID = sk_atomic_inc(&gPictureGenerationID) + 1;
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000595 } while (SK_InvalidGenID == genID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000596 return genID;
597}
598
Mike Klein744fb732014-06-23 15:13:26 -0400599// fRecord OK
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000600uint32_t SkPicture::uniqueID() const {
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000601 if (SK_InvalidGenID == fUniqueID) {
602 fUniqueID = next_picture_generation_id();
robertphillips@google.comd5500882014-04-02 23:51:13 +0000603 }
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000604 return fUniqueID;
robertphillips@google.comd5500882014-04-02 23:51:13 +0000605}
Mike Klein744fb732014-06-23 15:13:26 -0400606
607// fRecord OK
mtklein5ad6ee12014-08-11 08:08:43 -0700608SkPicture::SkPicture(int width, int height, SkRecord* record, SkBBoxHierarchy* bbh)
Mike Klein744fb732014-06-23 15:13:26 -0400609 : fWidth(width)
610 , fHeight(height)
tomhudsond9305112014-07-05 13:37:53 -0700611 , fRecord(record)
mtklein5ad6ee12014-08-11 08:08:43 -0700612 , fBBH(SkSafeRef(bbh))
mtkleinc551d9f2014-08-20 08:09:46 -0700613 , fAnalysis(*record) {
mtklein5ad6ee12014-08-11 08:08:43 -0700614 // TODO: delay as much of this work until just before first playback?
615 if (fBBH.get()) {
616 SkRecordFillBounds(*record, fBBH.get());
617 }
Mike Klein744fb732014-06-23 15:13:26 -0400618 this->needsNewGenID();
619}
robertphillipsd771f6b2014-07-22 10:18:06 -0700620
621// Note that we are assuming that this entry point will only be called from
mtklein3ba15ae2014-08-06 11:57:00 -0700622// one thread. Currently the only client of this method is
robertphillipsd771f6b2014-07-22 10:18:06 -0700623// SkGpuDevice::EXPERIMENTAL_optimize which should be only called from a single
624// thread.
625void SkPicture::addDeletionListener(DeletionListener* listener) const {
626 SkASSERT(NULL != listener);
627
628 *fDeletionListeners.append() = SkRef(listener);
629}
630
631void SkPicture::callDeletionListeners() {
632 for (int i = 0; i < fDeletionListeners.count(); ++i) {
633 fDeletionListeners[i]->onDeletion(this->uniqueID());
634 }
635
636 fDeletionListeners.unrefAll();
637}
mtklein5a246bb2014-08-14 19:17:18 -0700638
639// fRecord OK
640int SkPicture::approximateOpCount() const {
641 SkASSERT(fRecord.get() || fData.get());
642 if (fRecord.get()) {
643 return fRecord->count();
644 }
645 if (fData.get()) {
646 return fData->opCount();
647 }
648 return 0;
649}