SkPicture.cpp revision eab16dea1ce249dc8e4dc635cd76b6b1b7d0cc98
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 "SkPicture.h"
17#include "SkRegion.h"
18#include "SkStream.h"
19#include "SkTDArray.h"
20#include "SkTSearch.h"
21#include "SkTime.h"
22
23#include "SkReader32.h"
24#include "SkWriter32.h"
25#include "SkRTree.h"
26#include "SkBBoxHierarchyRecord.h"
27
28SK_DEFINE_INST_COUNT(SkPicture)
29
30#define DUMP_BUFFER_SIZE 65536
31
32//#define ENABLE_TIME_DRAW    // dumps milliseconds for each draw
33
34
35#ifdef SK_DEBUG
36// enable SK_DEBUG_TRACE to trace DrawType elements when
37//     recorded and played back
38// #define SK_DEBUG_TRACE
39// enable SK_DEBUG_SIZE to see the size of picture components
40// #define SK_DEBUG_SIZE
41// enable SK_DEBUG_DUMP to see the contents of recorded elements
42// #define SK_DEBUG_DUMP
43// enable SK_DEBUG_VALIDATE to check internal structures for consistency
44// #define SK_DEBUG_VALIDATE
45#endif
46
47#if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP
48const char* DrawTypeToString(DrawType drawType) {
49    switch (drawType) {
50        case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break;
51        case CLIP_PATH: return "CLIP_PATH";
52        case CLIP_REGION: return "CLIP_REGION";
53        case CLIP_RECT: return "CLIP_RECT";
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_RECT: return "DRAW_BITMAP_RECT";
58        case DRAW_PAINT: return "DRAW_PAINT";
59        case DRAW_PATH: return "DRAW_PATH";
60        case DRAW_PICTURE: return "DRAW_PICTURE";
61        case DRAW_POINTS: return "DRAW_POINTS";
62        case DRAW_POS_TEXT: return "DRAW_POS_TEXT";
63        case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H";
64        case DRAW_RECT_GENERAL: return "DRAW_RECT_GENERAL";
65        case DRAW_RECT_SIMPLE: return "DRAW_RECT_SIMPLE";
66        case DRAW_SPRITE: return "DRAW_SPRITE";
67        case DRAW_TEXT: return "DRAW_TEXT";
68        case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH";
69        case RESTORE: return "RESTORE";
70        case ROTATE: return "ROTATE";
71        case SAVE: return "SAVE";
72        case SAVE_LAYER: return "SAVE_LAYER";
73        case SCALE: return "SCALE";
74        case SKEW: return "SKEW";
75        case TRANSLATE: return "TRANSLATE";
76        default:
77            SkDebugf("DrawType error 0x%08x\n", drawType);
78            SkASSERT(0);
79            break;
80    }
81    SkASSERT(0);
82    return NULL;
83}
84#endif
85
86#ifdef SK_DEBUG_VALIDATE
87static void validateMatrix(const SkMatrix* matrix) {
88    SkScalar scaleX = matrix->getScaleX();
89    SkScalar scaleY = matrix->getScaleY();
90    SkScalar skewX = matrix->getSkewX();
91    SkScalar skewY = matrix->getSkewY();
92    SkScalar perspX = matrix->getPerspX();
93    SkScalar perspY = matrix->getPerspY();
94    if (scaleX != 0 && skewX != 0)
95        SkDebugf("scaleX != 0 && skewX != 0\n");
96    SkASSERT(scaleX == 0 || skewX == 0);
97    SkASSERT(scaleY == 0 || skewY == 0);
98    SkASSERT(perspX == 0);
99    SkASSERT(perspY == 0);
100}
101#endif
102
103
104///////////////////////////////////////////////////////////////////////////////
105
106SkPicture::SkPicture() {
107    fRecord = NULL;
108    fPlayback = NULL;
109    fWidth = fHeight = 0;
110}
111
112SkPicture::SkPicture(const SkPicture& src) : SkRefCnt() {
113    fWidth = src.fWidth;
114    fHeight = src.fHeight;
115    fRecord = NULL;
116
117    /*  We want to copy the src's playback. However, if that hasn't been built
118        yet, we need to fake a call to endRecording() without actually calling
119        it (since it is destructive, and we don't want to change src).
120     */
121    if (src.fPlayback) {
122        fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback));
123    } else if (src.fRecord) {
124        // here we do a fake src.endRecording()
125        fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fRecord));
126    } else {
127        fPlayback = NULL;
128    }
129}
130
131SkPicture::~SkPicture() {
132    SkSafeUnref(fRecord);
133    SkDELETE(fPlayback);
134}
135
136void SkPicture::swap(SkPicture& other) {
137    SkTSwap(fRecord, other.fRecord);
138    SkTSwap(fPlayback, other.fPlayback);
139    SkTSwap(fWidth, other.fWidth);
140    SkTSwap(fHeight, other.fHeight);
141}
142
143SkPicture* SkPicture::clone() const {
144    SkPicture* clonedPicture = SkNEW(SkPicture);
145    clone(clonedPicture, 1);
146    return clonedPicture;
147}
148
149void SkPicture::clone(SkPicture* pictures, int count) const {
150    SkPictCopyInfo copyInfo;
151
152    for (int i = 0; i < count; i++) {
153        SkPicture* clone = &pictures[i];
154
155        clone->fWidth = fWidth;
156        clone->fHeight = fHeight;
157        clone->fRecord = NULL;
158
159        /*  We want to copy the src's playback. However, if that hasn't been built
160            yet, we need to fake a call to endRecording() without actually calling
161            it (since it is destructive, and we don't want to change src).
162         */
163        if (fPlayback) {
164            clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fPlayback, &copyInfo));
165        } else if (fRecord) {
166            // here we do a fake src.endRecording()
167            clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord, true));
168        } else {
169            clone->fPlayback = NULL;
170        }
171    }
172}
173
174///////////////////////////////////////////////////////////////////////////////
175
176SkCanvas* SkPicture::beginRecording(int width, int height,
177                                    uint32_t recordingFlags) {
178    if (fPlayback) {
179        SkDELETE(fPlayback);
180        fPlayback = NULL;
181    }
182
183    if (NULL != fRecord) {
184        fRecord->unref();
185        fRecord = NULL;
186    }
187
188    if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) {
189        SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(width),
190                                           SkIntToScalar(height));
191        SkRTree* tree = SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
192                                        aspectRatio);
193        SkASSERT(NULL != tree);
194        fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (recordingFlags, tree));
195        tree->unref();
196    } else {
197        fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags));
198    }
199
200    fWidth = width;
201    fHeight = height;
202
203    SkBitmap bm;
204    bm.setConfig(SkBitmap::kNo_Config, width, height);
205    fRecord->setBitmapDevice(bm);
206
207    return fRecord;
208}
209
210SkCanvas* SkPicture::getRecordingCanvas() const {
211    // will be null if we are not recording
212    return fRecord;
213}
214
215void SkPicture::endRecording() {
216    if (NULL == fPlayback) {
217        if (NULL != fRecord) {
218            fRecord->endRecording();
219            fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
220            fRecord->unref();
221            fRecord = NULL;
222        }
223    }
224    SkASSERT(NULL == fRecord);
225}
226
227void SkPicture::draw(SkCanvas* surface) {
228    this->endRecording();
229    if (fPlayback) {
230        fPlayback->draw(*surface);
231    }
232}
233
234///////////////////////////////////////////////////////////////////////////////
235
236#include "SkStream.h"
237
238// V2 : adds SkPixelRef's generation ID.
239// V3 : PictInfo tag at beginning, and EOF tag at the end
240// V4 : move SkPictInfo to be the header
241// V5 : don't read/write FunctionPtr on cross-process (we can detect that)
242// V6 : added serialization of SkPath's bounds (and packed its flags tighter)
243// V7 : changed drawBitmapRect(IRect) to drawBitmapRectToRect(Rect)
244#define PICTURE_VERSION     7
245
246SkPicture::SkPicture(SkStream* stream, bool* success) : SkRefCnt() {
247    if (success) {
248        *success = false;
249    }
250    fRecord = NULL;
251    fPlayback = NULL;
252    fWidth = fHeight = 0;
253
254    SkPictInfo info;
255
256    if (!stream->read(&info, sizeof(info))) {
257        return;
258    }
259    if (PICTURE_VERSION != info.fVersion) {
260        return;
261    }
262
263    if (stream->readBool()) {
264        bool isValid = false;
265        fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream, info, &isValid));
266        if (!isValid) {
267            SkDELETE(fPlayback);
268            fPlayback = NULL;
269            return;
270        }
271    }
272
273    // do this at the end, so that they will be zero if we hit an error.
274    fWidth = info.fWidth;
275    fHeight = info.fHeight;
276    if (success) {
277        *success = true;
278    }
279}
280
281void SkPicture::serialize(SkWStream* stream) const {
282    SkPicturePlayback* playback = fPlayback;
283
284    if (NULL == playback && fRecord) {
285        playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
286    }
287
288    SkPictInfo info;
289
290    info.fVersion = PICTURE_VERSION;
291    info.fWidth = fWidth;
292    info.fHeight = fHeight;
293    info.fFlags = SkPictInfo::kCrossProcess_Flag;
294#ifdef SK_SCALAR_IS_FLOAT
295    info.fFlags |= SkPictInfo::kScalarIsFloat_Flag;
296#endif
297    if (8 == sizeof(void*)) {
298        info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
299    }
300
301    stream->write(&info, sizeof(info));
302    if (playback) {
303        stream->writeBool(true);
304        playback->serialize(stream);
305        // delete playback if it is a local version (i.e. cons'd up just now)
306        if (playback != fPlayback) {
307            SkDELETE(playback);
308        }
309    } else {
310        stream->writeBool(false);
311    }
312}
313
314void SkPicture::abortPlayback() {
315    if (NULL == fPlayback) {
316        return;
317    }
318    fPlayback->abort();
319}
320
321
322