[go: nahoru, domu]

blob: 1ff58656512681ed5ac056d1399f9558c33f0c91 [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
scroggo@google.com8b854502013-05-07 14:45:40 +00009
reed@android.combcd4d5a2008-12-17 15:59:43 +000010#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";
robertphillips@google.come7189be2013-02-15 17:19:15 +000055 case CLIP_RRECT: return "CLIP_RRECT";
reed@android.combcd4d5a2008-12-17 15:59:43 +000056 case CONCAT: return "CONCAT";
57 case DRAW_BITMAP: return "DRAW_BITMAP";
58 case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX";
robertphillips@google.come7189be2013-02-15 17:19:15 +000059 case DRAW_BITMAP_NINE: return "DRAW_BITMAP_NINE";
60 case DRAW_BITMAP_RECT_TO_RECT: return "DRAW_BITMAP_RECT_TO_RECT";
61 case DRAW_CLEAR: return "DRAW_CLEAR";
62 case DRAW_DATA: return "DRAW_DATA";
63 case DRAW_OVAL: return "DRAW_OVAL";
reed@android.combcd4d5a2008-12-17 15:59:43 +000064 case DRAW_PAINT: return "DRAW_PAINT";
65 case DRAW_PATH: return "DRAW_PATH";
66 case DRAW_PICTURE: return "DRAW_PICTURE";
67 case DRAW_POINTS: return "DRAW_POINTS";
68 case DRAW_POS_TEXT: return "DRAW_POS_TEXT";
robertphillips@google.come7189be2013-02-15 17:19:15 +000069 case DRAW_POS_TEXT_TOP_BOTTOM: return "DRAW_POS_TEXT_TOP_BOTTOM";
reed@android.combcd4d5a2008-12-17 15:59:43 +000070 case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H";
robertphillips@google.come7189be2013-02-15 17:19:15 +000071 case DRAW_POS_TEXT_H_TOP_BOTTOM: return "DRAW_POS_TEXT_H_TOP_BOTTOM";
72 case DRAW_RECT: return "DRAW_RECT";
73 case DRAW_RRECT: return "DRAW_RRECT";
reed@android.combcd4d5a2008-12-17 15:59:43 +000074 case DRAW_SPRITE: return "DRAW_SPRITE";
75 case DRAW_TEXT: return "DRAW_TEXT";
76 case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH";
robertphillips@google.come7189be2013-02-15 17:19:15 +000077 case DRAW_TEXT_TOP_BOTTOM: return "DRAW_TEXT_TOP_BOTTOM";
78 case DRAW_VERTICES: return "DRAW_VERTICES";
reed@android.combcd4d5a2008-12-17 15:59:43 +000079 case RESTORE: return "RESTORE";
80 case ROTATE: return "ROTATE";
81 case SAVE: return "SAVE";
82 case SAVE_LAYER: return "SAVE_LAYER";
83 case SCALE: return "SCALE";
robertphillips@google.come7189be2013-02-15 17:19:15 +000084 case SET_MATRIX: return "SET_MATRIX";
reed@android.combcd4d5a2008-12-17 15:59:43 +000085 case SKEW: return "SKEW";
86 case TRANSLATE: return "TRANSLATE";
robertphillips@google.come7189be2013-02-15 17:19:15 +000087 case NOOP: return "NOOP";
reed@google.com91911482011-02-07 15:30:46 +000088 default:
89 SkDebugf("DrawType error 0x%08x\n", drawType);
90 SkASSERT(0);
reed@android.combcd4d5a2008-12-17 15:59:43 +000091 break;
92 }
reed@google.com91911482011-02-07 15:30:46 +000093 SkASSERT(0);
reed@android.combcd4d5a2008-12-17 15:59:43 +000094 return NULL;
95}
96#endif
97
98#ifdef SK_DEBUG_VALIDATE
99static void validateMatrix(const SkMatrix* matrix) {
100 SkScalar scaleX = matrix->getScaleX();
101 SkScalar scaleY = matrix->getScaleY();
102 SkScalar skewX = matrix->getSkewX();
103 SkScalar skewY = matrix->getSkewY();
104 SkScalar perspX = matrix->getPerspX();
105 SkScalar perspY = matrix->getPerspY();
106 if (scaleX != 0 && skewX != 0)
107 SkDebugf("scaleX != 0 && skewX != 0\n");
108 SkASSERT(scaleX == 0 || skewX == 0);
109 SkASSERT(scaleY == 0 || skewY == 0);
110 SkASSERT(perspX == 0);
111 SkASSERT(perspY == 0);
112}
113#endif
114
115
116///////////////////////////////////////////////////////////////////////////////
117
118SkPicture::SkPicture() {
119 fRecord = NULL;
120 fPlayback = NULL;
121 fWidth = fHeight = 0;
122}
123
scroggo@google.comde5698f2013-02-22 21:38:35 +0000124SkPicture::SkPicture(const SkPicture& src) {
reed@android.combcd4d5a2008-12-17 15:59:43 +0000125 fWidth = src.fWidth;
126 fHeight = src.fHeight;
127 fRecord = NULL;
128
129 /* We want to copy the src's playback. However, if that hasn't been built
130 yet, we need to fake a call to endRecording() without actually calling
131 it (since it is destructive, and we don't want to change src).
132 */
133 if (src.fPlayback) {
134 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback));
135 } else if (src.fRecord) {
136 // here we do a fake src.endRecording()
137 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fRecord));
138 } else {
139 fPlayback = NULL;
140 }
141}
142
143SkPicture::~SkPicture() {
reed@google.com91911482011-02-07 15:30:46 +0000144 SkSafeUnref(fRecord);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000145 SkDELETE(fPlayback);
146}
147
148void SkPicture::swap(SkPicture& other) {
149 SkTSwap(fRecord, other.fRecord);
150 SkTSwap(fPlayback, other.fPlayback);
151 SkTSwap(fWidth, other.fWidth);
152 SkTSwap(fHeight, other.fHeight);
153}
154
djsollen@google.com3855f872012-08-29 18:52:07 +0000155SkPicture* SkPicture::clone() const {
156 SkPicture* clonedPicture = SkNEW(SkPicture);
157 clone(clonedPicture, 1);
158 return clonedPicture;
159}
160
161void SkPicture::clone(SkPicture* pictures, int count) const {
162 SkPictCopyInfo copyInfo;
163
164 for (int i = 0; i < count; i++) {
165 SkPicture* clone = &pictures[i];
166
167 clone->fWidth = fWidth;
168 clone->fHeight = fHeight;
169 clone->fRecord = NULL;
170
scroggo@google.coma0dd4332012-11-02 20:51:19 +0000171 if (NULL != clone->fRecord) {
172 clone->fRecord->unref();
173 clone->fRecord = NULL;
174 }
175 SkDELETE(clone->fPlayback);
176
djsollen@google.com3855f872012-08-29 18:52:07 +0000177 /* We want to copy the src's playback. However, if that hasn't been built
178 yet, we need to fake a call to endRecording() without actually calling
179 it (since it is destructive, and we don't want to change src).
180 */
181 if (fPlayback) {
182 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fPlayback, &copyInfo));
183 } else if (fRecord) {
184 // here we do a fake src.endRecording()
185 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord, true));
186 } else {
187 clone->fPlayback = NULL;
188 }
189 }
190}
191
reed@android.combcd4d5a2008-12-17 15:59:43 +0000192///////////////////////////////////////////////////////////////////////////////
193
reed@android.comc4a55d72009-02-13 14:56:09 +0000194SkCanvas* SkPicture::beginRecording(int width, int height,
195 uint32_t recordingFlags) {
reed@android.combcd4d5a2008-12-17 15:59:43 +0000196 if (fPlayback) {
197 SkDELETE(fPlayback);
198 fPlayback = NULL;
199 }
200
201 if (NULL != fRecord) {
202 fRecord->unref();
203 fRecord = NULL;
204 }
205
reed@google.com118a6d02012-09-27 20:31:31 +0000206 SkBitmap bm;
207 bm.setConfig(SkBitmap::kNo_Config, width, height);
208 SkAutoTUnref<SkDevice> dev(SkNEW_ARGS(SkDevice, (bm)));
209
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000210 // Must be set before calling createBBoxHierarchy
211 fWidth = width;
212 fHeight = height;
213
rileya@google.com7fb3d2f2012-09-13 21:41:51 +0000214 if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) {
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000215 SkBBoxHierarchy* tree = this->createBBoxHierarchy();
rileya@google.com7fb3d2f2012-09-13 21:41:51 +0000216 SkASSERT(NULL != tree);
reed@google.com118a6d02012-09-27 20:31:31 +0000217 fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (recordingFlags, tree, dev));
rileya@google.com7fb3d2f2012-09-13 21:41:51 +0000218 tree->unref();
219 } else {
reed@google.com118a6d02012-09-27 20:31:31 +0000220 fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags, dev));
rileya@google.com7fb3d2f2012-09-13 21:41:51 +0000221 }
reed@google.com118a6d02012-09-27 20:31:31 +0000222 fRecord->beginRecording();
reed@android.combcd4d5a2008-12-17 15:59:43 +0000223
reed@android.combcd4d5a2008-12-17 15:59:43 +0000224 return fRecord;
225}
226
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000227SkBBoxHierarchy* SkPicture::createBBoxHierarchy() const {
skia.committer@gmail.com8d230dd2012-11-02 02:01:24 +0000228 // These values were empirically determined to produce reasonable
junov@chromium.orgbb78c442012-11-01 17:10:32 +0000229 // performance in most cases.
230 static const int kRTreeMinChildren = 6;
231 static const int kRTreeMaxChildren = 11;
232
233 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
234 SkIntToScalar(fHeight));
235 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
236 aspectRatio);
237}
238
reed@android.combcd4d5a2008-12-17 15:59:43 +0000239SkCanvas* SkPicture::getRecordingCanvas() const {
240 // will be null if we are not recording
241 return fRecord;
242}
243
244void SkPicture::endRecording() {
245 if (NULL == fPlayback) {
246 if (NULL != fRecord) {
junov@chromium.org9aa18db2012-07-12 17:47:34 +0000247 fRecord->endRecording();
reed@android.combcd4d5a2008-12-17 15:59:43 +0000248 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
249 fRecord->unref();
250 fRecord = NULL;
251 }
252 }
253 SkASSERT(NULL == fRecord);
254}
255
256void SkPicture::draw(SkCanvas* surface) {
257 this->endRecording();
258 if (fPlayback) {
259 fPlayback->draw(*surface);
260 }
261}
262
263///////////////////////////////////////////////////////////////////////////////
264
265#include "SkStream.h"
266
scroggo@google.comde5698f2013-02-22 21:38:35 +0000267SkPicture::SkPicture(SkStream* stream) {
268 this->initFromStream(stream, NULL, NULL);
269}
270
271SkPicture::SkPicture(SkStream* stream, bool* success, InstallPixelRefProc proc) {
272 this->initFromStream(stream, success, proc);
273}
274
275void SkPicture::initFromStream(SkStream* stream, bool* success, InstallPixelRefProc proc) {
borenet@google.com30dda202012-09-17 18:26:06 +0000276 if (success) {
277 *success = false;
278 }
reed@android.combcd4d5a2008-12-17 15:59:43 +0000279 fRecord = NULL;
280 fPlayback = NULL;
reed@google.com4bca7f62012-06-22 15:38:39 +0000281 fWidth = fHeight = 0;
282
283 SkPictInfo info;
284
285 if (!stream->read(&info, sizeof(info))) {
286 return;
287 }
scroggo@google.com8b854502013-05-07 14:45:40 +0000288 if (PICTURE_VERSION != info.fVersion) {
reed@google.com4bca7f62012-06-22 15:38:39 +0000289 return;
290 }
reed@android.combcd4d5a2008-12-17 15:59:43 +0000291
292 if (stream->readBool()) {
scroggo@google.com906004e2013-02-25 16:05:00 +0000293 fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream, info, proc));
reed@android.combcd4d5a2008-12-17 15:59:43 +0000294 }
reed@google.com4bca7f62012-06-22 15:38:39 +0000295
296 // do this at the end, so that they will be zero if we hit an error.
297 fWidth = info.fWidth;
298 fHeight = info.fHeight;
borenet@google.com30dda202012-09-17 18:26:06 +0000299 if (success) {
300 *success = true;
301 }
reed@android.combcd4d5a2008-12-17 15:59:43 +0000302}
303
scroggo@google.com2e727312013-02-22 22:04:19 +0000304void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
reed@android.combcd4d5a2008-12-17 15:59:43 +0000305 SkPicturePlayback* playback = fPlayback;
reed@google.com91911482011-02-07 15:30:46 +0000306
reed@android.combcd4d5a2008-12-17 15:59:43 +0000307 if (NULL == playback && fRecord) {
308 playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
309 }
310
reed@google.com4bca7f62012-06-22 15:38:39 +0000311 SkPictInfo info;
312
313 info.fVersion = PICTURE_VERSION;
314 info.fWidth = fWidth;
315 info.fHeight = fHeight;
316 info.fFlags = SkPictInfo::kCrossProcess_Flag;
317#ifdef SK_SCALAR_IS_FLOAT
318 info.fFlags |= SkPictInfo::kScalarIsFloat_Flag;
319#endif
320 if (8 == sizeof(void*)) {
321 info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
322 }
323
324 stream->write(&info, sizeof(info));
reed@android.combcd4d5a2008-12-17 15:59:43 +0000325 if (playback) {
326 stream->writeBool(true);
scroggo@google.com6ff73172012-10-04 21:46:08 +0000327 playback->serialize(stream, encoder);
reed@android.combcd4d5a2008-12-17 15:59:43 +0000328 // delete playback if it is a local version (i.e. cons'd up just now)
329 if (playback != fPlayback) {
330 SkDELETE(playback);
331 }
332 } else {
333 stream->writeBool(false);
334 }
335}
336
djsollen@google.com6789a342013-02-01 16:18:09 +0000337#ifdef SK_BUILD_FOR_ANDROID
reed@android.combcd4d5a2008-12-17 15:59:43 +0000338void SkPicture::abortPlayback() {
339 if (NULL == fPlayback) {
340 return;
341 }
342 fPlayback->abort();
343}
djsollen@google.com6789a342013-02-01 16:18:09 +0000344#endif