1
2 /*
3 * 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
9
10 #include "SkPictureFlat.h"
11 #include "SkPicturePlayback.h"
12 #include "SkPictureRecord.h"
13
14 #include "SkCanvas.h"
15 #include "SkChunkAlloc.h"
16 #include "SkDevice.h"
17 #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"
26 #include "SkRTree.h"
27 #include "SkBBoxHierarchyRecord.h"
28
SK_DEFINE_INST_COUNT(SkPicture) const29 SK_DEFINE_INST_COUNT(SkPicture)
30
31 #define DUMP_BUFFER_SIZE 65536
32
33 //#define ENABLE_TIME_DRAW // dumps milliseconds for each draw
34
35
36 #ifdef SK_DEBUG
37 // enable SK_DEBUG_TRACE to trace DrawType elements when
38 // 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
49 const 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";
77 default:
78 SkDebugf("DrawType error 0x%08x\n", drawType);
79 SkASSERT(0);
80 break;
81 }
82 SkASSERT(0);
83 return NULL;
84 }
85 #endif
86
87 #ifdef SK_DEBUG_VALIDATE
validateMatrix(const SkMatrix * matrix)88 static 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
SkPicture()107 SkPicture::SkPicture() {
108 fRecord = NULL;
109 fPlayback = NULL;
110 fWidth = fHeight = 0;
111 }
112
SkPicture(const SkPicture & src)113 SkPicture::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
~SkPicture()132 SkPicture::~SkPicture() {
133 SkSafeUnref(fRecord);
134 SkDELETE(fPlayback);
135 }
136
swap(SkPicture & other)137 void 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
clone() const144 SkPicture* SkPicture::clone() const {
145 SkPicture* clonedPicture = SkNEW(SkPicture);
146 clone(clonedPicture, 1);
147 return clonedPicture;
148 }
149
clone(SkPicture * pictures,int count) const150 void 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 if (NULL != clone->fRecord) {
161 clone->fRecord->unref();
162 clone->fRecord = NULL;
163 }
164 SkDELETE(clone->fPlayback);
165
166 /* We want to copy the src's playback. However, if that hasn't been built
167 yet, we need to fake a call to endRecording() without actually calling
168 it (since it is destructive, and we don't want to change src).
169 */
170 if (fPlayback) {
171 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fPlayback, ©Info));
172 } else if (fRecord) {
173 // here we do a fake src.endRecording()
174 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord, true));
175 } else {
176 clone->fPlayback = NULL;
177 }
178 }
179 }
180
181 ///////////////////////////////////////////////////////////////////////////////
182
beginRecording(int width,int height,uint32_t recordingFlags)183 SkCanvas* SkPicture::beginRecording(int width, int height,
184 uint32_t recordingFlags) {
185 if (fPlayback) {
186 SkDELETE(fPlayback);
187 fPlayback = NULL;
188 }
189
190 if (NULL != fRecord) {
191 fRecord->unref();
192 fRecord = NULL;
193 }
194
195 SkBitmap bm;
196 bm.setConfig(SkBitmap::kNo_Config, width, height);
197 SkAutoTUnref<SkDevice> dev(SkNEW_ARGS(SkDevice, (bm)));
198
199 // Must be set before calling createBBoxHierarchy
200 fWidth = width;
201 fHeight = height;
202
203 if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) {
204 SkBBoxHierarchy* tree = this->createBBoxHierarchy();
205 SkASSERT(NULL != tree);
206 fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (recordingFlags, tree, dev));
207 tree->unref();
208 } else {
209 fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags, dev));
210 }
211 fRecord->beginRecording();
212
213 return fRecord;
214 }
215
createBBoxHierarchy() const216 SkBBoxHierarchy* SkPicture::createBBoxHierarchy() const {
217 // These values were empirically determined to produce reasonable
218 // performance in most cases.
219 static const int kRTreeMinChildren = 6;
220 static const int kRTreeMaxChildren = 11;
221
222 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
223 SkIntToScalar(fHeight));
224 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
225 aspectRatio);
226 }
227
getRecordingCanvas() const228 SkCanvas* SkPicture::getRecordingCanvas() const {
229 // will be null if we are not recording
230 return fRecord;
231 }
232
endRecording()233 void SkPicture::endRecording() {
234 if (NULL == fPlayback) {
235 if (NULL != fRecord) {
236 fRecord->endRecording();
237 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
238 fRecord->unref();
239 fRecord = NULL;
240 }
241 }
242 SkASSERT(NULL == fRecord);
243 }
244
draw(SkCanvas * surface)245 void SkPicture::draw(SkCanvas* surface) {
246 this->endRecording();
247 if (fPlayback) {
248 fPlayback->draw(*surface);
249 }
250 }
251
252 ///////////////////////////////////////////////////////////////////////////////
253
254 #include "SkStream.h"
255
SkPicture(SkStream * stream,bool * success,SkSerializationHelpers::DecodeBitmap decoder)256 SkPicture::SkPicture(SkStream* stream, bool* success, SkSerializationHelpers::DecodeBitmap decoder) : SkRefCnt() {
257 if (success) {
258 *success = false;
259 }
260 fRecord = NULL;
261 fPlayback = NULL;
262 fWidth = fHeight = 0;
263
264 SkPictInfo info;
265
266 if (!stream->read(&info, sizeof(info))) {
267 return;
268 }
269 if (PICTURE_VERSION != info.fVersion) {
270 return;
271 }
272
273 if (stream->readBool()) {
274 bool isValid = false;
275 fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream, info, &isValid, decoder));
276 if (!isValid) {
277 SkDELETE(fPlayback);
278 fPlayback = NULL;
279 return;
280 }
281 }
282
283 // do this at the end, so that they will be zero if we hit an error.
284 fWidth = info.fWidth;
285 fHeight = info.fHeight;
286 if (success) {
287 *success = true;
288 }
289 }
290
serialize(SkWStream * stream,SkSerializationHelpers::EncodeBitmap encoder) const291 void SkPicture::serialize(SkWStream* stream, SkSerializationHelpers::EncodeBitmap encoder) const {
292 SkPicturePlayback* playback = fPlayback;
293
294 if (NULL == playback && fRecord) {
295 playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
296 }
297
298 SkPictInfo info;
299
300 info.fVersion = PICTURE_VERSION;
301 info.fWidth = fWidth;
302 info.fHeight = fHeight;
303 info.fFlags = SkPictInfo::kCrossProcess_Flag;
304 #ifdef SK_SCALAR_IS_FLOAT
305 info.fFlags |= SkPictInfo::kScalarIsFloat_Flag;
306 #endif
307 if (8 == sizeof(void*)) {
308 info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
309 }
310
311 stream->write(&info, sizeof(info));
312 if (playback) {
313 stream->writeBool(true);
314 playback->serialize(stream, encoder);
315 // delete playback if it is a local version (i.e. cons'd up just now)
316 if (playback != fPlayback) {
317 SkDELETE(playback);
318 }
319 } else {
320 stream->writeBool(false);
321 }
322 }
323
324 #ifdef SK_BUILD_FOR_ANDROID
abortPlayback()325 void SkPicture::abortPlayback() {
326 if (NULL == fPlayback) {
327 return;
328 }
329 fPlayback->abort();
330 }
331 #endif
332