| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2011 Google Inc. | 2 * Copyright 2011 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 #ifndef SkPictureFlat_DEFINED | 7 #ifndef SkPictureFlat_DEFINED |
| 8 #define SkPictureFlat_DEFINED | 8 #define SkPictureFlat_DEFINED |
| 9 | 9 |
| 10 | 10 |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 156 | 156 |
| 157 void setupBuffer(SkReadBuffer& buffer) const { | 157 void setupBuffer(SkReadBuffer& buffer) const { |
| 158 buffer.setFactoryPlayback(fArray, fCount); | 158 buffer.setFactoryPlayback(fArray, fCount); |
| 159 } | 159 } |
| 160 | 160 |
| 161 private: | 161 private: |
| 162 int fCount; | 162 int fCount; |
| 163 SkFlattenable::Factory* fArray; | 163 SkFlattenable::Factory* fArray; |
| 164 }; | 164 }; |
| 165 | 165 |
| 166 /////////////////////////////////////////////////////////////////////////////// | |
| 167 // | |
| 168 // | |
| 169 // The following templated classes provide an efficient way to store and compare | |
| 170 // objects that have been flattened (i.e. serialized in an ordered binary | |
| 171 // format). | |
| 172 // | |
| 173 // SkFlatData: is a simple indexable container for the flattened data | |
| 174 // which is agnostic to the type of data is is indexing. It is | |
| 175 // also responsible for flattening/unflattening objects but | |
| 176 // details of that operation are hidden in the provided traits | |
| 177 // SkFlatDictionary: is an abstract templated dictionary that maintains a | |
| 178 // searchable set of SkFlatData objects of type T. | |
| 179 // SkFlatController: is an interface provided to SkFlatDictionary which handles | |
| 180 // allocation (and unallocation in some cases). It also holds | |
| 181 // ref count recorders and the like. | |
| 182 // | |
| 183 // NOTE: any class that wishes to be used in conjunction with SkFlatDictionary m
ust subclass the | |
| 184 // dictionary and provide the necessary flattening traits. SkFlatController mus
t also be | |
| 185 // implemented, or SkChunkFlatController can be used to use an SkChunkAllocator
and never do | |
| 186 // replacements. | |
| 187 // | |
| 188 // | |
| 189 /////////////////////////////////////////////////////////////////////////////// | |
| 190 | |
| 191 class SkFlatData; | |
| 192 | |
| 193 class SkFlatController : public SkRefCnt { | |
| 194 public: | |
| 195 | |
| 196 | |
| 197 SkFlatController(uint32_t writeBufferFlags = 0); | |
| 198 virtual ~SkFlatController(); | |
| 199 /** | |
| 200 * Return a new block of memory for the SkFlatDictionary to use. | |
| 201 * This memory is owned by the controller and has the same lifetime unless y
ou | |
| 202 * call unalloc(), in which case it may be freed early. | |
| 203 */ | |
| 204 virtual void* allocThrow(size_t bytes) = 0; | |
| 205 | |
| 206 /** | |
| 207 * Hint that this block, which was allocated with allocThrow, is no longer n
eeded. | |
| 208 * The implementation may choose to free this memory any time beteween now a
nd destruction. | |
| 209 */ | |
| 210 virtual void unalloc(void* ptr) = 0; | |
| 211 | |
| 212 /** | |
| 213 * Used during creation and unflattening of SkFlatData objects. If the | |
| 214 * objects being flattened contain bitmaps they are stored in this heap | |
| 215 * and the flattenable stores the index to the bitmap on the heap. | |
| 216 * This should be set by the protected setBitmapHeap. | |
| 217 */ | |
| 218 SkBitmapHeap* getBitmapHeap() { return fBitmapHeap; } | |
| 219 | |
| 220 /** | |
| 221 * Used during creation of SkFlatData objects. If a typeface recorder is | |
| 222 * required to flatten the objects being flattened (i.e. for SkPaints), this | |
| 223 * should be set by the protected setTypefaceSet. | |
| 224 */ | |
| 225 SkRefCntSet* getTypefaceSet() { return fTypefaceSet; } | |
| 226 | |
| 227 /** | |
| 228 * Used during unflattening of the SkFlatData objects in the | |
| 229 * SkFlatDictionary. Needs to be set by the protected setTypefacePlayback | |
| 230 * and needs to be reset to the SkRefCntSet passed to setTypefaceSet. | |
| 231 */ | |
| 232 SkTypefacePlayback* getTypefacePlayback() { return fTypefacePlayback; } | |
| 233 | |
| 234 /** | |
| 235 * Flags to use during creation of SkFlatData objects. Defaults to zero. | |
| 236 */ | |
| 237 uint32_t getWriteBufferFlags() { return fWriteBufferFlags; } | |
| 238 | |
| 239 protected: | |
| 240 /** | |
| 241 * Set an SkBitmapHeap to be used to store/read SkBitmaps. Ref counted. | |
| 242 */ | |
| 243 void setBitmapHeap(SkBitmapHeap*); | |
| 244 | |
| 245 /** | |
| 246 * Set an SkRefCntSet to be used to store SkTypefaces during flattening. Ref | |
| 247 * counted. | |
| 248 */ | |
| 249 void setTypefaceSet(SkRefCntSet*); | |
| 250 | |
| 251 /** | |
| 252 * Set an SkTypefacePlayback to be used to find references to SkTypefaces | |
| 253 * during unflattening. Should be reset to the set provided to | |
| 254 * setTypefaceSet. | |
| 255 */ | |
| 256 void setTypefacePlayback(SkTypefacePlayback*); | |
| 257 | |
| 258 private: | |
| 259 SkBitmapHeap* fBitmapHeap; | |
| 260 SkRefCntSet* fTypefaceSet; | |
| 261 SkTypefacePlayback* fTypefacePlayback; | |
| 262 const uint32_t fWriteBufferFlags; | |
| 263 | |
| 264 typedef SkRefCnt INHERITED; | |
| 265 }; | |
| 266 | |
| 267 class SkFlatData { | |
| 268 public: | |
| 269 // Flatten obj into an SkFlatData with this index. controller owns the SkFl
atData*. | |
| 270 template <typename Traits, typename T> | |
| 271 static SkFlatData* Create(SkFlatController* controller, const T& obj, int in
dex) { | |
| 272 // A buffer of 256 bytes should fit most paints, regions, and matrices. | |
| 273 uint32_t storage[64]; | |
| 274 SkWriteBuffer buffer(storage, sizeof(storage), controller->getWriteBuffe
rFlags()); | |
| 275 | |
| 276 buffer.setBitmapHeap(controller->getBitmapHeap()); | |
| 277 buffer.setTypefaceRecorder(controller->getTypefaceSet()); | |
| 278 | |
| 279 Traits::Flatten(buffer, obj); | |
| 280 size_t size = buffer.bytesWritten(); | |
| 281 SkASSERT(SkIsAlign4(size)); | |
| 282 | |
| 283 // Allocate enough memory to hold SkFlatData struct and the flat data it
self. | |
| 284 size_t allocSize = sizeof(SkFlatData) + size; | |
| 285 SkFlatData* result = (SkFlatData*) controller->allocThrow(allocSize); | |
| 286 | |
| 287 // Put the serialized contents into the data section of the new allocati
on. | |
| 288 buffer.writeToMemory(result->data()); | |
| 289 // Stamp the index, size and checksum in the header. | |
| 290 result->stampHeader(index, SkToS32(size)); | |
| 291 return result; | |
| 292 } | |
| 293 | |
| 294 // Unflatten this into result, using bitmapHeap and facePlayback for bitmaps
and fonts if given | |
| 295 template <typename Traits, typename T> | |
| 296 void unflatten(T* result, | |
| 297 SkBitmapHeap* bitmapHeap = nullptr, | |
| 298 SkTypefacePlayback* facePlayback = nullptr) const { | |
| 299 SkReadBuffer buffer(this->data(), fFlatSize); | |
| 300 | |
| 301 if (bitmapHeap) { | |
| 302 buffer.setBitmapStorage(bitmapHeap); | |
| 303 } | |
| 304 if (facePlayback) { | |
| 305 facePlayback->setupBuffer(buffer); | |
| 306 } | |
| 307 | |
| 308 Traits::Unflatten(buffer, result); | |
| 309 SkASSERT(fFlatSize == (int32_t)buffer.offset()); | |
| 310 } | |
| 311 | |
| 312 // Do these contain the same data? Ignores index() and topBot(). | |
| 313 bool operator==(const SkFlatData& that) const { | |
| 314 if (this->checksum() != that.checksum() || this->flatSize() != that.flat
Size()) { | |
| 315 return false; | |
| 316 } | |
| 317 return memcmp(this->data(), that.data(), this->flatSize()) == 0; | |
| 318 } | |
| 319 | |
| 320 int index() const { return fIndex; } | |
| 321 const uint8_t* data() const { return (const uint8_t*)this + sizeof(*this); } | |
| 322 size_t flatSize() const { return fFlatSize; } | |
| 323 uint32_t checksum() const { return fChecksum; } | |
| 324 | |
| 325 // Returns true if fTopBot[] has been recorded. | |
| 326 bool isTopBotWritten() const { | |
| 327 return !SkScalarIsNaN(fTopBot[0]); | |
| 328 } | |
| 329 | |
| 330 // Returns fTopBot array, so it can be passed to a routine to compute them. | |
| 331 // For efficiency, we assert that fTopBot have not been recorded yet. | |
| 332 SkScalar* writableTopBot() const { | |
| 333 SkASSERT(!this->isTopBotWritten()); | |
| 334 return fTopBot; | |
| 335 } | |
| 336 | |
| 337 // Return the topbot[] after it has been recorded. | |
| 338 const SkScalar* topBot() const { | |
| 339 SkASSERT(this->isTopBotWritten()); | |
| 340 return fTopBot; | |
| 341 } | |
| 342 | |
| 343 private: | |
| 344 struct HashTraits { | |
| 345 static const SkFlatData& GetKey(const SkFlatData& flat) { return flat; } | |
| 346 static uint32_t Hash(const SkFlatData& flat) { return flat.checksum(); } | |
| 347 }; | |
| 348 | |
| 349 void setIndex(int index) { fIndex = index; } | |
| 350 uint8_t* data() { return (uint8_t*)this + sizeof(*this); } | |
| 351 | |
| 352 // This assumes the payload flat data has already been written and does not
modify it. | |
| 353 void stampHeader(int index, int32_t size) { | |
| 354 SkASSERT(SkIsAlign4(size)); | |
| 355 fIndex = index; | |
| 356 fFlatSize = size; | |
| 357 fTopBot[0] = SK_ScalarNaN; // Mark as unwritten. | |
| 358 fChecksum = SkChecksum::Murmur3(this->data(), size); | |
| 359 } | |
| 360 | |
| 361 int fIndex; | |
| 362 int32_t fFlatSize; | |
| 363 uint32_t fChecksum; | |
| 364 mutable SkScalar fTopBot[2]; // Cache of FontMetrics fTop, fBottom. Starts
as [NaN,?]. | |
| 365 // uint32_t flattenedData[] implicitly hangs off the end. | |
| 366 | |
| 367 template <typename T, typename Traits> friend class SkFlatDictionary; | |
| 368 }; | |
| 369 | |
| 370 template <typename T, typename Traits> | |
| 371 class SkFlatDictionary { | |
| 372 public: | |
| 373 explicit SkFlatDictionary(SkFlatController* controller) | |
| 374 : fController(SkRef(controller)) | |
| 375 , fScratch(controller->getWriteBufferFlags()) | |
| 376 , fReady(false) { | |
| 377 this->reset(); | |
| 378 } | |
| 379 | |
| 380 /** | |
| 381 * Clears the dictionary of all entries. However, it does NOT free the | |
| 382 * memory that was allocated for each entry (that's owned by controller). | |
| 383 */ | |
| 384 void reset() { | |
| 385 fIndexedData.rewind(); | |
| 386 } | |
| 387 | |
| 388 int count() const { | |
| 389 SkASSERT(fHash.count() == fIndexedData.count()); | |
| 390 return fHash.count(); | |
| 391 } | |
| 392 | |
| 393 // For testing only. Index is zero-based. | |
| 394 const SkFlatData* operator[](int index) { | |
| 395 return fIndexedData[index]; | |
| 396 } | |
| 397 | |
| 398 /** | |
| 399 * Given an element of type T return its 1-based index in the dictionary. If | |
| 400 * the element wasn't previously in the dictionary it is automatically | |
| 401 * added. | |
| 402 * | |
| 403 */ | |
| 404 int find(const T& element) { | |
| 405 return this->findAndReturnFlat(element)->index(); | |
| 406 } | |
| 407 | |
| 408 /** | |
| 409 * Similar to find. Allows the caller to specify an SkFlatData to replace in | |
| 410 * the case of an add. Also tells the caller whether a new SkFlatData was | |
| 411 * added and whether the old one was replaced. The parameters added and | |
| 412 * replaced are required to be non-nullptr. Rather than returning the index
of | |
| 413 * the entry in the dictionary, it returns the actual SkFlatData. | |
| 414 */ | |
| 415 const SkFlatData* findAndReplace(const T& element, | |
| 416 const SkFlatData* toReplace, | |
| 417 bool* added, | |
| 418 bool* replaced) { | |
| 419 SkASSERT(added != nullptr && replaced != nullptr); | |
| 420 | |
| 421 const int oldCount = this->count(); | |
| 422 SkFlatData* flat = this->findAndReturnMutableFlat(element); | |
| 423 *added = this->count() > oldCount; | |
| 424 | |
| 425 // If we don't want to replace anything, we're done. | |
| 426 if (!*added || toReplace == nullptr) { | |
| 427 *replaced = false; | |
| 428 return flat; | |
| 429 } | |
| 430 | |
| 431 // If we don't have the thing to replace, we're done. | |
| 432 const SkFlatData* found = fHash.find(*toReplace); | |
| 433 if (found == nullptr) { | |
| 434 *replaced = false; | |
| 435 return flat; | |
| 436 } | |
| 437 | |
| 438 // findAndReturnMutableFlat put flat at the back. Swap it into found->i
ndex() instead. | |
| 439 // indices in SkFlatData are 1-based, while fIndexedData is 0-based. Wa
tch out! | |
| 440 SkASSERT(flat->index() == this->count()); | |
| 441 flat->setIndex(found->index()); | |
| 442 fIndexedData.removeShuffle(found->index()-1); | |
| 443 SkASSERT(flat == fIndexedData[found->index()-1]); | |
| 444 | |
| 445 // findAndReturnMutableFlat already called fHash.add(), so we just clean
up the old entry. | |
| 446 fHash.remove(*found); | |
| 447 fController->unalloc((void*)found); | |
| 448 SkASSERT(this->count() == oldCount); | |
| 449 | |
| 450 *replaced = true; | |
| 451 return flat; | |
| 452 } | |
| 453 | |
| 454 /** | |
| 455 * Unflatten the specific object at the given index. | |
| 456 * Caller takes ownership of the result. | |
| 457 */ | |
| 458 T* unflatten(int index) const { | |
| 459 // index is 1-based, while fIndexedData is 0-based. | |
| 460 const SkFlatData* element = fIndexedData[index-1]; | |
| 461 SkASSERT(index == element->index()); | |
| 462 | |
| 463 T* dst = new T; | |
| 464 this->unflatten(dst, element); | |
| 465 return dst; | |
| 466 } | |
| 467 | |
| 468 /** | |
| 469 * Find or insert a flattened version of element into the dictionary. | |
| 470 * Caller does not take ownership of the result. This will not return nullp
tr. | |
| 471 */ | |
| 472 const SkFlatData* findAndReturnFlat(const T& element) { | |
| 473 return this->findAndReturnMutableFlat(element); | |
| 474 } | |
| 475 | |
| 476 private: | |
| 477 // We have to delay fScratch's initialization until its first use; fControll
er might not | |
| 478 // be fully set up by the time we get it in the constructor. | |
| 479 void lazyInit() { | |
| 480 if (fReady) { | |
| 481 return; | |
| 482 } | |
| 483 | |
| 484 // Without a bitmap heap, we'll flatten bitmaps into paints. That's nev
er what you want. | |
| 485 SkASSERT(fController->getBitmapHeap() != nullptr); | |
| 486 fScratch.setBitmapHeap(fController->getBitmapHeap()); | |
| 487 fScratch.setTypefaceRecorder(fController->getTypefaceSet()); | |
| 488 fReady = true; | |
| 489 } | |
| 490 | |
| 491 // As findAndReturnFlat, but returns a mutable pointer for internal use. | |
| 492 SkFlatData* findAndReturnMutableFlat(const T& element) { | |
| 493 // Only valid until the next call to resetScratch(). | |
| 494 const SkFlatData& scratch = this->resetScratch(element, this->count()+1)
; | |
| 495 | |
| 496 SkFlatData* candidate = fHash.find(scratch); | |
| 497 if (candidate != nullptr) { | |
| 498 return candidate; | |
| 499 } | |
| 500 | |
| 501 SkFlatData* detached = this->detachScratch(); | |
| 502 fHash.add(detached); | |
| 503 *fIndexedData.append() = detached; | |
| 504 SkASSERT(fIndexedData.top()->index() == this->count()); | |
| 505 return detached; | |
| 506 } | |
| 507 | |
| 508 // This reference is valid only until the next call to resetScratch() or det
achScratch(). | |
| 509 const SkFlatData& resetScratch(const T& element, int index) { | |
| 510 this->lazyInit(); | |
| 511 | |
| 512 // Layout of fScratch: [ SkFlatData header, 20 bytes ] [ data ..., 4-byt
e aligned ] | |
| 513 fScratch.reset(); | |
| 514 fScratch.reserve(sizeof(SkFlatData)); | |
| 515 Traits::Flatten(fScratch, element); | |
| 516 const size_t dataSize = fScratch.bytesWritten() - sizeof(SkFlatData); | |
| 517 | |
| 518 // Reinterpret data in fScratch as an SkFlatData. | |
| 519 SkFlatData* scratch = (SkFlatData*)fScratch.getWriter32()->contiguousArr
ay(); | |
| 520 SkASSERT(scratch != nullptr); | |
| 521 scratch->stampHeader(index, SkToS32(dataSize)); | |
| 522 return *scratch; | |
| 523 } | |
| 524 | |
| 525 // This result is owned by fController and lives as long as it does (unless
unalloc'd). | |
| 526 SkFlatData* detachScratch() { | |
| 527 // Allocate a new SkFlatData exactly big enough to hold our current scra
tch. | |
| 528 // We use the controller for this allocation to extend the allocation's
lifetime and allow | |
| 529 // the controller to do whatever memory management it wants. | |
| 530 SkFlatData* detached = (SkFlatData*)fController->allocThrow(fScratch.byt
esWritten()); | |
| 531 | |
| 532 // Copy scratch into the new SkFlatData. | |
| 533 SkFlatData* scratch = (SkFlatData*)fScratch.getWriter32()->contiguousArr
ay(); | |
| 534 SkASSERT(scratch != nullptr); | |
| 535 memcpy(detached, scratch, fScratch.bytesWritten()); | |
| 536 | |
| 537 // We can now reuse fScratch, and detached will live until fController d
ies. | |
| 538 return detached; | |
| 539 } | |
| 540 | |
| 541 void unflatten(T* dst, const SkFlatData* element) const { | |
| 542 element->unflatten<Traits>(dst, | |
| 543 fController->getBitmapHeap(), | |
| 544 fController->getTypefacePlayback()); | |
| 545 } | |
| 546 | |
| 547 // All SkFlatData* stored in fIndexedData and fHash are owned by the control
ler. | |
| 548 SkAutoTUnref<SkFlatController> fController; | |
| 549 SkWriteBuffer fScratch; | |
| 550 bool fReady; | |
| 551 | |
| 552 // For index -> SkFlatData. 0-based, while all indices in the API are 1-bas
ed. Careful! | |
| 553 SkTDArray<const SkFlatData*> fIndexedData; | |
| 554 | |
| 555 // For SkFlatData -> cached SkFlatData, which has index(). | |
| 556 SkTDynamicHash<SkFlatData, SkFlatData, SkFlatData::HashTraits> fHash; | |
| 557 }; | |
| 558 | |
| 559 #endif | 166 #endif |
| OLD | NEW |