SkPicture.cpp revision 466310dbd3073add2ec934e336c30deaaf702eae
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
29SK_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
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 CLIP_RRECT: return "CLIP_RRECT";
56        case CONCAT: return "CONCAT";
57        case DRAW_BITMAP: return "DRAW_BITMAP";
58        case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX";
59        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";
64        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";
69        case DRAW_POS_TEXT_TOP_BOTTOM: return "DRAW_POS_TEXT_TOP_BOTTOM";
70        case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H";
71        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";
74        case DRAW_SPRITE: return "DRAW_SPRITE";
75        case DRAW_TEXT: return "DRAW_TEXT";
76        case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH";
77        case DRAW_TEXT_TOP_BOTTOM: return "DRAW_TEXT_TOP_BOTTOM";
78        case DRAW_VERTICES: return "DRAW_VERTICES";
79        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";
84        case SET_MATRIX: return "SET_MATRIX";
85        case SKEW: return "SKEW";
86        case TRANSLATE: return "TRANSLATE";
87        case NOOP: return "NOOP";
88        default:
89            SkDebugf("DrawType error 0x%08x\n", drawType);
90            SkASSERT(0);
91            break;
92    }
93    SkASSERT(0);
94    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
124SkPicture::SkPicture(const SkPicture& src) : INHERITED() {
125    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() {
144    SkSafeUnref(fRecord);
145    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
155SkPicture* 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
171        if (NULL != clone->fRecord) {
172            clone->fRecord->unref();
173            clone->fRecord = NULL;
174        }
175        SkDELETE(clone->fPlayback);
176
177        /*  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
192///////////////////////////////////////////////////////////////////////////////
193
194SkCanvas* SkPicture::beginRecording(int width, int height,
195                                    uint32_t recordingFlags) {
196    if (fPlayback) {
197        SkDELETE(fPlayback);
198        fPlayback = NULL;
199    }
200
201    if (NULL != fRecord) {
202        fRecord->unref();
203        fRecord = NULL;
204    }
205
206    SkBitmap bm;
207    bm.setConfig(SkBitmap::kNo_Config, width, height);
208    SkAutoTUnref<SkBaseDevice> dev(SkNEW_ARGS(SkBitmapDevice, (bm)));
209
210    // Must be set before calling createBBoxHierarchy
211    fWidth = width;
212    fHeight = height;
213
214    if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) {
215        SkBBoxHierarchy* tree = this->createBBoxHierarchy();
216        SkASSERT(NULL != tree);
217        fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (recordingFlags, tree, dev));
218        tree->unref();
219    } else {
220        fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags, dev));
221    }
222    fRecord->beginRecording();
223
224    return fRecord;
225}
226
227SkBBoxHierarchy* SkPicture::createBBoxHierarchy() const {
228    // These values were empirically determined to produce reasonable
229    // 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    bool sortDraws = false;  // Do not sort draw calls when bulk loading.
236
237    return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
238                           aspectRatio, sortDraws);
239}
240
241SkCanvas* SkPicture::getRecordingCanvas() const {
242    // will be null if we are not recording
243    return fRecord;
244}
245
246void SkPicture::endRecording() {
247    if (NULL == fPlayback) {
248        if (NULL != fRecord) {
249            fRecord->endRecording();
250            fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
251            fRecord->unref();
252            fRecord = NULL;
253        }
254    }
255    SkASSERT(NULL == fRecord);
256}
257
258void SkPicture::draw(SkCanvas* surface, SkDrawPictureCallback* callback) {
259    this->endRecording();
260    if (fPlayback) {
261        fPlayback->draw(*surface, callback);
262    }
263}
264
265///////////////////////////////////////////////////////////////////////////////
266
267#include "SkStream.h"
268
269static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
270
271bool SkPicture::StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
272    if (NULL == stream) {
273        return false;
274    }
275
276    // Check magic bytes.
277    char magic[sizeof(kMagic)];
278    stream->read(magic, sizeof(kMagic));
279    if (0 != memcmp(magic, kMagic, sizeof(kMagic))) {
280        return false;
281    }
282
283    SkPictInfo info;
284    if (!stream->read(&info, sizeof(SkPictInfo))) {
285        return false;
286    }
287    if (PICTURE_VERSION != info.fVersion
288#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V13_AND_ALL_OTHER_INSTANCES_TOO
289        // V13 is backwards compatible with V12
290        && PRIOR_PRIOR_PICTURE_VERSION != info.fVersion  // TODO: remove when .skps regenerated
291#endif
292#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO
293        // V14 is backwards compatible with V13
294        && PRIOR_PICTURE_VERSION2 != info.fVersion  // TODO: remove when .skps regenerated
295#endif
296#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO
297        // V16 is backwards compatible with V15
298        && PRIOR_PICTURE_VERSION3 != info.fVersion  // TODO: remove when .skps regenerated
299#endif
300        ) {
301        return false;
302    }
303
304    if (pInfo != NULL) {
305        *pInfo = info;
306    }
307    return true;
308}
309
310SkPicture::SkPicture(SkPicturePlayback* playback, int width, int height)
311    : fPlayback(playback)
312    , fRecord(NULL)
313    , fWidth(width)
314    , fHeight(height) {}
315
316SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
317    SkPictInfo info;
318
319    if (!StreamIsSKP(stream, &info)) {
320        return NULL;
321    }
322
323    SkPicturePlayback* playback;
324    // Check to see if there is a playback to recreate.
325    if (stream->readBool()) {
326        playback = SkPicturePlayback::CreateFromStream(stream, info, proc);
327        if (NULL == playback) {
328            return NULL;
329        }
330    } else {
331        playback = NULL;
332    }
333
334    return SkNEW_ARGS(SkPicture, (playback, info.fWidth, info.fHeight));
335}
336
337void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
338    SkPicturePlayback* playback = fPlayback;
339
340    if (NULL == playback && fRecord) {
341        playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
342    }
343
344    SkPictInfo info;
345
346    info.fVersion = PICTURE_VERSION;
347    info.fWidth = fWidth;
348    info.fHeight = fHeight;
349    info.fFlags = SkPictInfo::kCrossProcess_Flag;
350#ifdef SK_SCALAR_IS_FLOAT
351    info.fFlags |= SkPictInfo::kScalarIsFloat_Flag;
352#endif
353    if (8 == sizeof(void*)) {
354        info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
355    }
356
357    // Write 8 magic bytes to ID this file format.
358    SkASSERT(sizeof(kMagic) == 8);
359    stream->write(kMagic, sizeof(kMagic));
360
361    stream->write(&info, sizeof(info));
362    if (playback) {
363        stream->writeBool(true);
364        playback->serialize(stream, encoder);
365        // delete playback if it is a local version (i.e. cons'd up just now)
366        if (playback != fPlayback) {
367            SkDELETE(playback);
368        }
369    } else {
370        stream->writeBool(false);
371    }
372}
373
374bool SkPicture::willPlayBackBitmaps() const {
375    if (!fPlayback) return false;
376    return fPlayback->containsBitmaps();
377}
378
379#ifdef SK_BUILD_FOR_ANDROID
380void SkPicture::abortPlayback() {
381    if (NULL == fPlayback) {
382        return;
383    }
384    fPlayback->abort();
385}
386#endif
387