|
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 "SkBitmapDevice.h" |
|
15 #include "SkCanvas.h" |
|
16 #include "SkChunkAlloc.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 |
|
29 #define DUMP_BUFFER_SIZE 65536 |
|
30 |
|
31 //#define ENABLE_TIME_DRAW // dumps milliseconds for each draw |
|
32 |
|
33 |
|
34 #ifdef SK_DEBUG |
|
35 // enable SK_DEBUG_TRACE to trace DrawType elements when |
|
36 // recorded and played back |
|
37 // #define SK_DEBUG_TRACE |
|
38 // enable SK_DEBUG_SIZE to see the size of picture components |
|
39 // #define SK_DEBUG_SIZE |
|
40 // enable SK_DEBUG_DUMP to see the contents of recorded elements |
|
41 // #define SK_DEBUG_DUMP |
|
42 // enable SK_DEBUG_VALIDATE to check internal structures for consistency |
|
43 // #define SK_DEBUG_VALIDATE |
|
44 #endif |
|
45 |
|
46 #if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP |
|
47 const char* DrawTypeToString(DrawType drawType) { |
|
48 switch (drawType) { |
|
49 case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break; |
|
50 case CLIP_PATH: return "CLIP_PATH"; |
|
51 case CLIP_REGION: return "CLIP_REGION"; |
|
52 case CLIP_RECT: return "CLIP_RECT"; |
|
53 case CLIP_RRECT: return "CLIP_RRECT"; |
|
54 case CONCAT: return "CONCAT"; |
|
55 case DRAW_BITMAP: return "DRAW_BITMAP"; |
|
56 case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX"; |
|
57 case DRAW_BITMAP_NINE: return "DRAW_BITMAP_NINE"; |
|
58 case DRAW_BITMAP_RECT_TO_RECT: return "DRAW_BITMAP_RECT_TO_RECT"; |
|
59 case DRAW_CLEAR: return "DRAW_CLEAR"; |
|
60 case DRAW_DATA: return "DRAW_DATA"; |
|
61 case DRAW_OVAL: return "DRAW_OVAL"; |
|
62 case DRAW_PAINT: return "DRAW_PAINT"; |
|
63 case DRAW_PATH: return "DRAW_PATH"; |
|
64 case DRAW_PICTURE: return "DRAW_PICTURE"; |
|
65 case DRAW_POINTS: return "DRAW_POINTS"; |
|
66 case DRAW_POS_TEXT: return "DRAW_POS_TEXT"; |
|
67 case DRAW_POS_TEXT_TOP_BOTTOM: return "DRAW_POS_TEXT_TOP_BOTTOM"; |
|
68 case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H"; |
|
69 case DRAW_POS_TEXT_H_TOP_BOTTOM: return "DRAW_POS_TEXT_H_TOP_BOTTOM"; |
|
70 case DRAW_RECT: return "DRAW_RECT"; |
|
71 case DRAW_RRECT: return "DRAW_RRECT"; |
|
72 case DRAW_SPRITE: return "DRAW_SPRITE"; |
|
73 case DRAW_TEXT: return "DRAW_TEXT"; |
|
74 case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH"; |
|
75 case DRAW_TEXT_TOP_BOTTOM: return "DRAW_TEXT_TOP_BOTTOM"; |
|
76 case DRAW_VERTICES: return "DRAW_VERTICES"; |
|
77 case RESTORE: return "RESTORE"; |
|
78 case ROTATE: return "ROTATE"; |
|
79 case SAVE: return "SAVE"; |
|
80 case SAVE_LAYER: return "SAVE_LAYER"; |
|
81 case SCALE: return "SCALE"; |
|
82 case SET_MATRIX: return "SET_MATRIX"; |
|
83 case SKEW: return "SKEW"; |
|
84 case TRANSLATE: return "TRANSLATE"; |
|
85 case NOOP: return "NOOP"; |
|
86 default: |
|
87 SkDebugf("DrawType error 0x%08x\n", drawType); |
|
88 SkASSERT(0); |
|
89 break; |
|
90 } |
|
91 SkASSERT(0); |
|
92 return NULL; |
|
93 } |
|
94 #endif |
|
95 |
|
96 #ifdef SK_DEBUG_VALIDATE |
|
97 static void validateMatrix(const SkMatrix* matrix) { |
|
98 SkScalar scaleX = matrix->getScaleX(); |
|
99 SkScalar scaleY = matrix->getScaleY(); |
|
100 SkScalar skewX = matrix->getSkewX(); |
|
101 SkScalar skewY = matrix->getSkewY(); |
|
102 SkScalar perspX = matrix->getPerspX(); |
|
103 SkScalar perspY = matrix->getPerspY(); |
|
104 if (scaleX != 0 && skewX != 0) |
|
105 SkDebugf("scaleX != 0 && skewX != 0\n"); |
|
106 SkASSERT(scaleX == 0 || skewX == 0); |
|
107 SkASSERT(scaleY == 0 || skewY == 0); |
|
108 SkASSERT(perspX == 0); |
|
109 SkASSERT(perspY == 0); |
|
110 } |
|
111 #endif |
|
112 |
|
113 |
|
114 /////////////////////////////////////////////////////////////////////////////// |
|
115 |
|
116 SkPicture::SkPicture() { |
|
117 fRecord = NULL; |
|
118 fPlayback = NULL; |
|
119 fWidth = fHeight = 0; |
|
120 fAccelData = NULL; |
|
121 } |
|
122 |
|
123 SkPicture::SkPicture(const SkPicture& src) |
|
124 : INHERITED() |
|
125 , fAccelData(NULL) { |
|
126 fWidth = src.fWidth; |
|
127 fHeight = src.fHeight; |
|
128 fRecord = NULL; |
|
129 |
|
130 /* We want to copy the src's playback. However, if that hasn't been built |
|
131 yet, we need to fake a call to endRecording() without actually calling |
|
132 it (since it is destructive, and we don't want to change src). |
|
133 */ |
|
134 if (src.fPlayback) { |
|
135 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback)); |
|
136 } else if (src.fRecord) { |
|
137 // here we do a fake src.endRecording() |
|
138 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fRecord)); |
|
139 } else { |
|
140 fPlayback = NULL; |
|
141 } |
|
142 } |
|
143 |
|
144 SkPicture::~SkPicture() { |
|
145 SkSafeUnref(fRecord); |
|
146 SkDELETE(fPlayback); |
|
147 SkSafeUnref(fAccelData); |
|
148 } |
|
149 |
|
150 void SkPicture::internalOnly_EnableOpts(bool enableOpts) { |
|
151 if (NULL != fRecord) { |
|
152 fRecord->internalOnly_EnableOpts(enableOpts); |
|
153 } |
|
154 } |
|
155 |
|
156 void SkPicture::swap(SkPicture& other) { |
|
157 SkTSwap(fRecord, other.fRecord); |
|
158 SkTSwap(fPlayback, other.fPlayback); |
|
159 SkTSwap(fAccelData, other.fAccelData); |
|
160 SkTSwap(fWidth, other.fWidth); |
|
161 SkTSwap(fHeight, other.fHeight); |
|
162 } |
|
163 |
|
164 SkPicture* SkPicture::clone() const { |
|
165 SkPicture* clonedPicture = SkNEW(SkPicture); |
|
166 clone(clonedPicture, 1); |
|
167 return clonedPicture; |
|
168 } |
|
169 |
|
170 void SkPicture::clone(SkPicture* pictures, int count) const { |
|
171 SkPictCopyInfo copyInfo; |
|
172 |
|
173 for (int i = 0; i < count; i++) { |
|
174 SkPicture* clone = &pictures[i]; |
|
175 |
|
176 clone->fWidth = fWidth; |
|
177 clone->fHeight = fHeight; |
|
178 SkSafeSetNull(clone->fRecord); |
|
179 SkDELETE(clone->fPlayback); |
|
180 |
|
181 /* We want to copy the src's playback. However, if that hasn't been built |
|
182 yet, we need to fake a call to endRecording() without actually calling |
|
183 it (since it is destructive, and we don't want to change src). |
|
184 */ |
|
185 if (fPlayback) { |
|
186 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fPlayback, ©Info)); |
|
187 } else if (fRecord) { |
|
188 // here we do a fake src.endRecording() |
|
189 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord, true)); |
|
190 } else { |
|
191 clone->fPlayback = NULL; |
|
192 } |
|
193 } |
|
194 } |
|
195 |
|
196 SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() { |
|
197 static int32_t gNextID = 0; |
|
198 |
|
199 int32_t id = sk_atomic_inc(&gNextID); |
|
200 if (id >= 1 << (8 * sizeof(Domain))) { |
|
201 SK_CRASH(); |
|
202 } |
|
203 |
|
204 return static_cast<Domain>(id); |
|
205 } |
|
206 |
|
207 /////////////////////////////////////////////////////////////////////////////// |
|
208 |
|
209 SkCanvas* SkPicture::beginRecording(int width, int height, |
|
210 uint32_t recordingFlags) { |
|
211 if (fPlayback) { |
|
212 SkDELETE(fPlayback); |
|
213 fPlayback = NULL; |
|
214 } |
|
215 SkSafeUnref(fAccelData); |
|
216 SkSafeSetNull(fRecord); |
|
217 |
|
218 // Must be set before calling createBBoxHierarchy |
|
219 fWidth = width; |
|
220 fHeight = height; |
|
221 |
|
222 const SkISize size = SkISize::Make(width, height); |
|
223 |
|
224 if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) { |
|
225 SkBBoxHierarchy* tree = this->createBBoxHierarchy(); |
|
226 SkASSERT(NULL != tree); |
|
227 fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (size, recordingFlags, tree)); |
|
228 tree->unref(); |
|
229 } else { |
|
230 fRecord = SkNEW_ARGS(SkPictureRecord, (size, recordingFlags)); |
|
231 } |
|
232 fRecord->beginRecording(); |
|
233 |
|
234 return fRecord; |
|
235 } |
|
236 |
|
237 SkBBoxHierarchy* SkPicture::createBBoxHierarchy() const { |
|
238 // These values were empirically determined to produce reasonable |
|
239 // performance in most cases. |
|
240 static const int kRTreeMinChildren = 6; |
|
241 static const int kRTreeMaxChildren = 11; |
|
242 |
|
243 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth), |
|
244 SkIntToScalar(fHeight)); |
|
245 bool sortDraws = false; // Do not sort draw calls when bulk loading. |
|
246 |
|
247 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren, |
|
248 aspectRatio, sortDraws); |
|
249 } |
|
250 |
|
251 SkCanvas* SkPicture::getRecordingCanvas() const { |
|
252 // will be null if we are not recording |
|
253 return fRecord; |
|
254 } |
|
255 |
|
256 void SkPicture::endRecording() { |
|
257 if (NULL == fPlayback) { |
|
258 if (NULL != fRecord) { |
|
259 fRecord->endRecording(); |
|
260 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord)); |
|
261 SkSafeSetNull(fRecord); |
|
262 } |
|
263 } |
|
264 SkASSERT(NULL == fRecord); |
|
265 } |
|
266 |
|
267 void SkPicture::draw(SkCanvas* surface, SkDrawPictureCallback* callback) { |
|
268 this->endRecording(); |
|
269 if (NULL != fPlayback) { |
|
270 fPlayback->draw(*surface, callback); |
|
271 } |
|
272 } |
|
273 |
|
274 /////////////////////////////////////////////////////////////////////////////// |
|
275 |
|
276 #include "SkStream.h" |
|
277 |
|
278 static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; |
|
279 |
|
280 bool SkPicture::IsValidPictInfo(const SkPictInfo& info) { |
|
281 if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) { |
|
282 return false; |
|
283 } |
|
284 |
|
285 if (info.fVersion < MIN_PICTURE_VERSION || |
|
286 info.fVersion > CURRENT_PICTURE_VERSION) { |
|
287 return false; |
|
288 } |
|
289 |
|
290 return true; |
|
291 } |
|
292 |
|
293 bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) { |
|
294 if (NULL == stream) { |
|
295 return false; |
|
296 } |
|
297 |
|
298 // Check magic bytes. |
|
299 SkPictInfo info; |
|
300 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); |
|
301 if (!stream->read(&info, sizeof(info)) || !IsValidPictInfo(info)) { |
|
302 return false; |
|
303 } |
|
304 |
|
305 if (pInfo != NULL) { |
|
306 *pInfo = info; |
|
307 } |
|
308 return true; |
|
309 } |
|
310 |
|
311 bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer& buffer, SkPictInfo* pInfo) { |
|
312 // Check magic bytes. |
|
313 SkPictInfo info; |
|
314 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); |
|
315 if (!buffer.readByteArray(&info, sizeof(info)) || !IsValidPictInfo(info)) { |
|
316 return false; |
|
317 } |
|
318 |
|
319 if (pInfo != NULL) { |
|
320 *pInfo = info; |
|
321 } |
|
322 return true; |
|
323 } |
|
324 |
|
325 SkPicture::SkPicture(SkPicturePlayback* playback, int width, int height) |
|
326 : fPlayback(playback) |
|
327 , fRecord(NULL) |
|
328 , fWidth(width) |
|
329 , fHeight(height) |
|
330 , fAccelData(NULL) {} |
|
331 |
|
332 SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) { |
|
333 SkPictInfo info; |
|
334 |
|
335 if (!InternalOnly_StreamIsSKP(stream, &info)) { |
|
336 return NULL; |
|
337 } |
|
338 |
|
339 SkPicturePlayback* playback; |
|
340 // Check to see if there is a playback to recreate. |
|
341 if (stream->readBool()) { |
|
342 playback = SkPicturePlayback::CreateFromStream(stream, info, proc); |
|
343 if (NULL == playback) { |
|
344 return NULL; |
|
345 } |
|
346 } else { |
|
347 playback = NULL; |
|
348 } |
|
349 |
|
350 return SkNEW_ARGS(SkPicture, (playback, info.fWidth, info.fHeight)); |
|
351 } |
|
352 |
|
353 SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) { |
|
354 SkPictInfo info; |
|
355 |
|
356 if (!InternalOnly_BufferIsSKP(buffer, &info)) { |
|
357 return NULL; |
|
358 } |
|
359 |
|
360 SkPicturePlayback* playback; |
|
361 // Check to see if there is a playback to recreate. |
|
362 if (buffer.readBool()) { |
|
363 playback = SkPicturePlayback::CreateFromBuffer(buffer); |
|
364 if (NULL == playback) { |
|
365 return NULL; |
|
366 } |
|
367 } else { |
|
368 playback = NULL; |
|
369 } |
|
370 |
|
371 return SkNEW_ARGS(SkPicture, (playback, info.fWidth, info.fHeight)); |
|
372 } |
|
373 |
|
374 void SkPicture::createHeader(SkPictInfo* info) const { |
|
375 // Copy magic bytes at the beginning of the header |
|
376 SkASSERT(sizeof(kMagic) == 8); |
|
377 SkASSERT(sizeof(kMagic) == sizeof(info->fMagic)); |
|
378 memcpy(info->fMagic, kMagic, sizeof(kMagic)); |
|
379 |
|
380 // Set picture info after magic bytes in the header |
|
381 info->fVersion = CURRENT_PICTURE_VERSION; |
|
382 info->fWidth = fWidth; |
|
383 info->fHeight = fHeight; |
|
384 info->fFlags = SkPictInfo::kCrossProcess_Flag; |
|
385 // TODO: remove this flag, since we're always float (now) |
|
386 info->fFlags |= SkPictInfo::kScalarIsFloat_Flag; |
|
387 |
|
388 if (8 == sizeof(void*)) { |
|
389 info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag; |
|
390 } |
|
391 } |
|
392 |
|
393 void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const { |
|
394 SkPicturePlayback* playback = fPlayback; |
|
395 |
|
396 if (NULL == playback && fRecord) { |
|
397 playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord)); |
|
398 } |
|
399 |
|
400 SkPictInfo header; |
|
401 this->createHeader(&header); |
|
402 stream->write(&header, sizeof(header)); |
|
403 if (playback) { |
|
404 stream->writeBool(true); |
|
405 playback->serialize(stream, encoder); |
|
406 // delete playback if it is a local version (i.e. cons'd up just now) |
|
407 if (playback != fPlayback) { |
|
408 SkDELETE(playback); |
|
409 } |
|
410 } else { |
|
411 stream->writeBool(false); |
|
412 } |
|
413 } |
|
414 |
|
415 void SkPicture::flatten(SkWriteBuffer& buffer) const { |
|
416 SkPicturePlayback* playback = fPlayback; |
|
417 |
|
418 if (NULL == playback && fRecord) { |
|
419 playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord)); |
|
420 } |
|
421 |
|
422 SkPictInfo header; |
|
423 this->createHeader(&header); |
|
424 buffer.writeByteArray(&header, sizeof(header)); |
|
425 if (playback) { |
|
426 buffer.writeBool(true); |
|
427 playback->flatten(buffer); |
|
428 // delete playback if it is a local version (i.e. cons'd up just now) |
|
429 if (playback != fPlayback) { |
|
430 SkDELETE(playback); |
|
431 } |
|
432 } else { |
|
433 buffer.writeBool(false); |
|
434 } |
|
435 } |
|
436 |
|
437 bool SkPicture::willPlayBackBitmaps() const { |
|
438 if (!fPlayback) { |
|
439 return false; |
|
440 } |
|
441 return fPlayback->containsBitmaps(); |
|
442 } |
|
443 |
|
444 #ifdef SK_BUILD_FOR_ANDROID |
|
445 void SkPicture::abortPlayback() { |
|
446 if (NULL == fPlayback) { |
|
447 return; |
|
448 } |
|
449 fPlayback->abort(); |
|
450 } |
|
451 #endif |