blob: 515f28d0791097f1df693e11606151bd82d7c0c9 [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;
mtklein5ad6ee12014-08-11 08:08:43 -0700282 SkRecordDraw(src, recorder.beginRecording(width, height), NULL/*bbh*/, NULL/*callback*/);
mtklein73734562014-06-24 12:28:34 -0700283 return recorder.endRecording();
284}
285
Mike Kleinc11530e2014-06-24 11:29:06 -0400286// fRecord OK
robertphillipsd771f6b2014-07-22 10:18:06 -0700287SkPicture::~SkPicture() {
288 this->callDeletionListeners();
289}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290
mtklein7b705bb2014-08-20 14:22:58 -0700291// fRecord OK
mtkleind3e474e2014-06-27 12:34:44 -0700292#ifdef SK_SUPPORT_LEGACY_PICTURE_CLONE
djsollen@google.comc9ab9872012-08-29 18:52:07 +0000293SkPicture* SkPicture::clone() const {
mtklein3ba15ae2014-08-06 11:57:00 -0700294 return SkRef(const_cast<SkPicture*>(this));
djsollen@google.comc9ab9872012-08-29 18:52:07 +0000295}
mtkleind3e474e2014-06-27 12:34:44 -0700296#endif//SK_SUPPORT_LEGACY_PICTURE_CLONE
djsollen@google.comc9ab9872012-08-29 18:52:07 +0000297
Mike Klein744fb732014-06-23 15:13:26 -0400298// fRecord OK
299void SkPicture::EXPERIMENTAL_addAccelData(const SkPicture::AccelData* data) const {
300 fAccelData.reset(SkRef(data));
301}
302
303// fRecord OK
304const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData(
305 SkPicture::AccelData::Key key) const {
306 if (NULL != fAccelData.get() && fAccelData->getKey() == key) {
307 return fAccelData.get();
308 }
309 return NULL;
310}
311
312// fRecord OK
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000313SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
314 static int32_t gNextID = 0;
315
316 int32_t id = sk_atomic_inc(&gNextID);
317 if (id >= 1 << (8 * sizeof(Domain))) {
318 SK_CRASH();
319 }
320
321 return static_cast<Domain>(id);
322}
323
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324///////////////////////////////////////////////////////////////////////////////
325
robertphillips3afef1f2014-07-08 06:12:22 -0700326uint32_t SkPicture::OperationList::offset(int index) const {
327 SkASSERT(index < fOps.count());
328 return ((SkPictureStateTree::Draw*)fOps[index])->fOffset;
329}
330
331const SkMatrix& SkPicture::OperationList::matrix(int index) const {
332 SkASSERT(index < fOps.count());
333 return *((SkPictureStateTree::Draw*)fOps[index])->fMatrix;
334}
335
mtklein7b705bb2014-08-20 14:22:58 -0700336// fRecord TODO(robert) / kind of OK in a non-optimal sense
robertphillipsce4dd3d2014-07-07 13:46:35 -0700337const SkPicture::OperationList* SkPicture::EXPERIMENTAL_getActiveOps(const SkIRect& queryRect) const {
robertphillipsdb539902014-07-01 08:47:04 -0700338 SkASSERT(NULL != fData.get());
339 if (NULL != fData.get()) {
340 return fData->getActiveOps(queryRect);
commit-bot@chromium.org70512af2014-03-18 17:45:32 +0000341 }
robertphillipsce4dd3d2014-07-07 13:46:35 -0700342 return NULL;
commit-bot@chromium.org75cf29b2014-03-24 19:40:49 +0000343}
344
Mike Klein744fb732014-06-23 15:13:26 -0400345// fRecord OK
346void SkPicture::draw(SkCanvas* canvas, SkDrawPictureCallback* callback) const {
347 SkASSERT(NULL != canvas);
robertphillipsdb539902014-07-01 08:47:04 -0700348 SkASSERT(NULL != fData.get() || NULL != fRecord.get());
Mike Klein744fb732014-06-23 15:13:26 -0400349
mtklein3e8232b2014-08-18 13:39:11 -0700350 // If the query contains the whole picture, don't bother with the BBH.
351 SkRect clipBounds = { 0, 0, 0, 0 };
352 (void)canvas->getClipBounds(&clipBounds);
353 const bool useBBH = !clipBounds.contains(SkRect::MakeWH(this->width(), this->height()));
354
robertphillipsdb539902014-07-01 08:47:04 -0700355 if (NULL != fData.get()) {
robertphillipsce4dd3d2014-07-07 13:46:35 -0700356 SkPicturePlayback playback(this);
mtklein3e8232b2014-08-18 13:39:11 -0700357 playback.setUseBBH(useBBH);
robertphillipsce4dd3d2014-07-07 13:46:35 -0700358 playback.draw(canvas, callback);
Mike Klein744fb732014-06-23 15:13:26 -0400359 }
360 if (NULL != fRecord.get()) {
mtklein3e8232b2014-08-18 13:39:11 -0700361 SkRecordDraw(*fRecord, canvas, useBBH ? fBBH.get() : NULL, callback);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362 }
363}
364
365///////////////////////////////////////////////////////////////////////////////
366
367#include "SkStream.h"
368
rmistry@google.comd6bab022013-12-02 13:50:38 +0000369static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000370
Mike Klein744fb732014-06-23 15:13:26 -0400371// fRecord OK
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000372bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
373 if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
374 return false;
375 }
376
377 if (info.fVersion < MIN_PICTURE_VERSION ||
378 info.fVersion > CURRENT_PICTURE_VERSION) {
379 return false;
380 }
381
382 return true;
383}
rmistry@google.comd6bab022013-12-02 13:50:38 +0000384
Mike Klein744fb732014-06-23 15:13:26 -0400385// fRecord OK
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000386bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000387 if (NULL == stream) {
388 return false;
borenet@google.com66bcbd12012-09-17 18:26:06 +0000389 }
reed@google.com67562092012-06-22 15:38:39 +0000390
rmistry@google.comd6bab022013-12-02 13:50:38 +0000391 // Check magic bytes.
reed@google.com67562092012-06-22 15:38:39 +0000392 SkPictInfo info;
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000393 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
394 if (!stream->read(&info, sizeof(info)) || !IsValidPictInfo(info)) {
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000395 return false;
reed@google.com67562092012-06-22 15:38:39 +0000396 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000398 if (pInfo != NULL) {
399 *pInfo = info;
400 }
401 return true;
402}
403
Mike Klein744fb732014-06-23 15:13:26 -0400404// fRecord OK
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000405bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer& buffer, SkPictInfo* pInfo) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000406 // Check magic bytes.
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000407 SkPictInfo info;
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000408 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
409 if (!buffer.readByteArray(&info, sizeof(info)) || !IsValidPictInfo(info)) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000410 return false;
411 }
412
413 if (pInfo != NULL) {
414 *pInfo = info;
415 }
416 return true;
417}
418
Mike Klein744fb732014-06-23 15:13:26 -0400419// fRecord OK
robertphillipsdb539902014-07-01 08:47:04 -0700420SkPicture::SkPicture(SkPictureData* data, int width, int height)
421 : fData(data)
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000422 , fWidth(width)
Mike Klein27dc17c2014-08-18 18:35:16 -0400423 , fHeight(height)
tomhudson3a0f2792014-08-20 05:29:41 -0700424 , fAnalysis() {
robertphillips@google.comd5500882014-04-02 23:51:13 +0000425 this->needsNewGenID();
426}
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000427
mtklein7b705bb2014-08-20 14:22:58 -0700428SkPicture* SkPicture::Forwardport(const SkPicture& src) {
429 SkAutoTDelete<SkRecord> record(SkNEW(SkRecord));
430 SkRecorder canvas(record.get(), src.width(), src.height());
431 src.draw(&canvas);
432 return SkNEW_ARGS(SkPicture, (src.width(), src.height(), record.detach(), NULL/*bbh*/));
433}
434
Mike Klein744fb732014-06-23 15:13:26 -0400435// fRecord OK
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000436SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
437 SkPictInfo info;
438
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000439 if (!InternalOnly_StreamIsSKP(stream, &info)) {
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000440 return NULL;
441 }
442
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000443 // Check to see if there is a playback to recreate.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 if (stream->readBool()) {
robertphillipsdb539902014-07-01 08:47:04 -0700445 SkPictureData* data = SkPictureData::CreateFromStream(stream, info, proc);
446 if (NULL == data) {
scroggo@google.com12705322013-10-01 15:30:46 +0000447 return NULL;
448 }
mtklein4082d292014-08-20 16:18:25 -0700449#if 0
mtklein1b523ba2014-08-20 15:50:45 -0700450 const SkPicture src(data, info.fWidth, info.fHeight);
451 return Forwardport(src);
mtklein4082d292014-08-20 16:18:25 -0700452#else
453 return SkNEW_ARGS(SkPicture, (data, info.fWidth, info.fHeight));
454#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 }
reed@google.com67562092012-06-22 15:38:39 +0000456
robertphillipse26e65e2014-06-12 05:51:22 -0700457 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458}
459
Mike Klein744fb732014-06-23 15:13:26 -0400460// fRecord OK
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000461SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
462 SkPictInfo info;
463
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000464 if (!InternalOnly_BufferIsSKP(buffer, &info)) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000465 return NULL;
466 }
467
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000468 // Check to see if there is a playback to recreate.
469 if (buffer.readBool()) {
robertphillipsdb539902014-07-01 08:47:04 -0700470 SkPictureData* data = SkPictureData::CreateFromBuffer(buffer, info);
471 if (NULL == data) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000472 return NULL;
473 }
mtklein4082d292014-08-20 16:18:25 -0700474#if 0
mtklein1b523ba2014-08-20 15:50:45 -0700475 const SkPicture src(data, info.fWidth, info.fHeight);
476 return Forwardport(src);
mtklein4082d292014-08-20 16:18:25 -0700477#else
478 return SkNEW_ARGS(SkPicture, (data, info.fWidth, info.fHeight));
479#endif
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000480 }
481
robertphillipse26e65e2014-06-12 05:51:22 -0700482 return NULL;
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000483}
484
Mike Klein744fb732014-06-23 15:13:26 -0400485// fRecord OK
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000486void SkPicture::createHeader(SkPictInfo* info) const {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000487 // Copy magic bytes at the beginning of the header
488 SkASSERT(sizeof(kMagic) == 8);
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000489 SkASSERT(sizeof(kMagic) == sizeof(info->fMagic));
490 memcpy(info->fMagic, kMagic, sizeof(kMagic));
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000491
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000492 // Set picture info after magic bytes in the header
commit-bot@chromium.orge8d96142014-02-25 02:16:10 +0000493 info->fVersion = CURRENT_PICTURE_VERSION;
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000494 info->fWidth = fWidth;
495 info->fHeight = fHeight;
496 info->fFlags = SkPictInfo::kCrossProcess_Flag;
497 // TODO: remove this flag, since we're always float (now)
498 info->fFlags |= SkPictInfo::kScalarIsFloat_Flag;
499
500 if (8 == sizeof(void*)) {
501 info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
502 }
503}
504
mtklein73734562014-06-24 12:28:34 -0700505// fRecord OK
scroggo@google.com32ef1312013-02-22 22:04:19 +0000506void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
robertphillipsdb539902014-07-01 08:47:04 -0700507 const SkPictureData* data = fData.get();
mtklein73734562014-06-24 12:28:34 -0700508
509 // If we're a new-format picture, backport to old format for serialization.
510 SkAutoTDelete<SkPicture> oldFormat;
robertphillipsdb539902014-07-01 08:47:04 -0700511 if (NULL == data && NULL != fRecord.get()) {
mtklein73734562014-06-24 12:28:34 -0700512 oldFormat.reset(backport(*fRecord, fWidth, fHeight));
robertphillipsdb539902014-07-01 08:47:04 -0700513 data = oldFormat->fData.get();
514 SkASSERT(NULL != data);
mtklein73734562014-06-24 12:28:34 -0700515 }
516
commit-bot@chromium.org2ee3c2c2014-05-19 12:36:15 +0000517 SkPictInfo info;
518 this->createHeader(&info);
commit-bot@chromium.org0943f5f2014-03-28 18:05:47 +0000519 stream->write(&info, sizeof(info));
mtklein40684ba2014-06-24 10:12:39 -0700520
robertphillipsdb539902014-07-01 08:47:04 -0700521 if (NULL != data) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 stream->writeBool(true);
robertphillipsdb539902014-07-01 08:47:04 -0700523 data->serialize(stream, encoder);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 } else {
525 stream->writeBool(false);
526 }
527}
528
Mike Klein744fb732014-06-23 15:13:26 -0400529// fRecord OK
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000530void SkPicture::flatten(SkWriteBuffer& buffer) const {
robertphillipsdb539902014-07-01 08:47:04 -0700531 const SkPictureData* data = fData.get();
mtklein73734562014-06-24 12:28:34 -0700532
533 // If we're a new-format picture, backport to old format for serialization.
534 SkAutoTDelete<SkPicture> oldFormat;
robertphillipsdb539902014-07-01 08:47:04 -0700535 if (NULL == data && NULL != fRecord.get()) {
mtklein73734562014-06-24 12:28:34 -0700536 oldFormat.reset(backport(*fRecord, fWidth, fHeight));
robertphillipsdb539902014-07-01 08:47:04 -0700537 data = oldFormat->fData.get();
538 SkASSERT(NULL != data);
mtklein73734562014-06-24 12:28:34 -0700539 }
540
commit-bot@chromium.org2ee3c2c2014-05-19 12:36:15 +0000541 SkPictInfo info;
542 this->createHeader(&info);
commit-bot@chromium.org0943f5f2014-03-28 18:05:47 +0000543 buffer.writeByteArray(&info, sizeof(info));
mtklein40684ba2014-06-24 10:12:39 -0700544
robertphillipsdb539902014-07-01 08:47:04 -0700545 if (NULL != data) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000546 buffer.writeBool(true);
robertphillipsdb539902014-07-01 08:47:04 -0700547 data->flatten(buffer);
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000548 } else {
549 buffer.writeBool(false);
550 }
551}
552
commit-bot@chromium.org1c308182014-03-19 22:54:40 +0000553#if SK_SUPPORT_GPU
tomhudson3a0f2792014-08-20 05:29:41 -0700554// fRecord OK
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000555bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **reason) const {
tomhudson3a0f2792014-08-20 05:29:41 -0700556 if (fRecord.get()) {
557 return fAnalysis.suitableForGpuRasterization(reason, 0);
558 }
robertphillipsdb539902014-07-01 08:47:04 -0700559 if (NULL == fData.get()) {
robertphillips0bdbea72014-06-11 11:37:55 -0700560 if (NULL != reason) {
robertphillipsdb539902014-07-01 08:47:04 -0700561 *reason = "Missing internal data.";
robertphillips0bdbea72014-06-11 11:37:55 -0700562 }
563 return false;
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000564 }
robertphillips0bdbea72014-06-11 11:37:55 -0700565
robertphillipsdb539902014-07-01 08:47:04 -0700566 return fData->suitableForGpuRasterization(context, reason);
commit-bot@chromium.orgeb9547c2014-03-19 21:24:25 +0000567}
commit-bot@chromium.org1c308182014-03-19 22:54:40 +0000568#endif
commit-bot@chromium.orgeb9547c2014-03-19 21:24:25 +0000569
mtkleinc551d9f2014-08-20 08:09:46 -0700570// fRecord OK
ajuma750ae262014-08-18 12:59:55 -0700571bool SkPicture::hasText() const {
mtkleinc551d9f2014-08-20 08:09:46 -0700572 if (fRecord.get()) {
573 return fAnalysis.fHasText;
574 }
575 if (fData.get()) {
576 return fData->hasText();
577 }
578 SkFAIL("Unreachable");
579 return false;
ajuma750ae262014-08-18 12:59:55 -0700580}
581
tomhudsond9305112014-07-05 13:37:53 -0700582// fRecord OK
tomhudson@google.com381010e2013-10-24 11:12:47 +0000583bool SkPicture::willPlayBackBitmaps() const {
tomhudsond9305112014-07-05 13:37:53 -0700584 if (fRecord.get()) {
tomhudson3a0f2792014-08-20 05:29:41 -0700585 return fAnalysis.fWillPlaybackBitmaps;
tomhudsond9305112014-07-05 13:37:53 -0700586 }
mtkleinc551d9f2014-08-20 08:09:46 -0700587 if (fData.get()) {
588 return fData->containsBitmaps();
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000589 }
mtkleinc551d9f2014-08-20 08:09:46 -0700590 SkFAIL("Unreachable");
591 return false;
tomhudson@google.com381010e2013-10-24 11:12:47 +0000592}
593
Mike Klein744fb732014-06-23 15:13:26 -0400594// fRecord OK
robertphillips@google.comd5500882014-04-02 23:51:13 +0000595static int32_t next_picture_generation_id() {
596 static int32_t gPictureGenerationID = 0;
597 // do a loop in case our global wraps around, as we never want to
598 // return a 0
599 int32_t genID;
600 do {
601 genID = sk_atomic_inc(&gPictureGenerationID) + 1;
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000602 } while (SK_InvalidGenID == genID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000603 return genID;
604}
605
Mike Klein744fb732014-06-23 15:13:26 -0400606// fRecord OK
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000607uint32_t SkPicture::uniqueID() const {
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000608 if (SK_InvalidGenID == fUniqueID) {
609 fUniqueID = next_picture_generation_id();
robertphillips@google.comd5500882014-04-02 23:51:13 +0000610 }
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000611 return fUniqueID;
robertphillips@google.comd5500882014-04-02 23:51:13 +0000612}
Mike Klein744fb732014-06-23 15:13:26 -0400613
614// fRecord OK
mtklein5ad6ee12014-08-11 08:08:43 -0700615SkPicture::SkPicture(int width, int height, SkRecord* record, SkBBoxHierarchy* bbh)
Mike Klein744fb732014-06-23 15:13:26 -0400616 : fWidth(width)
617 , fHeight(height)
tomhudsond9305112014-07-05 13:37:53 -0700618 , fRecord(record)
mtklein5ad6ee12014-08-11 08:08:43 -0700619 , fBBH(SkSafeRef(bbh))
mtkleinc551d9f2014-08-20 08:09:46 -0700620 , fAnalysis(*record) {
mtklein5ad6ee12014-08-11 08:08:43 -0700621 // TODO: delay as much of this work until just before first playback?
622 if (fBBH.get()) {
623 SkRecordFillBounds(*record, fBBH.get());
624 }
Mike Klein744fb732014-06-23 15:13:26 -0400625 this->needsNewGenID();
626}
robertphillipsd771f6b2014-07-22 10:18:06 -0700627
628// Note that we are assuming that this entry point will only be called from
mtklein3ba15ae2014-08-06 11:57:00 -0700629// one thread. Currently the only client of this method is
robertphillipsd771f6b2014-07-22 10:18:06 -0700630// SkGpuDevice::EXPERIMENTAL_optimize which should be only called from a single
631// thread.
632void SkPicture::addDeletionListener(DeletionListener* listener) const {
633 SkASSERT(NULL != listener);
634
635 *fDeletionListeners.append() = SkRef(listener);
636}
637
638void SkPicture::callDeletionListeners() {
639 for (int i = 0; i < fDeletionListeners.count(); ++i) {
640 fDeletionListeners[i]->onDeletion(this->uniqueID());
641 }
642
643 fDeletionListeners.unrefAll();
644}
mtklein5a246bb2014-08-14 19:17:18 -0700645
646// fRecord OK
647int SkPicture::approximateOpCount() const {
648 SkASSERT(fRecord.get() || fData.get());
649 if (fRecord.get()) {
650 return fRecord->count();
651 }
652 if (fData.get()) {
653 return fData->opCount();
654 }
655 return 0;
656}