SkPicture.cpp revision 5b39f5ba9c339d1e4dae391fee9ec1396feec180
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
269bool SkPicture::StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
270    if (NULL == stream) {
271        return false;
272    }
273
274    SkPictInfo info;
275    if (!stream->read(&info, sizeof(SkPictInfo))) {
276        return false;
277    }
278    if (PICTURE_VERSION != info.fVersion
279#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V13_AND_ALL_OTHER_INSTANCES_TOO
280        // V13 is backwards compatible with V12
281        && PRIOR_PRIOR_PICTURE_VERSION != info.fVersion  // TODO: remove when .skps regenerated
282#endif
283#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO
284        // V14 is backwards compatible with V13
285        && PRIOR_PICTURE_VERSION2 != info.fVersion  // TODO: remove when .skps regenerated
286#endif
287        ) {
288        return false;
289    }
290
291    if (pInfo != NULL) {
292        *pInfo = info;
293    }
294    return true;
295}
296
297SkPicture::SkPicture(SkPicturePlayback* playback, int width, int height)
298    : fPlayback(playback)
299    , fRecord(NULL)
300    , fWidth(width)
301    , fHeight(height) {}
302
303SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
304    SkPictInfo info;
305
306    if (!StreamIsSKP(stream, &info)) {
307        return NULL;
308    }
309
310    SkPicturePlayback* playback;
311    // Check to see if there is a playback to recreate.
312    if (stream->readBool()) {
313        playback = SkPicturePlayback::CreateFromStream(stream, info, proc);
314        if (NULL == playback) {
315            return NULL;
316        }
317    } else {
318        playback = NULL;
319    }
320
321    return SkNEW_ARGS(SkPicture, (playback, info.fWidth, info.fHeight));
322}
323
324void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const {
325    SkPicturePlayback* playback = fPlayback;
326
327    if (NULL == playback && fRecord) {
328        playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
329    }
330
331    SkPictInfo info;
332
333    info.fVersion = PICTURE_VERSION;
334    info.fWidth = fWidth;
335    info.fHeight = fHeight;
336    info.fFlags = SkPictInfo::kCrossProcess_Flag;
337#ifdef SK_SCALAR_IS_FLOAT
338    info.fFlags |= SkPictInfo::kScalarIsFloat_Flag;
339#endif
340    if (8 == sizeof(void*)) {
341        info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
342    }
343
344    stream->write(&info, sizeof(info));
345    if (playback) {
346        stream->writeBool(true);
347        playback->serialize(stream, encoder);
348        // delete playback if it is a local version (i.e. cons'd up just now)
349        if (playback != fPlayback) {
350            SkDELETE(playback);
351        }
352    } else {
353        stream->writeBool(false);
354    }
355}
356
357bool SkPicture::willPlayBackBitmaps() const {
358    if (!fPlayback) return false;
359    return fPlayback->containsBitmaps();
360}
361
362#ifdef SK_BUILD_FOR_ANDROID
363void SkPicture::abortPlayback() {
364    if (NULL == fPlayback) {
365        return;
366    }
367    fPlayback->abort();
368}
369#endif
370