blob: d1a81a5aff345c4439e163d98959ac9c9db2e80a [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 }
mtklein4082d292014-08-20 16:18:25 -0700450#if 0
mtklein1b523ba2014-08-20 15:50:45 -0700451 const SkPicture src(data, info.fWidth, info.fHeight);
452 return Forwardport(src);
mtklein4082d292014-08-20 16:18:25 -0700453#else
454 return SkNEW_ARGS(SkPicture, (data, info.fWidth, info.fHeight));
455#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 }
reed@google.com67562092012-06-22 15:38:39 +0000457
robertphillipse26e65e2014-06-12 05:51:22 -0700458 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459}
460
Mike Klein744fb732014-06-23 15:13:26 -0400461// fRecord OK
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000462SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
463 SkPictInfo info;
464
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000465 if (!InternalOnly_BufferIsSKP(buffer, &info)) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000466 return NULL;
467 }
468
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000469 // Check to see if there is a playback to recreate.
470 if (buffer.readBool()) {
robertphillipsdb539902014-07-01 08:47:04 -0700471 SkPictureData* data = SkPictureData::CreateFromBuffer(buffer, info);
472 if (NULL == data) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000473 return NULL;
474 }
mtklein4082d292014-08-20 16:18:25 -0700475#if 0
mtklein1b523ba2014-08-20 15:50:45 -0700476 const SkPicture src(data, info.fWidth, info.fHeight);
477 return Forwardport(src);
mtklein4082d292014-08-20 16:18:25 -0700478#else
479 return SkNEW_ARGS(SkPicture, (data, info.fWidth, info.fHeight));
480#endif
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000481 }
482
robertphillipse26e65e2014-06-12 05:51:22 -0700483 return NULL;
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000484}
485
Mike Klein744fb732014-06-23 15:13:26 -0400486// fRecord OK
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000487void SkPicture::createHeader(SkPictInfo* info) const {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000488 // Copy magic bytes at the beginning of the header
489 SkASSERT(sizeof(kMagic) == 8);
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000490 SkASSERT(sizeof(kMagic) == sizeof(info->fMagic));
491 memcpy(info->fMagic, kMagic, sizeof(kMagic));
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000492
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000493 // Set picture info after magic bytes in the header
commit-bot@chromium.orge8d96142014-02-25 02:16:10 +0000494 info->fVersion = CURRENT_PICTURE_VERSION;
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000495 info->fWidth = fWidth;
496 info->fHeight = fHeight;
497 info->fFlags = SkPictInfo::kCrossProcess_Flag;
498 // TODO: remove this flag, since we're always float (now)
499 info->fFlags |= SkPictInfo::kScalarIsFloat_Flag;
500
501 if (8 == sizeof(void*)) {
502 info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
503 }
504}
505
mtklein73734562014-06-24 12:28:34 -0700506// fRecord OK
scroggo@google.com32ef1312013-02-22 22:04:19 +0000507void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
robertphillipsdb539902014-07-01 08:47:04 -0700508 const SkPictureData* data = fData.get();
mtklein73734562014-06-24 12:28:34 -0700509
510 // If we're a new-format picture, backport to old format for serialization.
511 SkAutoTDelete<SkPicture> oldFormat;
robertphillipsdb539902014-07-01 08:47:04 -0700512 if (NULL == data && NULL != fRecord.get()) {
mtklein73734562014-06-24 12:28:34 -0700513 oldFormat.reset(backport(*fRecord, fWidth, fHeight));
robertphillipsdb539902014-07-01 08:47:04 -0700514 data = oldFormat->fData.get();
515 SkASSERT(NULL != data);
mtklein73734562014-06-24 12:28:34 -0700516 }
517
commit-bot@chromium.org2ee3c2c2014-05-19 12:36:15 +0000518 SkPictInfo info;
519 this->createHeader(&info);
commit-bot@chromium.org0943f5f2014-03-28 18:05:47 +0000520 stream->write(&info, sizeof(info));
mtklein40684ba2014-06-24 10:12:39 -0700521
robertphillipsdb539902014-07-01 08:47:04 -0700522 if (NULL != data) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 stream->writeBool(true);
robertphillipsdb539902014-07-01 08:47:04 -0700524 data->serialize(stream, encoder);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 } else {
526 stream->writeBool(false);
527 }
528}
529
Mike Klein744fb732014-06-23 15:13:26 -0400530// fRecord OK
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000531void SkPicture::flatten(SkWriteBuffer& buffer) const {
robertphillipsdb539902014-07-01 08:47:04 -0700532 const SkPictureData* data = fData.get();
mtklein73734562014-06-24 12:28:34 -0700533
534 // If we're a new-format picture, backport to old format for serialization.
535 SkAutoTDelete<SkPicture> oldFormat;
robertphillipsdb539902014-07-01 08:47:04 -0700536 if (NULL == data && NULL != fRecord.get()) {
mtklein73734562014-06-24 12:28:34 -0700537 oldFormat.reset(backport(*fRecord, fWidth, fHeight));
robertphillipsdb539902014-07-01 08:47:04 -0700538 data = oldFormat->fData.get();
539 SkASSERT(NULL != data);
mtklein73734562014-06-24 12:28:34 -0700540 }
541
commit-bot@chromium.org2ee3c2c2014-05-19 12:36:15 +0000542 SkPictInfo info;
543 this->createHeader(&info);
commit-bot@chromium.org0943f5f2014-03-28 18:05:47 +0000544 buffer.writeByteArray(&info, sizeof(info));
mtklein40684ba2014-06-24 10:12:39 -0700545
robertphillipsdb539902014-07-01 08:47:04 -0700546 if (NULL != data) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000547 buffer.writeBool(true);
robertphillipsdb539902014-07-01 08:47:04 -0700548 data->flatten(buffer);
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000549 } else {
550 buffer.writeBool(false);
551 }
552}
553
commit-bot@chromium.org1c308182014-03-19 22:54:40 +0000554#if SK_SUPPORT_GPU
tomhudson3a0f2792014-08-20 05:29:41 -0700555// fRecord OK
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000556bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **reason) const {
tomhudson3a0f2792014-08-20 05:29:41 -0700557 if (fRecord.get()) {
558 return fAnalysis.suitableForGpuRasterization(reason, 0);
559 }
robertphillipsdb539902014-07-01 08:47:04 -0700560 if (NULL == fData.get()) {
robertphillips0bdbea72014-06-11 11:37:55 -0700561 if (NULL != reason) {
robertphillipsdb539902014-07-01 08:47:04 -0700562 *reason = "Missing internal data.";
robertphillips0bdbea72014-06-11 11:37:55 -0700563 }
564 return false;
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000565 }
robertphillips0bdbea72014-06-11 11:37:55 -0700566
robertphillipsdb539902014-07-01 08:47:04 -0700567 return fData->suitableForGpuRasterization(context, reason);
commit-bot@chromium.orgeb9547c2014-03-19 21:24:25 +0000568}
commit-bot@chromium.org1c308182014-03-19 22:54:40 +0000569#endif
commit-bot@chromium.orgeb9547c2014-03-19 21:24:25 +0000570
mtkleinc551d9f2014-08-20 08:09:46 -0700571// fRecord OK
ajuma750ae262014-08-18 12:59:55 -0700572bool SkPicture::hasText() const {
mtkleinc551d9f2014-08-20 08:09:46 -0700573 if (fRecord.get()) {
574 return fAnalysis.fHasText;
575 }
576 if (fData.get()) {
577 return fData->hasText();
578 }
579 SkFAIL("Unreachable");
580 return false;
ajuma750ae262014-08-18 12:59:55 -0700581}
582
tomhudsond9305112014-07-05 13:37:53 -0700583// fRecord OK
tomhudson@google.com381010e2013-10-24 11:12:47 +0000584bool SkPicture::willPlayBackBitmaps() const {
tomhudsond9305112014-07-05 13:37:53 -0700585 if (fRecord.get()) {
tomhudson3a0f2792014-08-20 05:29:41 -0700586 return fAnalysis.fWillPlaybackBitmaps;
tomhudsond9305112014-07-05 13:37:53 -0700587 }
mtkleinc551d9f2014-08-20 08:09:46 -0700588 if (fData.get()) {
589 return fData->containsBitmaps();
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000590 }
mtkleinc551d9f2014-08-20 08:09:46 -0700591 SkFAIL("Unreachable");
592 return false;
tomhudson@google.com381010e2013-10-24 11:12:47 +0000593}
594
Mike Klein744fb732014-06-23 15:13:26 -0400595// fRecord OK
robertphillips@google.comd5500882014-04-02 23:51:13 +0000596static int32_t next_picture_generation_id() {
597 static int32_t gPictureGenerationID = 0;
598 // do a loop in case our global wraps around, as we never want to
599 // return a 0
600 int32_t genID;
601 do {
602 genID = sk_atomic_inc(&gPictureGenerationID) + 1;
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000603 } while (SK_InvalidGenID == genID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000604 return genID;
605}
606
Mike Klein744fb732014-06-23 15:13:26 -0400607// fRecord OK
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000608uint32_t SkPicture::uniqueID() const {
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000609 if (SK_InvalidGenID == fUniqueID) {
610 fUniqueID = next_picture_generation_id();
robertphillips@google.comd5500882014-04-02 23:51:13 +0000611 }
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000612 return fUniqueID;
robertphillips@google.comd5500882014-04-02 23:51:13 +0000613}
Mike Klein744fb732014-06-23 15:13:26 -0400614
615// fRecord OK
mtklein5ad6ee12014-08-11 08:08:43 -0700616SkPicture::SkPicture(int width, int height, SkRecord* record, SkBBoxHierarchy* bbh)
Mike Klein744fb732014-06-23 15:13:26 -0400617 : fWidth(width)
618 , fHeight(height)
tomhudsond9305112014-07-05 13:37:53 -0700619 , fRecord(record)
mtklein5ad6ee12014-08-11 08:08:43 -0700620 , fBBH(SkSafeRef(bbh))
mtkleinc551d9f2014-08-20 08:09:46 -0700621 , fAnalysis(*record) {
mtklein5ad6ee12014-08-11 08:08:43 -0700622 // TODO: delay as much of this work until just before first playback?
623 if (fBBH.get()) {
624 SkRecordFillBounds(*record, fBBH.get());
625 }
Mike Klein744fb732014-06-23 15:13:26 -0400626 this->needsNewGenID();
627}
robertphillipsd771f6b2014-07-22 10:18:06 -0700628
629// Note that we are assuming that this entry point will only be called from
mtklein3ba15ae2014-08-06 11:57:00 -0700630// one thread. Currently the only client of this method is
robertphillipsd771f6b2014-07-22 10:18:06 -0700631// SkGpuDevice::EXPERIMENTAL_optimize which should be only called from a single
632// thread.
633void SkPicture::addDeletionListener(DeletionListener* listener) const {
634 SkASSERT(NULL != listener);
635
636 *fDeletionListeners.append() = SkRef(listener);
637}
638
639void SkPicture::callDeletionListeners() {
640 for (int i = 0; i < fDeletionListeners.count(); ++i) {
641 fDeletionListeners[i]->onDeletion(this->uniqueID());
642 }
643
644 fDeletionListeners.unrefAll();
645}
mtklein5a246bb2014-08-14 19:17:18 -0700646
647// fRecord OK
648int SkPicture::approximateOpCount() const {
649 SkASSERT(fRecord.get() || fData.get());
650 if (fRecord.get()) {
651 return fRecord->count();
652 }
653 if (fData.get()) {
654 return fData->opCount();
655 }
656 return 0;
657}