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, ©Info)); 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