SkPictureFlat.h revision cf52f5b7267a1f463d39d58cb6577030acca80df
1 2/* 3 * Copyright 2011 Google Inc. 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#ifndef SkPictureFlat_DEFINED 9#define SkPictureFlat_DEFINED 10 11//#define SK_DEBUG_SIZE 12 13#include "SkBitmapHeap.h" 14#include "SkChecksum.h" 15#include "SkChunkAlloc.h" 16#include "SkReadBuffer.h" 17#include "SkWriteBuffer.h" 18#include "SkPaint.h" 19#include "SkPicture.h" 20#include "SkPtrRecorder.h" 21#include "SkTDynamicHash.h" 22#include "SkTRefArray.h" 23 24enum DrawType { 25 UNUSED, 26 CLIP_PATH, 27 CLIP_REGION, 28 CLIP_RECT, 29 CLIP_RRECT, 30 CONCAT, 31 DRAW_BITMAP, 32 DRAW_BITMAP_MATRIX, 33 DRAW_BITMAP_NINE, 34 DRAW_BITMAP_RECT_TO_RECT, 35 DRAW_CLEAR, 36 DRAW_DATA, 37 DRAW_OVAL, 38 DRAW_PAINT, 39 DRAW_PATH, 40 DRAW_PICTURE, 41 DRAW_POINTS, 42 DRAW_POS_TEXT, 43 DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT 44 DRAW_POS_TEXT_H, 45 DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H 46 DRAW_RECT, 47 DRAW_RRECT, 48 DRAW_SPRITE, 49 DRAW_TEXT, 50 DRAW_TEXT_ON_PATH, 51 DRAW_TEXT_TOP_BOTTOM, // fast variant of DRAW_TEXT 52 DRAW_VERTICES, 53 RESTORE, 54 ROTATE, 55 SAVE, 56 SAVE_LAYER, 57 SCALE, 58 SET_MATRIX, 59 SKEW, 60 TRANSLATE, 61 NOOP, 62 BEGIN_COMMENT_GROUP, 63 COMMENT, 64 END_COMMENT_GROUP, 65 66 LAST_DRAWTYPE_ENUM = END_COMMENT_GROUP 67}; 68 69// In the 'match' method, this constant will match any flavor of DRAW_BITMAP* 70static const int kDRAW_BITMAP_FLAVOR = LAST_DRAWTYPE_ENUM+1; 71 72enum DrawVertexFlags { 73 DRAW_VERTICES_HAS_TEXS = 0x01, 74 DRAW_VERTICES_HAS_COLORS = 0x02, 75 DRAW_VERTICES_HAS_INDICES = 0x04, 76 DRAW_VERTICES_HAS_XFER = 0x08, 77}; 78 79/////////////////////////////////////////////////////////////////////////////// 80// clipparams are packed in 5 bits 81// doAA:1 | regionOp:4 82 83static inline uint32_t ClipParams_pack(SkRegion::Op op, bool doAA) { 84 unsigned doAABit = doAA ? 1 : 0; 85 return (doAABit << 4) | op; 86} 87 88static inline SkRegion::Op ClipParams_unpackRegionOp(uint32_t packed) { 89 return (SkRegion::Op)(packed & 0xF); 90} 91 92static inline bool ClipParams_unpackDoAA(uint32_t packed) { 93 return SkToBool((packed >> 4) & 1); 94} 95 96/////////////////////////////////////////////////////////////////////////////// 97 98class SkTypefacePlayback { 99public: 100 SkTypefacePlayback(); 101 virtual ~SkTypefacePlayback(); 102 103 int count() const { return fCount; } 104 105 void reset(const SkRefCntSet*); 106 107 void setCount(int count); 108 SkRefCnt* set(int index, SkRefCnt*); 109 110 void setupBuffer(SkReadBuffer& buffer) const { 111 buffer.setTypefaceArray((SkTypeface**)fArray, fCount); 112 } 113 114protected: 115 int fCount; 116 SkRefCnt** fArray; 117}; 118 119class SkFactoryPlayback { 120public: 121 SkFactoryPlayback(int count) : fCount(count) { 122 fArray = SkNEW_ARRAY(SkFlattenable::Factory, count); 123 } 124 125 ~SkFactoryPlayback() { 126 SkDELETE_ARRAY(fArray); 127 } 128 129 SkFlattenable::Factory* base() const { return fArray; } 130 131 void setupBuffer(SkReadBuffer& buffer) const { 132 buffer.setFactoryPlayback(fArray, fCount); 133 } 134 135private: 136 int fCount; 137 SkFlattenable::Factory* fArray; 138}; 139 140/////////////////////////////////////////////////////////////////////////////// 141// 142// 143// The following templated classes provide an efficient way to store and compare 144// objects that have been flattened (i.e. serialized in an ordered binary 145// format). 146// 147// SkFlatData: is a simple indexable container for the flattened data 148// which is agnostic to the type of data is is indexing. It is 149// also responsible for flattening/unflattening objects but 150// details of that operation are hidden in the provided traits 151// SkFlatDictionary: is an abstract templated dictionary that maintains a 152// searchable set of SkFlatData objects of type T. 153// SkFlatController: is an interface provided to SkFlatDictionary which handles 154// allocation (and unallocation in some cases). It also holds 155// ref count recorders and the like. 156// 157// NOTE: any class that wishes to be used in conjunction with SkFlatDictionary must subclass the 158// dictionary and provide the necessary flattening traits. SkFlatController must also be 159// implemented, or SkChunkFlatController can be used to use an SkChunkAllocator and never do 160// replacements. 161// 162// 163/////////////////////////////////////////////////////////////////////////////// 164 165class SkFlatData; 166 167class SkFlatController : public SkRefCnt { 168public: 169 SK_DECLARE_INST_COUNT(SkFlatController) 170 171 SkFlatController(uint32_t writeBufferFlags = 0); 172 virtual ~SkFlatController(); 173 /** 174 * Return a new block of memory for the SkFlatDictionary to use. 175 * This memory is owned by the controller and has the same lifetime unless you 176 * call unalloc(), in which case it may be freed early. 177 */ 178 virtual void* allocThrow(size_t bytes) = 0; 179 180 /** 181 * Hint that this block, which was allocated with allocThrow, is no longer needed. 182 * The implementation may choose to free this memory any time beteween now and destruction. 183 */ 184 virtual void unalloc(void* ptr) = 0; 185 186 /** 187 * Used during creation and unflattening of SkFlatData objects. If the 188 * objects being flattened contain bitmaps they are stored in this heap 189 * and the flattenable stores the index to the bitmap on the heap. 190 * This should be set by the protected setBitmapHeap. 191 */ 192 SkBitmapHeap* getBitmapHeap() { return fBitmapHeap; } 193 194 /** 195 * Used during creation of SkFlatData objects. If a typeface recorder is 196 * required to flatten the objects being flattened (i.e. for SkPaints), this 197 * should be set by the protected setTypefaceSet. 198 */ 199 SkRefCntSet* getTypefaceSet() { return fTypefaceSet; } 200 201 /** 202 * Used during unflattening of the SkFlatData objects in the 203 * SkFlatDictionary. Needs to be set by the protected setTypefacePlayback 204 * and needs to be reset to the SkRefCntSet passed to setTypefaceSet. 205 */ 206 SkTypefacePlayback* getTypefacePlayback() { return fTypefacePlayback; } 207 208 /** 209 * Optional factory recorder used during creation of SkFlatData objects. Set 210 * using the protected method setNamedFactorySet. 211 */ 212 SkNamedFactorySet* getNamedFactorySet() { return fFactorySet; } 213 214 /** 215 * Flags to use during creation of SkFlatData objects. Defaults to zero. 216 */ 217 uint32_t getWriteBufferFlags() { return fWriteBufferFlags; } 218 219protected: 220 /** 221 * Set an SkBitmapHeap to be used to store/read SkBitmaps. Ref counted. 222 */ 223 void setBitmapHeap(SkBitmapHeap*); 224 225 /** 226 * Set an SkRefCntSet to be used to store SkTypefaces during flattening. Ref 227 * counted. 228 */ 229 void setTypefaceSet(SkRefCntSet*); 230 231 /** 232 * Set an SkTypefacePlayback to be used to find references to SkTypefaces 233 * during unflattening. Should be reset to the set provided to 234 * setTypefaceSet. 235 */ 236 void setTypefacePlayback(SkTypefacePlayback*); 237 238 /** 239 * Set an SkNamedFactorySet to be used to store Factorys and their 240 * corresponding names during flattening. Ref counted. Returns the same 241 * set as a convenience. 242 */ 243 SkNamedFactorySet* setNamedFactorySet(SkNamedFactorySet*); 244 245private: 246 SkBitmapHeap* fBitmapHeap; 247 SkRefCntSet* fTypefaceSet; 248 SkTypefacePlayback* fTypefacePlayback; 249 SkNamedFactorySet* fFactorySet; 250 const uint32_t fWriteBufferFlags; 251 252 typedef SkRefCnt INHERITED; 253}; 254 255class SkFlatData { 256public: 257 // Flatten obj into an SkFlatData with this index. controller owns the SkFlatData*. 258 template <typename Traits, typename T> 259 static SkFlatData* Create(SkFlatController* controller, const T& obj, int index) { 260 // A buffer of 256 bytes should fit most paints, regions, and matrices. 261 uint32_t storage[64]; 262 SkWriteBuffer buffer(storage, sizeof(storage), controller->getWriteBufferFlags()); 263 264 buffer.setBitmapHeap(controller->getBitmapHeap()); 265 buffer.setTypefaceRecorder(controller->getTypefaceSet()); 266 buffer.setNamedFactoryRecorder(controller->getNamedFactorySet()); 267 268 Traits::Flatten(buffer, obj); 269 size_t size = buffer.bytesWritten(); 270 SkASSERT(SkIsAlign4(size)); 271 272 // Allocate enough memory to hold SkFlatData struct and the flat data itself. 273 size_t allocSize = sizeof(SkFlatData) + size; 274 SkFlatData* result = (SkFlatData*) controller->allocThrow(allocSize); 275 276 // Put the serialized contents into the data section of the new allocation. 277 buffer.writeToMemory(result->data()); 278 // Stamp the index, size and checksum in the header. 279 result->stampHeader(index, SkToS32(size)); 280 return result; 281 } 282 283 // Unflatten this into result, using bitmapHeap and facePlayback for bitmaps and fonts if given 284 template <typename Traits, typename T> 285 void unflatten(T* result, 286 SkBitmapHeap* bitmapHeap = NULL, 287 SkTypefacePlayback* facePlayback = NULL) const { 288 SkReadBuffer buffer(this->data(), fFlatSize); 289 290 if (bitmapHeap) { 291 buffer.setBitmapStorage(bitmapHeap); 292 } 293 if (facePlayback) { 294 facePlayback->setupBuffer(buffer); 295 } 296 297 Traits::Unflatten(buffer, result); 298 SkASSERT(fFlatSize == (int32_t)buffer.offset()); 299 } 300 301 // Do these contain the same data? Ignores index() and topBot(). 302 bool operator==(const SkFlatData& that) const { 303 if (this->checksum() != that.checksum() || this->flatSize() != that.flatSize()) { 304 return false; 305 } 306 return memcmp(this->data(), that.data(), this->flatSize()) == 0; 307 } 308 309 int index() const { return fIndex; } 310 const uint8_t* data() const { return (const uint8_t*)this + sizeof(*this); } 311 size_t flatSize() const { return fFlatSize; } 312 uint32_t checksum() const { return fChecksum; } 313 314 // Returns true if fTopBot[] has been recorded. 315 bool isTopBotWritten() const { 316 return !SkScalarIsNaN(fTopBot[0]); 317 } 318 319 // Returns fTopBot array, so it can be passed to a routine to compute them. 320 // For efficiency, we assert that fTopBot have not been recorded yet. 321 SkScalar* writableTopBot() const { 322 SkASSERT(!this->isTopBotWritten()); 323 return fTopBot; 324 } 325 326 // Return the topbot[] after it has been recorded. 327 const SkScalar* topBot() const { 328 SkASSERT(this->isTopBotWritten()); 329 return fTopBot; 330 } 331 332private: 333 // For SkTDynamicHash. 334 static const SkFlatData& Identity(const SkFlatData& flat) { return flat; } 335 static uint32_t Hash(const SkFlatData& flat) { return flat.checksum(); } 336 static bool Equal(const SkFlatData& a, const SkFlatData& b) { return a == b; } 337 338 void setIndex(int index) { fIndex = index; } 339 uint8_t* data() { return (uint8_t*)this + sizeof(*this); } 340 341 // This assumes the payload flat data has already been written and does not modify it. 342 void stampHeader(int index, int32_t size) { 343 SkASSERT(SkIsAlign4(size)); 344 fIndex = index; 345 fFlatSize = size; 346 fTopBot[0] = SK_ScalarNaN; // Mark as unwritten. 347 fChecksum = SkChecksum::Compute((uint32_t*)this->data(), size); 348 } 349 350 int fIndex; 351 int32_t fFlatSize; 352 uint32_t fChecksum; 353 mutable SkScalar fTopBot[2]; // Cache of FontMetrics fTop, fBottom. Starts as [NaN,?]. 354 // uint32_t flattenedData[] implicitly hangs off the end. 355 356 template <typename T, typename Traits> friend class SkFlatDictionary; 357}; 358 359template <typename T, typename Traits> 360class SkFlatDictionary { 361public: 362 explicit SkFlatDictionary(SkFlatController* controller) 363 : fController(SkRef(controller)) 364 , fScratch(controller->getWriteBufferFlags()) 365 , fReady(false) { 366 this->reset(); 367 } 368 369 /** 370 * Clears the dictionary of all entries. However, it does NOT free the 371 * memory that was allocated for each entry (that's owned by controller). 372 */ 373 void reset() { 374 fIndexedData.rewind(); 375 } 376 377 int count() const { 378 SkASSERT(fHash.count() == fIndexedData.count()); 379 return fHash.count(); 380 } 381 382 // For testing only. Index is zero-based. 383 const SkFlatData* operator[](int index) { 384 return fIndexedData[index]; 385 } 386 387 /** 388 * Given an element of type T return its 1-based index in the dictionary. If 389 * the element wasn't previously in the dictionary it is automatically 390 * added. 391 * 392 */ 393 int find(const T& element) { 394 return this->findAndReturnFlat(element)->index(); 395 } 396 397 /** 398 * Similar to find. Allows the caller to specify an SkFlatData to replace in 399 * the case of an add. Also tells the caller whether a new SkFlatData was 400 * added and whether the old one was replaced. The parameters added and 401 * replaced are required to be non-NULL. Rather than returning the index of 402 * the entry in the dictionary, it returns the actual SkFlatData. 403 */ 404 const SkFlatData* findAndReplace(const T& element, 405 const SkFlatData* toReplace, 406 bool* added, 407 bool* replaced) { 408 SkASSERT(added != NULL && replaced != NULL); 409 410 const int oldCount = this->count(); 411 SkFlatData* flat = this->findAndReturnMutableFlat(element); 412 *added = this->count() > oldCount; 413 414 // If we don't want to replace anything, we're done. 415 if (!*added || toReplace == NULL) { 416 *replaced = false; 417 return flat; 418 } 419 420 // If we don't have the thing to replace, we're done. 421 const SkFlatData* found = fHash.find(*toReplace); 422 if (found == NULL) { 423 *replaced = false; 424 return flat; 425 } 426 427 // findAndReturnMutableFlat put flat at the back. Swap it into found->index() instead. 428 // indices in SkFlatData are 1-based, while fIndexedData is 0-based. Watch out! 429 SkASSERT(flat->index() == this->count()); 430 flat->setIndex(found->index()); 431 fIndexedData.removeShuffle(found->index()-1); 432 SkASSERT(flat == fIndexedData[found->index()-1]); 433 434 // findAndReturnMutableFlat already called fHash.add(), so we just clean up the old entry. 435 fHash.remove(*found); 436 fController->unalloc((void*)found); 437 SkASSERT(this->count() == oldCount); 438 439 *replaced = true; 440 return flat; 441 } 442 443 /** 444 * Unflatten the objects and return them in SkTRefArray, or return NULL 445 * if there no objects. Caller takes ownership of result. 446 */ 447 SkTRefArray<T>* unflattenToArray() const { 448 const int count = this->count(); 449 if (count == 0) { 450 return NULL; 451 } 452 SkTRefArray<T>* array = SkTRefArray<T>::Create(count); 453 for (int i = 0; i < count; i++) { 454 this->unflatten(&array->writableAt(i), fIndexedData[i]); 455 } 456 return array; 457 } 458 459 /** 460 * Unflatten the specific object at the given index. 461 * Caller takes ownership of the result. 462 */ 463 T* unflatten(int index) const { 464 // index is 1-based, while fIndexedData is 0-based. 465 const SkFlatData* element = fIndexedData[index-1]; 466 SkASSERT(index == element->index()); 467 468 T* dst = new T; 469 this->unflatten(dst, element); 470 return dst; 471 } 472 473 /** 474 * Find or insert a flattened version of element into the dictionary. 475 * Caller does not take ownership of the result. This will not return NULL. 476 */ 477 const SkFlatData* findAndReturnFlat(const T& element) { 478 return this->findAndReturnMutableFlat(element); 479 } 480 481private: 482 // We have to delay fScratch's initialization until its first use; fController might not 483 // be fully set up by the time we get it in the constructor. 484 void lazyInit() { 485 if (fReady) { 486 return; 487 } 488 489 // Without a bitmap heap, we'll flatten bitmaps into paints. That's never what you want. 490 SkASSERT(fController->getBitmapHeap() != NULL); 491 fScratch.setBitmapHeap(fController->getBitmapHeap()); 492 fScratch.setTypefaceRecorder(fController->getTypefaceSet()); 493 fScratch.setNamedFactoryRecorder(fController->getNamedFactorySet()); 494 fReady = true; 495 } 496 497 // As findAndReturnFlat, but returns a mutable pointer for internal use. 498 SkFlatData* findAndReturnMutableFlat(const T& element) { 499 // Only valid until the next call to resetScratch(). 500 const SkFlatData& scratch = this->resetScratch(element, this->count()+1); 501 502 SkFlatData* candidate = fHash.find(scratch); 503 if (candidate != NULL) return candidate; 504 505 SkFlatData* detached = this->detachScratch(); 506 fHash.add(detached); 507 *fIndexedData.append() = detached; 508 SkASSERT(fIndexedData.top()->index() == this->count()); 509 return detached; 510 } 511 512 // This reference is valid only until the next call to resetScratch() or detachScratch(). 513 const SkFlatData& resetScratch(const T& element, int index) { 514 this->lazyInit(); 515 516 // Layout of fScratch: [ SkFlatData header, 20 bytes ] [ data ..., 4-byte aligned ] 517 fScratch.reset(); 518 fScratch.reserve(sizeof(SkFlatData)); 519 Traits::Flatten(fScratch, element); 520 const size_t dataSize = fScratch.bytesWritten() - sizeof(SkFlatData); 521 522 // Reinterpret data in fScratch as an SkFlatData. 523 SkFlatData* scratch = (SkFlatData*)fScratch.getWriter32()->contiguousArray(); 524 SkASSERT(scratch != NULL); 525 scratch->stampHeader(index, dataSize); 526 return *scratch; 527 } 528 529 // This result is owned by fController and lives as long as it does (unless unalloc'd). 530 SkFlatData* detachScratch() { 531 // Allocate a new SkFlatData exactly big enough to hold our current scratch. 532 // We use the controller for this allocation to extend the allocation's lifetime and allow 533 // the controller to do whatever memory management it wants. 534 SkFlatData* detached = (SkFlatData*)fController->allocThrow(fScratch.bytesWritten()); 535 536 // Copy scratch into the new SkFlatData. 537 SkFlatData* scratch = (SkFlatData*)fScratch.getWriter32()->contiguousArray(); 538 SkASSERT(scratch != NULL); 539 memcpy(detached, scratch, fScratch.bytesWritten()); 540 541 // We can now reuse fScratch, and detached will live until fController dies. 542 return detached; 543 } 544 545 void unflatten(T* dst, const SkFlatData* element) const { 546 element->unflatten<Traits>(dst, 547 fController->getBitmapHeap(), 548 fController->getTypefacePlayback()); 549 } 550 551 // All SkFlatData* stored in fIndexedData and fHash are owned by the controller. 552 SkAutoTUnref<SkFlatController> fController; 553 SkWriteBuffer fScratch; 554 bool fReady; 555 556 // For index -> SkFlatData. 0-based, while all indices in the API are 1-based. Careful! 557 SkTDArray<const SkFlatData*> fIndexedData; 558 559 // For SkFlatData -> cached SkFlatData, which has index(). 560 SkTDynamicHash<SkFlatData, SkFlatData, 561 SkFlatData::Identity, SkFlatData::Hash, SkFlatData::Equal> fHash; 562}; 563 564typedef SkFlatDictionary<SkPaint, SkPaint::FlatteningTraits> SkPaintDictionary; 565 566class SkChunkFlatController : public SkFlatController { 567public: 568 SkChunkFlatController(size_t minSize) 569 : fHeap(minSize) 570 , fTypefaceSet(SkNEW(SkRefCntSet)) 571 , fLastAllocated(NULL) { 572 this->setTypefaceSet(fTypefaceSet); 573 this->setTypefacePlayback(&fTypefacePlayback); 574 } 575 576 virtual void* allocThrow(size_t bytes) SK_OVERRIDE { 577 fLastAllocated = fHeap.allocThrow(bytes); 578 return fLastAllocated; 579 } 580 581 virtual void unalloc(void* ptr) SK_OVERRIDE { 582 // fHeap can only free a pointer if it was the last one allocated. Otherwise, we'll just 583 // have to wait until fHeap is destroyed. 584 if (ptr == fLastAllocated) (void)fHeap.unalloc(ptr); 585 } 586 587 void setupPlaybacks() const { 588 fTypefacePlayback.reset(fTypefaceSet.get()); 589 } 590 591 void setBitmapStorage(SkBitmapHeap* heap) { 592 this->setBitmapHeap(heap); 593 } 594 595private: 596 SkChunkAlloc fHeap; 597 SkAutoTUnref<SkRefCntSet> fTypefaceSet; 598 void* fLastAllocated; 599 mutable SkTypefacePlayback fTypefacePlayback; 600}; 601 602#endif 603