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 29#define DUMP_BUFFER_SIZE 65536 30 31//#define ENABLE_TIME_DRAW // dumps milliseconds for each draw 32 33 34#ifdef SK_DEBUG 35// enable SK_DEBUG_TRACE to trace DrawType elements when 36// recorded and played back 37// #define SK_DEBUG_TRACE 38// enable SK_DEBUG_SIZE to see the size of picture components 39// #define SK_DEBUG_SIZE 40// enable SK_DEBUG_DUMP to see the contents of recorded elements 41// #define SK_DEBUG_DUMP 42// enable SK_DEBUG_VALIDATE to check internal structures for consistency 43// #define SK_DEBUG_VALIDATE 44#endif 45 46#if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP 47const char* DrawTypeToString(DrawType drawType) { 48 switch (drawType) { 49 case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break; 50 case CLIP_PATH: return "CLIP_PATH"; 51 case CLIP_REGION: return "CLIP_REGION"; 52 case CLIP_RECT: return "CLIP_RECT"; 53 case CLIP_RRECT: return "CLIP_RRECT"; 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_NINE: return "DRAW_BITMAP_NINE"; 58 case DRAW_BITMAP_RECT_TO_RECT: return "DRAW_BITMAP_RECT_TO_RECT"; 59 case DRAW_CLEAR: return "DRAW_CLEAR"; 60 case DRAW_DATA: return "DRAW_DATA"; 61 case DRAW_OVAL: return "DRAW_OVAL"; 62 case DRAW_PAINT: return "DRAW_PAINT"; 63 case DRAW_PATH: return "DRAW_PATH"; 64 case DRAW_PICTURE: return "DRAW_PICTURE"; 65 case DRAW_POINTS: return "DRAW_POINTS"; 66 case DRAW_POS_TEXT: return "DRAW_POS_TEXT"; 67 case DRAW_POS_TEXT_TOP_BOTTOM: return "DRAW_POS_TEXT_TOP_BOTTOM"; 68 case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H"; 69 case DRAW_POS_TEXT_H_TOP_BOTTOM: return "DRAW_POS_TEXT_H_TOP_BOTTOM"; 70 case DRAW_RECT: return "DRAW_RECT"; 71 case DRAW_RRECT: return "DRAW_RRECT"; 72 case DRAW_SPRITE: return "DRAW_SPRITE"; 73 case DRAW_TEXT: return "DRAW_TEXT"; 74 case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH"; 75 case DRAW_TEXT_TOP_BOTTOM: return "DRAW_TEXT_TOP_BOTTOM"; 76 case DRAW_VERTICES: return "DRAW_VERTICES"; 77 case RESTORE: return "RESTORE"; 78 case ROTATE: return "ROTATE"; 79 case SAVE: return "SAVE"; 80 case SAVE_LAYER: return "SAVE_LAYER"; 81 case SCALE: return "SCALE"; 82 case SET_MATRIX: return "SET_MATRIX"; 83 case SKEW: return "SKEW"; 84 case TRANSLATE: return "TRANSLATE"; 85 case NOOP: return "NOOP"; 86 default: 87 SkDebugf("DrawType error 0x%08x\n", drawType); 88 SkASSERT(0); 89 break; 90 } 91 SkASSERT(0); 92 return NULL; 93} 94#endif 95 96#ifdef SK_DEBUG_VALIDATE 97static void validateMatrix(const SkMatrix* matrix) { 98 SkScalar scaleX = matrix->getScaleX(); 99 SkScalar scaleY = matrix->getScaleY(); 100 SkScalar skewX = matrix->getSkewX(); 101 SkScalar skewY = matrix->getSkewY(); 102 SkScalar perspX = matrix->getPerspX(); 103 SkScalar perspY = matrix->getPerspY(); 104 if (scaleX != 0 && skewX != 0) 105 SkDebugf("scaleX != 0 && skewX != 0\n"); 106 SkASSERT(scaleX == 0 || skewX == 0); 107 SkASSERT(scaleY == 0 || skewY == 0); 108 SkASSERT(perspX == 0); 109 SkASSERT(perspY == 0); 110} 111#endif 112 113 114/////////////////////////////////////////////////////////////////////////////// 115 116SkPicture::SkPicture() { 117 fRecord = NULL; 118 fPlayback = NULL; 119 fWidth = fHeight = 0; 120} 121 122SkPicture::SkPicture(const SkPicture& src) : INHERITED() { 123 fWidth = src.fWidth; 124 fHeight = src.fHeight; 125 fRecord = NULL; 126 127 /* We want to copy the src's playback. However, if that hasn't been built 128 yet, we need to fake a call to endRecording() without actually calling 129 it (since it is destructive, and we don't want to change src). 130 */ 131 if (src.fPlayback) { 132 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback)); 133 } else if (src.fRecord) { 134 // here we do a fake src.endRecording() 135 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fRecord)); 136 } else { 137 fPlayback = NULL; 138 } 139} 140 141SkPicture::~SkPicture() { 142 SkSafeUnref(fRecord); 143 SkDELETE(fPlayback); 144} 145 146void SkPicture::swap(SkPicture& other) { 147 SkTSwap(fRecord, other.fRecord); 148 SkTSwap(fPlayback, other.fPlayback); 149 SkTSwap(fWidth, other.fWidth); 150 SkTSwap(fHeight, other.fHeight); 151} 152 153SkPicture* SkPicture::clone() const { 154 SkPicture* clonedPicture = SkNEW(SkPicture); 155 clone(clonedPicture, 1); 156 return clonedPicture; 157} 158 159void SkPicture::clone(SkPicture* pictures, int count) const { 160 SkPictCopyInfo copyInfo; 161 162 for (int i = 0; i < count; i++) { 163 SkPicture* clone = &pictures[i]; 164 165 clone->fWidth = fWidth; 166 clone->fHeight = fHeight; 167 clone->fRecord = NULL; 168 169 if (NULL != clone->fRecord) { 170 clone->fRecord->unref(); 171 clone->fRecord = NULL; 172 } 173 SkDELETE(clone->fPlayback); 174 175 /* We want to copy the src's playback. However, if that hasn't been built 176 yet, we need to fake a call to endRecording() without actually calling 177 it (since it is destructive, and we don't want to change src). 178 */ 179 if (fPlayback) { 180 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fPlayback, ©Info)); 181 } else if (fRecord) { 182 // here we do a fake src.endRecording() 183 clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord, true)); 184 } else { 185 clone->fPlayback = NULL; 186 } 187 } 188} 189 190/////////////////////////////////////////////////////////////////////////////// 191 192SkCanvas* SkPicture::beginRecording(int width, int height, 193 uint32_t recordingFlags) { 194 if (fPlayback) { 195 SkDELETE(fPlayback); 196 fPlayback = NULL; 197 } 198 199 if (NULL != fRecord) { 200 fRecord->unref(); 201 fRecord = NULL; 202 } 203 204 SkBitmap bm; 205 bm.setConfig(SkBitmap::kNo_Config, width, height); 206 SkAutoTUnref<SkBaseDevice> dev(SkNEW_ARGS(SkBitmapDevice, (bm))); 207 208 // Must be set before calling createBBoxHierarchy 209 fWidth = width; 210 fHeight = height; 211 212 if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) { 213 SkBBoxHierarchy* tree = this->createBBoxHierarchy(); 214 SkASSERT(NULL != tree); 215 fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (recordingFlags, tree, dev)); 216 tree->unref(); 217 } else { 218 fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags, dev)); 219 } 220 fRecord->beginRecording(); 221 222 return fRecord; 223} 224 225SkBBoxHierarchy* SkPicture::createBBoxHierarchy() const { 226 // These values were empirically determined to produce reasonable 227 // performance in most cases. 228 static const int kRTreeMinChildren = 6; 229 static const int kRTreeMaxChildren = 11; 230 231 SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth), 232 SkIntToScalar(fHeight)); 233 bool sortDraws = false; // Do not sort draw calls when bulk loading. 234 235 return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren, 236 aspectRatio, sortDraws); 237} 238 239SkCanvas* SkPicture::getRecordingCanvas() const { 240 // will be null if we are not recording 241 return fRecord; 242} 243 244void SkPicture::endRecording() { 245 if (NULL == fPlayback) { 246 if (NULL != fRecord) { 247 fRecord->endRecording(); 248 fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord)); 249 fRecord->unref(); 250 fRecord = NULL; 251 } 252 } 253 SkASSERT(NULL == fRecord); 254} 255 256void SkPicture::draw(SkCanvas* surface, SkDrawPictureCallback* callback) { 257 this->endRecording(); 258 if (fPlayback) { 259 fPlayback->draw(*surface, callback); 260 } 261} 262 263/////////////////////////////////////////////////////////////////////////////// 264 265#include "SkStream.h" 266 267static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; 268 269bool SkPicture::StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) { 270 if (NULL == stream) { 271 return false; 272 } 273 274 // Check magic bytes. 275 char magic[sizeof(kMagic)]; 276 stream->read(magic, sizeof(kMagic)); 277 if (0 != memcmp(magic, kMagic, sizeof(kMagic))) { 278 return false; 279 } 280 281 SkPictInfo info; 282 if (!stream->read(&info, sizeof(SkPictInfo))) { 283 return false; 284 } 285 286 if (PICTURE_VERSION != info.fVersion 287#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO 288 // V16 is backwards compatible with V15 289 && PRIOR_PICTURE_VERSION != info.fVersion // TODO: remove when .skps regenerated 290#endif 291 ) { 292 return false; 293 } 294 295 if (pInfo != NULL) { 296 *pInfo = info; 297 } 298 return true; 299} 300 301SkPicture::SkPicture(SkPicturePlayback* playback, int width, int height) 302 : fPlayback(playback) 303 , fRecord(NULL) 304 , fWidth(width) 305 , fHeight(height) {} 306 307SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) { 308 SkPictInfo info; 309 310 if (!StreamIsSKP(stream, &info)) { 311 return NULL; 312 } 313 314 SkPicturePlayback* playback; 315 // Check to see if there is a playback to recreate. 316 if (stream->readBool()) { 317 playback = SkPicturePlayback::CreateFromStream(stream, info, proc); 318 if (NULL == playback) { 319 return NULL; 320 } 321 } else { 322 playback = NULL; 323 } 324 325 return SkNEW_ARGS(SkPicture, (playback, info.fWidth, info.fHeight)); 326} 327 328void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const { 329 SkPicturePlayback* playback = fPlayback; 330 331 if (NULL == playback && fRecord) { 332 playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord)); 333 } 334 335 SkPictInfo info; 336 337 info.fVersion = PICTURE_VERSION; 338 info.fWidth = fWidth; 339 info.fHeight = fHeight; 340 info.fFlags = SkPictInfo::kCrossProcess_Flag; 341#ifdef SK_SCALAR_IS_FLOAT 342 info.fFlags |= SkPictInfo::kScalarIsFloat_Flag; 343#endif 344 if (8 == sizeof(void*)) { 345 info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag; 346 } 347 348 // Write 8 magic bytes to ID this file format. 349 SkASSERT(sizeof(kMagic) == 8); 350 stream->write(kMagic, sizeof(kMagic)); 351 352 stream->write(&info, sizeof(info)); 353 if (playback) { 354 stream->writeBool(true); 355 playback->serialize(stream, encoder); 356 // delete playback if it is a local version (i.e. cons'd up just now) 357 if (playback != fPlayback) { 358 SkDELETE(playback); 359 } 360 } else { 361 stream->writeBool(false); 362 } 363} 364 365bool SkPicture::willPlayBackBitmaps() const { 366 if (!fPlayback) return false; 367 return fPlayback->containsBitmaps(); 368} 369 370#ifdef SK_BUILD_FOR_ANDROID 371void SkPicture::abortPlayback() { 372 if (NULL == fPlayback) { 373 return; 374 } 375 fPlayback->abort(); 376} 377#endif 378