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 |