blob: 70e329efea4a22fdd1cb27ff613a952c40d9dbe9 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * 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
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
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
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000026SK_DEFINE_INST_COUNT(SkPicture)
27
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#define DUMP_BUFFER_SIZE 65536
29
30//#define ENABLE_TIME_DRAW // dumps milliseconds for each draw
31
32
33#ifdef SK_DEBUG
reed@google.com82065d62011-02-07 15:30:46 +000034// enable SK_DEBUG_TRACE to trace DrawType elements when
reed@android.com8a1c16f2008-12-17 15:59:43 +000035// recorded and played back
36// #define SK_DEBUG_TRACE
37// enable SK_DEBUG_SIZE to see the size of picture components
38// #define SK_DEBUG_SIZE
39// enable SK_DEBUG_DUMP to see the contents of recorded elements
40// #define SK_DEBUG_DUMP
41// enable SK_DEBUG_VALIDATE to check internal structures for consistency
42// #define SK_DEBUG_VALIDATE
43#endif
44
45#if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP
46const char* DrawTypeToString(DrawType drawType) {
47 switch (drawType) {
48 case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break;
49 case CLIP_PATH: return "CLIP_PATH";
50 case CLIP_REGION: return "CLIP_REGION";
51 case CLIP_RECT: return "CLIP_RECT";
52 case CONCAT: return "CONCAT";
53 case DRAW_BITMAP: return "DRAW_BITMAP";
54 case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX";
55 case DRAW_BITMAP_RECT: return "DRAW_BITMAP_RECT";
56 case DRAW_PAINT: return "DRAW_PAINT";
57 case DRAW_PATH: return "DRAW_PATH";
58 case DRAW_PICTURE: return "DRAW_PICTURE";
59 case DRAW_POINTS: return "DRAW_POINTS";
60 case DRAW_POS_TEXT: return "DRAW_POS_TEXT";
61 case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H";
62 case DRAW_RECT_GENERAL: return "DRAW_RECT_GENERAL";
63 case DRAW_RECT_SIMPLE: return "DRAW_RECT_SIMPLE";
64 case DRAW_SPRITE: return "DRAW_SPRITE";
65 case DRAW_TEXT: return "DRAW_TEXT";
66 case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH";
67 case RESTORE: return "RESTORE";
68 case ROTATE: return "ROTATE";
69 case SAVE: return "SAVE";
70 case SAVE_LAYER: return "SAVE_LAYER";
71 case SCALE: return "SCALE";
72 case SKEW: return "SKEW";
73 case TRANSLATE: return "TRANSLATE";
reed@google.com82065d62011-02-07 15:30:46 +000074 default:
75 SkDebugf("DrawType error 0x%08x\n", drawType);
76 SkASSERT(0);
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 break;
78 }
reed@google.com82065d62011-02-07 15:30:46 +000079 SkASSERT(0);
reed@android.com8a1c16f2008-12-17 15:59:43 +000080 return NULL;
81}
82#endif
83
84#ifdef SK_DEBUG_VALIDATE
85static void validateMatrix(const SkMatrix* matrix) {
86 SkScalar scaleX = matrix->getScaleX();
87 SkScalar scaleY = matrix->getScaleY();
88 SkScalar skewX = matrix->getSkewX();
89 SkScalar skewY = matrix->getSkewY();
90 SkScalar perspX = matrix->getPerspX();
91 SkScalar perspY = matrix->getPerspY();
92 if (scaleX != 0 && skewX != 0)
93 SkDebugf("scaleX != 0 && skewX != 0\n");
94 SkASSERT(scaleX == 0 || skewX == 0);
95 SkASSERT(scaleY == 0 || skewY == 0);
96 SkASSERT(perspX == 0);
97 SkASSERT(perspY == 0);
98}
99#endif
100
101
102///////////////////////////////////////////////////////////////////////////////
103
104SkPicture::SkPicture() {
105 fRecord = NULL;
106 fPlayback = NULL;
107 fWidth = fHeight = 0;
108}
109
110SkPicture::SkPicture(const SkPicture& src) : SkRefCnt() {
111 fWidth = src.fWidth;
112 fHeight = src.fHeight;
113 fRecord = NULL;
114
115 /* We want to copy the src's playback. However, if that hasn't been built
116 yet, we need to fake a call to endRecording() without actually calling
117 it (since it is destructive, and we don't want to change src).
118 */
119 if (src.fPlayback) {
120 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback));
121 } else if (src.fRecord) {
122 // here we do a fake src.endRecording()
123 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fRecord));
124 } else {
125 fPlayback = NULL;
126 }
127}
128
129SkPicture::~SkPicture() {
reed@google.com82065d62011-02-07 15:30:46 +0000130 SkSafeUnref(fRecord);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 SkDELETE(fPlayback);
132}
133
134void SkPicture::swap(SkPicture& other) {
135 SkTSwap(fRecord, other.fRecord);
136 SkTSwap(fPlayback, other.fPlayback);
137 SkTSwap(fWidth, other.fWidth);
138 SkTSwap(fHeight, other.fHeight);
139}
140
141///////////////////////////////////////////////////////////////////////////////
142
reed@android.comae814c82009-02-13 14:56:09 +0000143SkCanvas* SkPicture::beginRecording(int width, int height,
144 uint32_t recordingFlags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145 if (fPlayback) {
146 SkDELETE(fPlayback);
147 fPlayback = NULL;
148 }
149
150 if (NULL != fRecord) {
151 fRecord->unref();
152 fRecord = NULL;
153 }
154
reed@android.comae814c82009-02-13 14:56:09 +0000155 fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156
157 fWidth = width;
158 fHeight = height;
159
160 SkBitmap bm;
161 bm.setConfig(SkBitmap::kNo_Config, width, height);
162 fRecord->setBitmapDevice(bm);
reed@google.com82065d62011-02-07 15:30:46 +0000163
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 return fRecord;
165}
166
junov@chromium.org4866cc02012-06-01 21:23:07 +0000167bool SkPicture::hasRecorded() const {
168 return NULL != fRecord && fRecord->writeStream().size() > 0;
169}
170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171SkCanvas* SkPicture::getRecordingCanvas() const {
172 // will be null if we are not recording
173 return fRecord;
174}
175
176void SkPicture::endRecording() {
177 if (NULL == fPlayback) {
178 if (NULL != fRecord) {
junov@chromium.orga6c9e0e2012-07-12 17:47:34 +0000179 fRecord->endRecording();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
181 fRecord->unref();
182 fRecord = NULL;
183 }
184 }
185 SkASSERT(NULL == fRecord);
186}
187
188void SkPicture::draw(SkCanvas* surface) {
189 this->endRecording();
190 if (fPlayback) {
191 fPlayback->draw(*surface);
192 }
193}
194
195///////////////////////////////////////////////////////////////////////////////
196
197#include "SkStream.h"
198
reed@google.comed384952012-06-22 13:12:17 +0000199// V2 : adds SkPixelRef's generation ID.
200// V3 : PictInfo tag at beginning, and EOF tag at the end
reed@google.com67562092012-06-22 15:38:39 +0000201// V4 : move SkPictInfo to be the header
reed@google.com34342f62012-06-25 14:36:28 +0000202// V5 : don't read/write FunctionPtr on cross-process (we can detect that)
robertphillips@google.com01ec2eb2012-08-17 10:58:49 +0000203// V6 : added serialization of SkPath's bounds (and packed its flags tighter)
204#define PICTURE_VERSION 6
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205
206SkPicture::SkPicture(SkStream* stream) : SkRefCnt() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 fRecord = NULL;
208 fPlayback = NULL;
reed@google.com67562092012-06-22 15:38:39 +0000209 fWidth = fHeight = 0;
210
211 SkPictInfo info;
212
213 if (!stream->read(&info, sizeof(info))) {
214 return;
215 }
216 if (PICTURE_VERSION != info.fVersion) {
217 return;
218 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219
220 if (stream->readBool()) {
reed@google.com67562092012-06-22 15:38:39 +0000221 bool isValid = false;
222 fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream, info, &isValid));
223 if (!isValid) {
224 SkDELETE(fPlayback);
225 fPlayback = NULL;
226 return;
227 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 }
reed@google.com67562092012-06-22 15:38:39 +0000229
230 // do this at the end, so that they will be zero if we hit an error.
231 fWidth = info.fWidth;
232 fHeight = info.fHeight;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233}
234
235void SkPicture::serialize(SkWStream* stream) const {
236 SkPicturePlayback* playback = fPlayback;
reed@google.com82065d62011-02-07 15:30:46 +0000237
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 if (NULL == playback && fRecord) {
239 playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
240 }
241
reed@google.com67562092012-06-22 15:38:39 +0000242 SkPictInfo info;
243
244 info.fVersion = PICTURE_VERSION;
245 info.fWidth = fWidth;
246 info.fHeight = fHeight;
247 info.fFlags = SkPictInfo::kCrossProcess_Flag;
248#ifdef SK_SCALAR_IS_FLOAT
249 info.fFlags |= SkPictInfo::kScalarIsFloat_Flag;
250#endif
251 if (8 == sizeof(void*)) {
252 info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
253 }
254
255 stream->write(&info, sizeof(info));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 if (playback) {
257 stream->writeBool(true);
258 playback->serialize(stream);
259 // delete playback if it is a local version (i.e. cons'd up just now)
260 if (playback != fPlayback) {
261 SkDELETE(playback);
262 }
263 } else {
264 stream->writeBool(false);
265 }
266}
267
268void SkPicture::abortPlayback() {
269 if (NULL == fPlayback) {
270 return;
271 }
272 fPlayback->abort();
273}
274
275