[go: nahoru, domu]

blob: 410952479bb9460f95759a18d9c84f8c1c875239 [file] [log] [blame]
epoger@google.com685cfc02011-07-28 14:26:00 +00001
reed@android.combcd4d5a2008-12-17 15:59:43 +00002/*
epoger@google.com685cfc02011-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
reed@android.combcd4d5a2008-12-17 15:59:43 +00009
10#include "SkPictureFlat.h"
11#include "SkPicturePlayback.h"
12#include "SkPictureRecord.h"
13
14#include "SkCanvas.h"
15#include "SkChunkAlloc.h"
reed@google.com118a6d02012-09-27 20:31:31 +000016#include "SkDevice.h"
reed@android.combcd4d5a2008-12-17 15:59:43 +000017#include "SkPicture.h"
18#include "SkRegion.h"
19#include "SkStream.h"
20#include "SkTDArray.h"
21#include "SkTSearch.h"
22#include "SkTime.h"
23
24#include "SkReader32.h"
25#include "SkWriter32.h"
rileya@google.com7fb3d2f2012-09-13 21:41:51 +000026#include "SkRTree.h"
27#include "SkBBoxHierarchyRecord.h"
reed@android.combcd4d5a2008-12-17 15:59:43 +000028
robertphillips@google.com86dbfc42012-06-21 20:25:03 +000029SK_DEFINE_INST_COUNT(SkPicture)
30
reed@android.combcd4d5a2008-12-17 15:59:43 +000031#define DUMP_BUFFER_SIZE 65536
32
33//#define ENABLE_TIME_DRAW // dumps milliseconds for each draw
34
35
36#ifdef SK_DEBUG
reed@google.com91911482011-02-07 15:30:46 +000037// enable SK_DEBUG_TRACE to trace DrawType elements when
reed@android.combcd4d5a2008-12-17 15:59:43 +000038// recorded and played back
39// #define SK_DEBUG_TRACE
40// enable SK_DEBUG_SIZE to see the size of picture components
41// #define SK_DEBUG_SIZE
42// enable SK_DEBUG_DUMP to see the contents of recorded elements
43// #define SK_DEBUG_DUMP
44// enable SK_DEBUG_VALIDATE to check internal structures for consistency
45// #define SK_DEBUG_VALIDATE
46#endif
47
48#if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP
49const char* DrawTypeToString(DrawType drawType) {
50 switch (drawType) {
51 case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break;
52 case CLIP_PATH: return "CLIP_PATH";
53 case CLIP_REGION: return "CLIP_REGION";
54 case CLIP_RECT: return "CLIP_RECT";
55 case CONCAT: return "CONCAT";
56 case DRAW_BITMAP: return "DRAW_BITMAP";
57 case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX";
58 case DRAW_BITMAP_RECT: return "DRAW_BITMAP_RECT";
59 case DRAW_PAINT: return "DRAW_PAINT";
60 case DRAW_PATH: return "DRAW_PATH";
61 case DRAW_PICTURE: return "DRAW_PICTURE";
62 case DRAW_POINTS: return "DRAW_POINTS";
63 case DRAW_POS_TEXT: return "DRAW_POS_TEXT";
64 case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H";
65 case DRAW_RECT_GENERAL: return "DRAW_RECT_GENERAL";
66 case DRAW_RECT_SIMPLE: return "DRAW_RECT_SIMPLE";
67 case DRAW_SPRITE: return "DRAW_SPRITE";
68 case DRAW_TEXT: return "DRAW_TEXT";
69 case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH";
70 case RESTORE: return "RESTORE";
71 case ROTATE: return "ROTATE";
72 case SAVE: return "SAVE";
73 case SAVE_LAYER: return "SAVE_LAYER";
74 case SCALE: return "SCALE";
75 case SKEW: return "SKEW";
76 case TRANSLATE: return "TRANSLATE";
reed@google.com91911482011-02-07 15:30:46 +000077 default:
78 SkDebugf("DrawType error 0x%08x\n", drawType);
79 SkASSERT(0);
reed@android.combcd4d5a2008-12-17 15:59:43 +000080 break;
81 }
reed@google.com91911482011-02-07 15:30:46 +000082 SkASSERT(0);
reed@android.combcd4d5a2008-12-17 15:59:43 +000083 return NULL;
84}
85#endif
86
87#ifdef SK_DEBUG_VALIDATE
88static void validateMatrix(const SkMatrix* matrix) {
89 SkScalar scaleX = matrix->getScaleX();
90 SkScalar scaleY = matrix->getScaleY();
91 SkScalar skewX = matrix->getSkewX();
92 SkScalar skewY = matrix->getSkewY();
93 SkScalar perspX = matrix->getPerspX();
94 SkScalar perspY = matrix->getPerspY();
95 if (scaleX != 0 && skewX != 0)
96 SkDebugf("scaleX != 0 && skewX != 0\n");
97 SkASSERT(scaleX == 0 || skewX == 0);
98 SkASSERT(scaleY == 0 || skewY == 0);
99 SkASSERT(perspX == 0);
100 SkASSERT(perspY == 0);
101}
102#endif
103
104
105///////////////////////////////////////////////////////////////////////////////
106
107SkPicture::SkPicture() {
108 fRecord = NULL;
109 fPlayback = NULL;
110 fWidth = fHeight = 0;
111}
112
113SkPicture::SkPicture(const SkPicture& src) : SkRefCnt() {
114 fWidth = src.fWidth;
115 fHeight = src.fHeight;
116 fRecord = NULL;
117
118 /* We want to copy the src's playback. However, if that hasn't been built
119 yet, we need to fake a call to endRecording() without actually calling
120 it (since it is destructive, and we don't want to change src).
121 */
122 if (src.fPlayback) {
123 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback));
124 } else if (src.fRecord) {
125 // here we do a fake src.endRecording()
126 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fRecord));
127 } else {
128 fPlayback = NULL;
129 }
130}
131
132SkPicture::~SkPicture() {
reed@google.com91911482011-02-07 15:30:46 +0000133 SkSafeUnref(fRecord);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000134 SkDELETE(fPlayback);
135}
136
137void SkPicture::swap(SkPicture& other) {
138 SkTSwap(fRecord, other.fRecord);
139 SkTSwap(fPlayback, other.fPlayback);
140 SkTSwap(fWidth, other.fWidth);
141 SkTSwap(fHeight, other.fHeight);
142}
143
djsollen@google.com3855f872012-08-29 18:52:07 +0000144SkPicture* SkPicture::clone() const {
145 SkPicture* clonedPicture = SkNEW(SkPicture);
146 clone(clonedPicture, 1);
147 return clonedPicture;
148}
149
150void SkPicture::clone(SkPicture* pictures, int count) const {
151 SkPictCopyInfo copyInfo;
152
153 for (int i = 0; i < count; i++) {
154 SkPicture* clone = &pictures[i];
155
156 clone->fWidth = fWidth;
157 clone->fHeight = fHeight;
158 clone->fRecord = NULL;
159
160 /* We want to copy the src's playback. However, if that hasn't been built
161 yet, we need to fake a call to endRecording() without actually calling
162 it (since it is destructive, and we don't want to change src).
163 */
164 if (fPlayback) {
165 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fPlayback, &copyInfo));
166 } else if (fRecord) {
167 // here we do a fake src.endRecording()
168 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord, true));
169 } else {
170 clone->fPlayback = NULL;
171 }
172 }
173}
174
reed@android.combcd4d5a2008-12-17 15:59:43 +0000175///////////////////////////////////////////////////////////////////////////////
176
reed@android.comc4a55d72009-02-13 14:56:09 +0000177SkCanvas* SkPicture::beginRecording(int width, int height,
178 uint32_t recordingFlags) {
reed@android.combcd4d5a2008-12-17 15:59:43 +0000179 if (fPlayback) {
180 SkDELETE(fPlayback);
181 fPlayback = NULL;
182 }
183
184 if (NULL != fRecord) {
185 fRecord->unref();
186 fRecord = NULL;
187 }
188
reed@google.com118a6d02012-09-27 20:31:31 +0000189 SkBitmap bm;
190 bm.setConfig(SkBitmap::kNo_Config, width, height);
191 SkAutoTUnref<SkDevice> dev(SkNEW_ARGS(SkDevice, (bm)));
192
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000193 // Must be set before calling createBBoxHierarchy
194 fWidth = width;
195 fHeight = height;
196
rileya@google.com7fb3d2f2012-09-13 21:41:51 +0000197 if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) {
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000198 SkBBoxHierarchy* tree = this->createBBoxHierarchy();
rileya@google.com7fb3d2f2012-09-13 21:41:51 +0000199 SkASSERT(NULL != tree);
reed@google.com118a6d02012-09-27 20:31:31 +0000200 fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (recordingFlags, tree, dev));
rileya@google.com7fb3d2f2012-09-13 21:41:51 +0000201 tree->unref();
202 } else {
reed@google.com118a6d02012-09-27 20:31:31 +0000203 fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags, dev));
rileya@google.com7fb3d2f2012-09-13 21:41:51 +0000204 }
reed@google.com118a6d02012-09-27 20:31:31 +0000205 fRecord->beginRecording();
reed@android.combcd4d5a2008-12-17 15:59:43 +0000206
reed@android.combcd4d5a2008-12-17 15:59:43 +0000207 return fRecord;
208}
209
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000210SkBBoxHierarchy* SkPicture::createBBoxHierarchy() const {
skia.committer@gmail.com8d230dd2012-11-02 02:01:24 +0000211 // These values were empirically determined to produce reasonable
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000212 // performance in most cases.
213 static const int kRTreeMinChildren = 6;
214 static const int kRTreeMaxChildren = 11;
215
216 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
217 SkIntToScalar(fHeight));
218 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
219 aspectRatio);
220}
221
reed@android.combcd4d5a2008-12-17 15:59:43 +0000222SkCanvas* SkPicture::getRecordingCanvas() const {
223 // will be null if we are not recording
224 return fRecord;
225}
226
227void SkPicture::endRecording() {
228 if (NULL == fPlayback) {
229 if (NULL != fRecord) {
junov@chromium.org9aa18db2012-07-12 17:47:34 +0000230 fRecord->endRecording();
reed@android.combcd4d5a2008-12-17 15:59:43 +0000231 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
232 fRecord->unref();
233 fRecord = NULL;
234 }
235 }
236 SkASSERT(NULL == fRecord);
237}
238
239void SkPicture::draw(SkCanvas* surface) {
240 this->endRecording();
241 if (fPlayback) {
242 fPlayback->draw(*surface);
243 }
244}
245
246///////////////////////////////////////////////////////////////////////////////
247
248#include "SkStream.h"
249
reed@google.comc162a862012-06-22 13:12:17 +0000250// V2 : adds SkPixelRef's generation ID.
251// V3 : PictInfo tag at beginning, and EOF tag at the end
reed@google.com4bca7f62012-06-22 15:38:39 +0000252// V4 : move SkPictInfo to be the header
reed@google.com481fc1f2012-06-25 14:36:28 +0000253// V5 : don't read/write FunctionPtr on cross-process (we can detect that)
robertphillips@google.com18163cc2012-08-17 10:58:49 +0000254// V6 : added serialization of SkPath's bounds (and packed its flags tighter)
reed@google.com3b4aeae2012-09-18 15:14:33 +0000255// V7 : changed drawBitmapRect(IRect) to drawBitmapRectToRect(Rect)
scroggo@google.com92e06142012-10-09 20:02:20 +0000256// V8 : Add an option for encoding bitmaps
257// V9 : Allow the reader and writer of an SKP disagree on whether to support
258// SK_SUPPORT_HINTING_SCALE_FACTOR
259#define PICTURE_VERSION 9
reed@android.combcd4d5a2008-12-17 15:59:43 +0000260
scroggo@google.com6ff73172012-10-04 21:46:08 +0000261SkPicture::SkPicture(SkStream* stream, bool* success, SkSerializationHelpers::DecodeBitmap decoder) : SkRefCnt() {
borenet@google.com30dda202012-09-17 18:26:06 +0000262 if (success) {
263 *success = false;
264 }
reed@android.combcd4d5a2008-12-17 15:59:43 +0000265 fRecord = NULL;
266 fPlayback = NULL;
reed@google.com4bca7f62012-06-22 15:38:39 +0000267 fWidth = fHeight = 0;
268
269 SkPictInfo info;
270
271 if (!stream->read(&info, sizeof(info))) {
272 return;
273 }
274 if (PICTURE_VERSION != info.fVersion) {
275 return;
276 }
reed@android.combcd4d5a2008-12-17 15:59:43 +0000277
278 if (stream->readBool()) {
reed@google.com4bca7f62012-06-22 15:38:39 +0000279 bool isValid = false;
scroggo@google.com6ff73172012-10-04 21:46:08 +0000280 fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream, info, &isValid, decoder));
reed@google.com4bca7f62012-06-22 15:38:39 +0000281 if (!isValid) {
282 SkDELETE(fPlayback);
283 fPlayback = NULL;
284 return;
285 }
reed@android.combcd4d5a2008-12-17 15:59:43 +0000286 }
reed@google.com4bca7f62012-06-22 15:38:39 +0000287
288 // do this at the end, so that they will be zero if we hit an error.
289 fWidth = info.fWidth;
290 fHeight = info.fHeight;
borenet@google.com30dda202012-09-17 18:26:06 +0000291 if (success) {
292 *success = true;
293 }
reed@android.combcd4d5a2008-12-17 15:59:43 +0000294}
295
scroggo@google.com6ff73172012-10-04 21:46:08 +0000296void SkPicture::serialize(SkWStream* stream, SkSerializationHelpers::EncodeBitmap encoder) const {
reed@android.combcd4d5a2008-12-17 15:59:43 +0000297 SkPicturePlayback* playback = fPlayback;
reed@google.com91911482011-02-07 15:30:46 +0000298
reed@android.combcd4d5a2008-12-17 15:59:43 +0000299 if (NULL == playback && fRecord) {
300 playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
301 }
302
reed@google.com4bca7f62012-06-22 15:38:39 +0000303 SkPictInfo info;
304
305 info.fVersion = PICTURE_VERSION;
306 info.fWidth = fWidth;
307 info.fHeight = fHeight;
308 info.fFlags = SkPictInfo::kCrossProcess_Flag;
309#ifdef SK_SCALAR_IS_FLOAT
310 info.fFlags |= SkPictInfo::kScalarIsFloat_Flag;
311#endif
312 if (8 == sizeof(void*)) {
313 info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
314 }
315
316 stream->write(&info, sizeof(info));
reed@android.combcd4d5a2008-12-17 15:59:43 +0000317 if (playback) {
318 stream->writeBool(true);
scroggo@google.com6ff73172012-10-04 21:46:08 +0000319 playback->serialize(stream, encoder);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000320 // delete playback if it is a local version (i.e. cons'd up just now)
321 if (playback != fPlayback) {
322 SkDELETE(playback);
323 }
324 } else {
325 stream->writeBool(false);
326 }
327}
328
329void SkPicture::abortPlayback() {
330 if (NULL == fPlayback) {
331 return;
332 }
333 fPlayback->abort();
334}
335
336