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
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 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
88static 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
107SkPicture::SkPicture() {
108    fRecord = NULL;
109    fPlayback = NULL;
110    fWidth = fHeight = 0;
111}
112
113SkPicture::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
132SkPicture::~SkPicture() {
133    SkSafeUnref(fRecord);
134    SkDELETE(fPlayback);
135}
136
137void 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
144SkPicture* SkPicture::clone() const {
145    SkPicture* clonedPicture = SkNEW(SkPicture);
146    clone(clonedPicture, 1);
147    return clonedPicture;
148}
149
150void 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, &copyInfo));
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
183SkCanvas* 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
216SkBBoxHierarchy* 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
228SkCanvas* SkPicture::getRecordingCanvas() const {
229    // will be null if we are not recording
230    return fRecord;
231}
232
233void 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
245void SkPicture::draw(SkCanvas* surface) {
246    this->endRecording();
247    if (fPlayback) {
248        fPlayback->draw(*surface);
249    }
250}
251
252///////////////////////////////////////////////////////////////////////////////
253
254#include "SkStream.h"
255
256SkPicture::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
291void 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
325void SkPicture::abortPlayback() {
326    if (NULL == fPlayback) {
327        return;
328    }
329    fPlayback->abort();
330}
331#endif
332