blob: e80a17bd22cda92bae2feef21fc0a42f69dafa3b [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
Mike Klein744fb732014-06-23 15:13:26 -0400255// fRecord OK
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700256SkPicture::SkPicture(SkScalar width, SkScalar height,
robertphillipse26e65e2014-06-12 05:51:22 -0700257 const SkPictureRecord& record,
robertphillips0bdbea72014-06-11 11:37:55 -0700258 bool deepCopyOps)
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700259 : fCullWidth(width)
260 , fCullHeight(height)
tomhudson3a0f2792014-08-20 05:29:41 -0700261 , fAnalysis() {
robertphillips0bdbea72014-06-11 11:37:55 -0700262 this->needsNewGenID();
robertphillips9058d602014-06-10 11:45:46 -0700263
robertphillips0bdbea72014-06-11 11:37:55 -0700264 SkPictInfo info;
265 this->createHeader(&info);
robertphillipsdb539902014-07-01 08:47:04 -0700266 fData.reset(SkNEW_ARGS(SkPictureData, (record, info, deepCopyOps)));
commit-bot@chromium.org2ee3c2c2014-05-19 12:36:15 +0000267}
268
robertphillipsdb539902014-07-01 08:47:04 -0700269// Create an SkPictureData-backed SkPicture from an SkRecord.
mtklein73734562014-06-24 12:28:34 -0700270// This for compatibility with serialization code only. This is not cheap.
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700271static SkPicture* backport(const SkRecord& src, const SkRect& cullRect) {
mtklein73734562014-06-24 12:28:34 -0700272 SkPictureRecorder recorder;
mtkleinc92e5502014-08-21 13:07:27 -0700273 SkRecordDraw(src,
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700274 recorder.DEPRECATED_beginRecording(cullRect.width(), cullRect.height()),
275 NULL/*bbh*/, NULL/*callback*/);
mtklein73734562014-06-24 12:28:34 -0700276 return recorder.endRecording();
277}
278
Mike Kleinc11530e2014-06-24 11:29:06 -0400279// fRecord OK
robertphillipsd771f6b2014-07-22 10:18:06 -0700280SkPicture::~SkPicture() {
281 this->callDeletionListeners();
282}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283
mtklein7b705bb2014-08-20 14:22:58 -0700284// fRecord OK
mtkleind3e474e2014-06-27 12:34:44 -0700285#ifdef SK_SUPPORT_LEGACY_PICTURE_CLONE
djsollen@google.comc9ab9872012-08-29 18:52:07 +0000286SkPicture* SkPicture::clone() const {
mtklein3ba15ae2014-08-06 11:57:00 -0700287 return SkRef(const_cast<SkPicture*>(this));
djsollen@google.comc9ab9872012-08-29 18:52:07 +0000288}
mtkleind3e474e2014-06-27 12:34:44 -0700289#endif//SK_SUPPORT_LEGACY_PICTURE_CLONE
djsollen@google.comc9ab9872012-08-29 18:52:07 +0000290
Mike Klein744fb732014-06-23 15:13:26 -0400291// fRecord OK
292void SkPicture::EXPERIMENTAL_addAccelData(const SkPicture::AccelData* data) const {
293 fAccelData.reset(SkRef(data));
294}
295
296// fRecord OK
297const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData(
298 SkPicture::AccelData::Key key) const {
299 if (NULL != fAccelData.get() && fAccelData->getKey() == key) {
300 return fAccelData.get();
301 }
302 return NULL;
303}
304
305// fRecord OK
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000306SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
307 static int32_t gNextID = 0;
308
309 int32_t id = sk_atomic_inc(&gNextID);
310 if (id >= 1 << (8 * sizeof(Domain))) {
311 SK_CRASH();
312 }
313
314 return static_cast<Domain>(id);
315}
316
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317///////////////////////////////////////////////////////////////////////////////
318
robertphillips3afef1f2014-07-08 06:12:22 -0700319uint32_t SkPicture::OperationList::offset(int index) const {
320 SkASSERT(index < fOps.count());
321 return ((SkPictureStateTree::Draw*)fOps[index])->fOffset;
322}
323
324const SkMatrix& SkPicture::OperationList::matrix(int index) const {
325 SkASSERT(index < fOps.count());
326 return *((SkPictureStateTree::Draw*)fOps[index])->fMatrix;
327}
328
Mike Klein744fb732014-06-23 15:13:26 -0400329// fRecord OK
robertphillipsc5ba71d2014-09-04 08:42:50 -0700330void SkPicture::playback(SkCanvas* canvas, SkDrawPictureCallback* callback) const {
Mike Klein744fb732014-06-23 15:13:26 -0400331 SkASSERT(NULL != canvas);
robertphillipsdb539902014-07-01 08:47:04 -0700332 SkASSERT(NULL != fData.get() || NULL != fRecord.get());
Mike Klein744fb732014-06-23 15:13:26 -0400333
mtklein3e8232b2014-08-18 13:39:11 -0700334 // If the query contains the whole picture, don't bother with the BBH.
335 SkRect clipBounds = { 0, 0, 0, 0 };
336 (void)canvas->getClipBounds(&clipBounds);
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700337 const bool useBBH = !clipBounds.contains(this->cullRect());
mtklein3e8232b2014-08-18 13:39:11 -0700338
robertphillipsdb539902014-07-01 08:47:04 -0700339 if (NULL != fData.get()) {
robertphillipsce4dd3d2014-07-07 13:46:35 -0700340 SkPicturePlayback playback(this);
mtklein3e8232b2014-08-18 13:39:11 -0700341 playback.setUseBBH(useBBH);
robertphillipsce4dd3d2014-07-07 13:46:35 -0700342 playback.draw(canvas, callback);
Mike Klein744fb732014-06-23 15:13:26 -0400343 }
344 if (NULL != fRecord.get()) {
mtklein3e8232b2014-08-18 13:39:11 -0700345 SkRecordDraw(*fRecord, canvas, useBBH ? fBBH.get() : NULL, callback);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346 }
347}
348
349///////////////////////////////////////////////////////////////////////////////
350
351#include "SkStream.h"
352
rmistry@google.comd6bab022013-12-02 13:50:38 +0000353static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000354
Mike Klein744fb732014-06-23 15:13:26 -0400355// fRecord OK
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000356bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
357 if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
358 return false;
359 }
360
361 if (info.fVersion < MIN_PICTURE_VERSION ||
362 info.fVersion > CURRENT_PICTURE_VERSION) {
363 return false;
364 }
365
366 return true;
367}
rmistry@google.comd6bab022013-12-02 13:50:38 +0000368
Mike Klein744fb732014-06-23 15:13:26 -0400369// fRecord OK
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000370bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000371 if (NULL == stream) {
372 return false;
borenet@google.com66bcbd12012-09-17 18:26:06 +0000373 }
reed@google.com67562092012-06-22 15:38:39 +0000374
rmistry@google.comd6bab022013-12-02 13:50:38 +0000375 // Check magic bytes.
reed@google.com67562092012-06-22 15:38:39 +0000376 SkPictInfo info;
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000377 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700378
379 if (!stream->read(&info.fMagic, sizeof(kMagic))) {
380 return false;
381 }
382
383 info.fVersion = stream->readU32();
384
385#ifndef V35_COMPATIBILITY_CODE
386 if (info.fVersion < 35) {
387 info.fCullRect.fLeft = 0;
388 info.fCullRect.fTop = 0;
389 info.fCullRect.fRight = SkIntToScalar(stream->readU32());
390 info.fCullRect.fBottom = SkIntToScalar(stream->readU32());
391 } else {
392#endif
393 info.fCullRect.fLeft = stream->readScalar();
394 info.fCullRect.fTop = stream->readScalar();
395 info.fCullRect.fRight = stream->readScalar();
396 info.fCullRect.fBottom = stream->readScalar();
397#ifndef V35_COMPATIBILITY_CODE
398 }
399#endif
400
401 info.fFlags = stream->readU32();
402
403 if (!IsValidPictInfo(info)) {
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000404 return false;
reed@google.com67562092012-06-22 15:38:39 +0000405 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000407 if (pInfo != NULL) {
408 *pInfo = info;
409 }
410 return true;
411}
412
Mike Klein744fb732014-06-23 15:13:26 -0400413// fRecord OK
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700414bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000415 // Check magic bytes.
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000416 SkPictInfo info;
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000417 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700418
419 if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) {
420 return false;
421 }
422
423 info.fVersion = buffer->readUInt();
424
425#ifndef V35_COMPATIBILITY_CODE
426 if (info.fVersion < 35) {
427 info.fCullRect.fLeft = 0;
428 info.fCullRect.fTop = 0;
429 info.fCullRect.fRight = SkIntToScalar(buffer->readUInt());
430 info.fCullRect.fBottom = SkIntToScalar(buffer->readUInt());
431 } else {
432#endif
433 buffer->readRect(&info.fCullRect);
434#ifndef V35_COMPATIBILITY_CODE
435 }
436#endif
437
438 info.fFlags = buffer->readUInt();
439
440 if (!IsValidPictInfo(info)) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000441 return false;
442 }
443
444 if (pInfo != NULL) {
445 *pInfo = info;
446 }
447 return true;
448}
449
Mike Klein744fb732014-06-23 15:13:26 -0400450// fRecord OK
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700451SkPicture::SkPicture(SkPictureData* data, SkScalar width, SkScalar height)
robertphillipsdb539902014-07-01 08:47:04 -0700452 : fData(data)
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700453 , fCullWidth(width)
454 , fCullHeight(height)
tomhudson3a0f2792014-08-20 05:29:41 -0700455 , fAnalysis() {
robertphillips@google.comd5500882014-04-02 23:51:13 +0000456 this->needsNewGenID();
457}
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000458
mtklein7b705bb2014-08-20 14:22:58 -0700459SkPicture* SkPicture::Forwardport(const SkPicture& src) {
460 SkAutoTDelete<SkRecord> record(SkNEW(SkRecord));
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700461 SkRecorder canvas(record.get(), src.cullRect().width(), src.cullRect().height());
robertphillipsc5ba71d2014-09-04 08:42:50 -0700462 src.playback(&canvas);
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700463 return SkNEW_ARGS(SkPicture, (src.cullRect().width(), src.cullRect().height(),
464 record.detach(), NULL/*bbh*/));
mtklein7b705bb2014-08-20 14:22:58 -0700465}
466
Mike Klein744fb732014-06-23 15:13:26 -0400467// fRecord OK
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000468SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
469 SkPictInfo info;
470
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000471 if (!InternalOnly_StreamIsSKP(stream, &info)) {
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000472 return NULL;
473 }
474
scroggo@google.comf1754ec2013-06-28 21:32:00 +0000475 // Check to see if there is a playback to recreate.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476 if (stream->readBool()) {
robertphillipsdb539902014-07-01 08:47:04 -0700477 SkPictureData* data = SkPictureData::CreateFromStream(stream, info, proc);
478 if (NULL == data) {
scroggo@google.com12705322013-10-01 15:30:46 +0000479 return NULL;
480 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700481 const SkPicture src(data, info.fCullRect.width(), info.fCullRect.height());
mtklein1b523ba2014-08-20 15:50:45 -0700482 return Forwardport(src);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 }
reed@google.com67562092012-06-22 15:38:39 +0000484
robertphillipse26e65e2014-06-12 05:51:22 -0700485 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486}
487
Mike Klein744fb732014-06-23 15:13:26 -0400488// fRecord OK
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000489SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
490 SkPictInfo info;
491
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700492 if (!InternalOnly_BufferIsSKP(&buffer, &info)) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000493 return NULL;
494 }
495
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000496 // Check to see if there is a playback to recreate.
497 if (buffer.readBool()) {
robertphillipsdb539902014-07-01 08:47:04 -0700498 SkPictureData* data = SkPictureData::CreateFromBuffer(buffer, info);
499 if (NULL == data) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000500 return NULL;
501 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700502 const SkPicture src(data, info.fCullRect.width(), info.fCullRect.height());
mtklein1b523ba2014-08-20 15:50:45 -0700503 return Forwardport(src);
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000504 }
505
robertphillipse26e65e2014-06-12 05:51:22 -0700506 return NULL;
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000507}
508
Mike Klein744fb732014-06-23 15:13:26 -0400509// fRecord OK
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000510void SkPicture::createHeader(SkPictInfo* info) const {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000511 // Copy magic bytes at the beginning of the header
512 SkASSERT(sizeof(kMagic) == 8);
commit-bot@chromium.org9e5f85e2014-03-12 14:46:41 +0000513 SkASSERT(sizeof(kMagic) == sizeof(info->fMagic));
514 memcpy(info->fMagic, kMagic, sizeof(kMagic));
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000515
commit-bot@chromium.org6f4fb0f2014-03-03 19:18:39 +0000516 // Set picture info after magic bytes in the header
commit-bot@chromium.orge8d96142014-02-25 02:16:10 +0000517 info->fVersion = CURRENT_PICTURE_VERSION;
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700518 info->fCullRect = this->cullRect();
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000519 info->fFlags = SkPictInfo::kCrossProcess_Flag;
520 // TODO: remove this flag, since we're always float (now)
521 info->fFlags |= SkPictInfo::kScalarIsFloat_Flag;
522
523 if (8 == sizeof(void*)) {
524 info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
525 }
526}
527
mtklein73734562014-06-24 12:28:34 -0700528// fRecord OK
scroggo@google.com32ef1312013-02-22 22:04:19 +0000529void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
robertphillipsdb539902014-07-01 08:47:04 -0700530 const SkPictureData* data = fData.get();
mtklein73734562014-06-24 12:28:34 -0700531
532 // If we're a new-format picture, backport to old format for serialization.
533 SkAutoTDelete<SkPicture> oldFormat;
robertphillipsdb539902014-07-01 08:47:04 -0700534 if (NULL == data && NULL != fRecord.get()) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700535 oldFormat.reset(backport(*fRecord, this->cullRect()));
robertphillipsdb539902014-07-01 08:47:04 -0700536 data = oldFormat->fData.get();
537 SkASSERT(NULL != data);
mtklein73734562014-06-24 12:28:34 -0700538 }
539
commit-bot@chromium.org2ee3c2c2014-05-19 12:36:15 +0000540 SkPictInfo info;
541 this->createHeader(&info);
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700542 SkASSERT(sizeof(SkPictInfo) == 32);
commit-bot@chromium.org0943f5f2014-03-28 18:05:47 +0000543 stream->write(&info, sizeof(info));
mtklein40684ba2014-06-24 10:12:39 -0700544
robertphillipsdb539902014-07-01 08:47:04 -0700545 if (NULL != data) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 stream->writeBool(true);
robertphillipsdb539902014-07-01 08:47:04 -0700547 data->serialize(stream, encoder);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 } else {
549 stream->writeBool(false);
550 }
551}
552
Mike Klein744fb732014-06-23 15:13:26 -0400553// fRecord OK
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000554void SkPicture::flatten(SkWriteBuffer& buffer) const {
robertphillipsdb539902014-07-01 08:47:04 -0700555 const SkPictureData* data = fData.get();
mtklein73734562014-06-24 12:28:34 -0700556
557 // If we're a new-format picture, backport to old format for serialization.
558 SkAutoTDelete<SkPicture> oldFormat;
robertphillipsdb539902014-07-01 08:47:04 -0700559 if (NULL == data && NULL != fRecord.get()) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700560 oldFormat.reset(backport(*fRecord, this->cullRect()));
robertphillipsdb539902014-07-01 08:47:04 -0700561 data = oldFormat->fData.get();
562 SkASSERT(NULL != data);
mtklein73734562014-06-24 12:28:34 -0700563 }
564
commit-bot@chromium.org2ee3c2c2014-05-19 12:36:15 +0000565 SkPictInfo info;
566 this->createHeader(&info);
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700567 buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic));
568 buffer.writeUInt(info.fVersion);
569 buffer.writeRect(info.fCullRect);
570 buffer.writeUInt(info.fFlags);
mtklein40684ba2014-06-24 10:12:39 -0700571
robertphillipsdb539902014-07-01 08:47:04 -0700572 if (NULL != data) {
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000573 buffer.writeBool(true);
robertphillipsdb539902014-07-01 08:47:04 -0700574 data->flatten(buffer);
commit-bot@chromium.org5e0995e2014-02-07 12:20:04 +0000575 } else {
576 buffer.writeBool(false);
577 }
578}
579
commit-bot@chromium.org1c308182014-03-19 22:54:40 +0000580#if SK_SUPPORT_GPU
tomhudson3a0f2792014-08-20 05:29:41 -0700581// fRecord OK
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000582bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **reason) const {
tomhudson3a0f2792014-08-20 05:29:41 -0700583 if (fRecord.get()) {
584 return fAnalysis.suitableForGpuRasterization(reason, 0);
585 }
robertphillipsdb539902014-07-01 08:47:04 -0700586 if (NULL == fData.get()) {
robertphillips0bdbea72014-06-11 11:37:55 -0700587 if (NULL != reason) {
robertphillipsdb539902014-07-01 08:47:04 -0700588 *reason = "Missing internal data.";
robertphillips0bdbea72014-06-11 11:37:55 -0700589 }
590 return false;
commit-bot@chromium.orga1ff26a2014-05-30 21:52:52 +0000591 }
robertphillips0bdbea72014-06-11 11:37:55 -0700592
robertphillipsdb539902014-07-01 08:47:04 -0700593 return fData->suitableForGpuRasterization(context, reason);
commit-bot@chromium.orgeb9547c2014-03-19 21:24:25 +0000594}
commit-bot@chromium.org1c308182014-03-19 22:54:40 +0000595#endif
commit-bot@chromium.orgeb9547c2014-03-19 21:24:25 +0000596
mtkleinc551d9f2014-08-20 08:09:46 -0700597// fRecord OK
ajuma750ae262014-08-18 12:59:55 -0700598bool SkPicture::hasText() const {
mtkleinc551d9f2014-08-20 08:09:46 -0700599 if (fRecord.get()) {
600 return fAnalysis.fHasText;
601 }
602 if (fData.get()) {
603 return fData->hasText();
604 }
605 SkFAIL("Unreachable");
606 return false;
ajuma750ae262014-08-18 12:59:55 -0700607}
608
tomhudsond9305112014-07-05 13:37:53 -0700609// fRecord OK
tomhudson@google.com381010e2013-10-24 11:12:47 +0000610bool SkPicture::willPlayBackBitmaps() const {
tomhudsond9305112014-07-05 13:37:53 -0700611 if (fRecord.get()) {
tomhudson3a0f2792014-08-20 05:29:41 -0700612 return fAnalysis.fWillPlaybackBitmaps;
tomhudsond9305112014-07-05 13:37:53 -0700613 }
mtkleinc551d9f2014-08-20 08:09:46 -0700614 if (fData.get()) {
615 return fData->containsBitmaps();
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000616 }
mtkleinc551d9f2014-08-20 08:09:46 -0700617 SkFAIL("Unreachable");
618 return false;
tomhudson@google.com381010e2013-10-24 11:12:47 +0000619}
620
Mike Klein744fb732014-06-23 15:13:26 -0400621// fRecord OK
robertphillips@google.comd5500882014-04-02 23:51:13 +0000622static int32_t next_picture_generation_id() {
623 static int32_t gPictureGenerationID = 0;
624 // do a loop in case our global wraps around, as we never want to
625 // return a 0
626 int32_t genID;
627 do {
628 genID = sk_atomic_inc(&gPictureGenerationID) + 1;
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000629 } while (SK_InvalidGenID == genID);
robertphillips@google.comd5500882014-04-02 23:51:13 +0000630 return genID;
631}
632
Mike Klein744fb732014-06-23 15:13:26 -0400633// fRecord OK
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000634uint32_t SkPicture::uniqueID() const {
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000635 if (SK_InvalidGenID == fUniqueID) {
636 fUniqueID = next_picture_generation_id();
robertphillips@google.comd5500882014-04-02 23:51:13 +0000637 }
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +0000638 return fUniqueID;
robertphillips@google.comd5500882014-04-02 23:51:13 +0000639}
Mike Klein744fb732014-06-23 15:13:26 -0400640
641// fRecord OK
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700642SkPicture::SkPicture(SkScalar width, SkScalar height, SkRecord* record, SkBBoxHierarchy* bbh)
643 : fCullWidth(width)
644 , fCullHeight(height)
tomhudsond9305112014-07-05 13:37:53 -0700645 , fRecord(record)
mtklein5ad6ee12014-08-11 08:08:43 -0700646 , fBBH(SkSafeRef(bbh))
mtkleinc551d9f2014-08-20 08:09:46 -0700647 , fAnalysis(*record) {
mtklein5ad6ee12014-08-11 08:08:43 -0700648 // TODO: delay as much of this work until just before first playback?
649 if (fBBH.get()) {
650 SkRecordFillBounds(*record, fBBH.get());
651 }
Mike Klein744fb732014-06-23 15:13:26 -0400652 this->needsNewGenID();
653}
robertphillipsd771f6b2014-07-22 10:18:06 -0700654
655// Note that we are assuming that this entry point will only be called from
mtklein3ba15ae2014-08-06 11:57:00 -0700656// one thread. Currently the only client of this method is
robertphillipsd771f6b2014-07-22 10:18:06 -0700657// SkGpuDevice::EXPERIMENTAL_optimize which should be only called from a single
658// thread.
659void SkPicture::addDeletionListener(DeletionListener* listener) const {
660 SkASSERT(NULL != listener);
661
662 *fDeletionListeners.append() = SkRef(listener);
663}
664
665void SkPicture::callDeletionListeners() {
666 for (int i = 0; i < fDeletionListeners.count(); ++i) {
667 fDeletionListeners[i]->onDeletion(this->uniqueID());
668 }
669
670 fDeletionListeners.unrefAll();
671}
mtklein5a246bb2014-08-14 19:17:18 -0700672
673// fRecord OK
674int SkPicture::approximateOpCount() const {
675 SkASSERT(fRecord.get() || fData.get());
676 if (fRecord.get()) {
677 return fRecord->count();
678 }
679 if (fData.get()) {
680 return fData->opCount();
681 }
682 return 0;
683}