blob: c3623eec699e2946c9780ccc32eec25120c3f76a [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#ifndef SkPictureFlat_DEFINED
9#define SkPictureFlat_DEFINED
10
reed@google.come2589ae2012-07-10 19:38:01 +000011//#define SK_DEBUG_SIZE
12
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkBitmap.h"
djsollen@google.com21830d92012-08-07 19:49:41 +000014#include "SkBitmapHeap.h"
mtklein@google.com9e3074e2013-08-20 16:48:47 +000015#include "SkChecksum.h"
16#include "SkChunkAlloc.h"
17#include "SkMatrix.h"
djsollen@google.com2b2ede32012-04-12 13:24:04 +000018#include "SkOrderedReadBuffer.h"
djsollen@google.comd2700ee2012-05-30 16:54:13 +000019#include "SkOrderedWriteBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkPaint.h"
21#include "SkPath.h"
mtklein@google.com9e3074e2013-08-20 16:48:47 +000022#include "SkPicture.h"
23#include "SkPtrRecorder.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkRegion.h"
mtklein@google.com9e3074e2013-08-20 16:48:47 +000025#include "SkTDynamicHash.h"
reed@google.comf4cc1872012-07-23 15:04:45 +000026#include "SkTRefArray.h"
djsollen@google.comd2700ee2012-05-30 16:54:13 +000027#include "SkTSearch.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
29enum DrawType {
30 UNUSED,
31 CLIP_PATH,
32 CLIP_REGION,
33 CLIP_RECT,
reed@google.com4ed0fb72012-12-12 20:48:18 +000034 CLIP_RRECT,
reed@android.com8a1c16f2008-12-17 15:59:43 +000035 CONCAT,
36 DRAW_BITMAP,
37 DRAW_BITMAP_MATRIX,
reed@google.comf0b5e112011-09-07 11:57:34 +000038 DRAW_BITMAP_NINE,
reed@google.com71121732012-09-18 15:14:33 +000039 DRAW_BITMAP_RECT_TO_RECT,
reed@google.com2a981812011-04-14 18:59:28 +000040 DRAW_CLEAR,
reed@android.comcb608442009-12-04 21:32:27 +000041 DRAW_DATA,
reed@google.com4ed0fb72012-12-12 20:48:18 +000042 DRAW_OVAL,
reed@android.com8a1c16f2008-12-17 15:59:43 +000043 DRAW_PAINT,
44 DRAW_PATH,
45 DRAW_PICTURE,
46 DRAW_POINTS,
47 DRAW_POS_TEXT,
reed@google.com9efd9a02012-01-30 15:41:43 +000048 DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT
reed@android.com8a1c16f2008-12-17 15:59:43 +000049 DRAW_POS_TEXT_H,
50 DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
51 DRAW_RECT,
reed@google.com4ed0fb72012-12-12 20:48:18 +000052 DRAW_RRECT,
reed@android.com8a1c16f2008-12-17 15:59:43 +000053 DRAW_SPRITE,
54 DRAW_TEXT,
55 DRAW_TEXT_ON_PATH,
56 DRAW_TEXT_TOP_BOTTOM, // fast variant of DRAW_TEXT
57 DRAW_VERTICES,
58 RESTORE,
59 ROTATE,
60 SAVE,
61 SAVE_LAYER,
62 SCALE,
reed@android.com6e073b92009-01-06 15:03:30 +000063 SET_MATRIX,
reed@android.com8a1c16f2008-12-17 15:59:43 +000064 SKEW,
reed@google.comffacd3c2012-08-30 15:31:23 +000065 TRANSLATE,
robertphillips@google.come4ce5b82013-02-15 17:19:15 +000066 NOOP,
robertphillips@google.com0a4805e2013-05-29 13:24:23 +000067 BEGIN_COMMENT_GROUP,
68 COMMENT,
69 END_COMMENT_GROUP,
reed@google.comffacd3c2012-08-30 15:31:23 +000070
robertphillips@google.com0a4805e2013-05-29 13:24:23 +000071 LAST_DRAWTYPE_ENUM = END_COMMENT_GROUP
reed@android.com8a1c16f2008-12-17 15:59:43 +000072};
73
robertphillips@google.comb8f96102013-03-12 15:39:44 +000074// In the 'match' method, this constant will match any flavor of DRAW_BITMAP*
75static const int kDRAW_BITMAP_FLAVOR = LAST_DRAWTYPE_ENUM+1;
76
reed@android.com8a1c16f2008-12-17 15:59:43 +000077enum DrawVertexFlags {
78 DRAW_VERTICES_HAS_TEXS = 0x01,
79 DRAW_VERTICES_HAS_COLORS = 0x02,
reed@google.com85e143c2013-12-30 15:51:25 +000080 DRAW_VERTICES_HAS_INDICES = 0x04,
81 DRAW_VERTICES_HAS_XFER = 0x08,
reed@android.com8a1c16f2008-12-17 15:59:43 +000082};
83
reed@google.com83ab4952011-11-11 21:34:54 +000084///////////////////////////////////////////////////////////////////////////////
85// clipparams are packed in 5 bits
86// doAA:1 | regionOp:4
87
88static inline uint32_t ClipParams_pack(SkRegion::Op op, bool doAA) {
89 unsigned doAABit = doAA ? 1 : 0;
90 return (doAABit << 4) | op;
91}
92
93static inline SkRegion::Op ClipParams_unpackRegionOp(uint32_t packed) {
94 return (SkRegion::Op)(packed & 0xF);
95}
96
97static inline bool ClipParams_unpackDoAA(uint32_t packed) {
98 return SkToBool((packed >> 4) & 1);
99}
100
101///////////////////////////////////////////////////////////////////////////////
102
djsollen@google.com21830d92012-08-07 19:49:41 +0000103class SkTypefacePlayback {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104public:
djsollen@google.com21830d92012-08-07 19:49:41 +0000105 SkTypefacePlayback();
106 virtual ~SkTypefacePlayback();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 int count() const { return fCount; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000109
mike@reedtribe.orge9e08cc2011-04-29 01:44:52 +0000110 void reset(const SkRefCntSet*);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111
112 void setCount(int count);
113 SkRefCnt* set(int index, SkRefCnt*);
114
djsollen@google.com21830d92012-08-07 19:49:41 +0000115 void setupBuffer(SkOrderedReadBuffer& buffer) const {
116 buffer.setTypefaceArray((SkTypeface**)fArray, fCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000118
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119protected:
120 int fCount;
121 SkRefCnt** fArray;
122};
123
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124class SkFactoryPlayback {
125public:
126 SkFactoryPlayback(int count) : fCount(count) {
127 fArray = SkNEW_ARRAY(SkFlattenable::Factory, count);
128 }
129
130 ~SkFactoryPlayback() {
131 SkDELETE_ARRAY(fArray);
132 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000133
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 SkFlattenable::Factory* base() const { return fArray; }
135
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000136 void setupBuffer(SkOrderedReadBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137 buffer.setFactoryPlayback(fArray, fCount);
138 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000139
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140private:
141 int fCount;
142 SkFlattenable::Factory* fArray;
143};
144
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000145///////////////////////////////////////////////////////////////////////////////
146//
147//
148// The following templated classes provide an efficient way to store and compare
149// objects that have been flattened (i.e. serialized in an ordered binary
150// format).
151//
152// SkFlatData: is a simple indexable container for the flattened data
153// which is agnostic to the type of data is is indexing. It is
154// also responsible for flattening/unflattening objects but
155// details of that operation are hidden in the provided procs
scroggo@google.com4dffc592012-07-17 16:49:40 +0000156// SkFlatDictionary: is an abstract templated dictionary that maintains a
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000157// searchable set of SkFlatData objects of type T.
scroggo@google.com4dffc592012-07-17 16:49:40 +0000158// SkFlatController: is an interface provided to SkFlatDictionary which handles
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000159// allocation (and unallocation in some cases). It also holds
scroggo@google.com15543602012-08-02 18:49:49 +0000160// ref count recorders and the like.
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000161//
162// NOTE: any class that wishes to be used in conjunction with SkFlatDictionary
163// must subclass the dictionary and provide the necessary flattening procs.
164// The end of this header contains dictionary subclasses for some common classes
scroggo@google.com4dffc592012-07-17 16:49:40 +0000165// like SkBitmap, SkMatrix, SkPaint, and SkRegion. SkFlatController must also
166// be implemented, or SkChunkFlatController can be used to use an
167// SkChunkAllocator and never do replacements.
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000168//
169//
170///////////////////////////////////////////////////////////////////////////////
171
scroggo@google.com4dffc592012-07-17 16:49:40 +0000172class SkFlatData;
173
174class SkFlatController : public SkRefCnt {
175public:
robertphillips@google.coma22e2112012-08-16 14:58:06 +0000176 SK_DECLARE_INST_COUNT(SkFlatController)
177
scroggo@google.com15543602012-08-02 18:49:49 +0000178 SkFlatController();
179 virtual ~SkFlatController();
scroggo@google.com4dffc592012-07-17 16:49:40 +0000180 /**
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000181 * Return a new block of memory for the SkFlatDictionary to use.
182 * This memory is owned by the controller and has the same lifetime unless you
183 * call unalloc(), in which case it may be freed early.
scroggo@google.com4dffc592012-07-17 16:49:40 +0000184 */
185 virtual void* allocThrow(size_t bytes) = 0;
186
187 /**
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000188 * Hint that this block, which was allocated with allocThrow, is no longer needed.
189 * The implementation may choose to free this memory any time beteween now and destruction.
scroggo@google.com4dffc592012-07-17 16:49:40 +0000190 */
191 virtual void unalloc(void* ptr) = 0;
scroggo@google.com0c3e5fe2012-08-01 19:34:20 +0000192
scroggo@google.com15543602012-08-02 18:49:49 +0000193 /**
djsollen@google.com21830d92012-08-07 19:49:41 +0000194 * Used during creation and unflattening of SkFlatData objects. If the
195 * objects being flattened contain bitmaps they are stored in this heap
196 * and the flattenable stores the index to the bitmap on the heap.
197 * This should be set by the protected setBitmapHeap.
scroggo@google.com15543602012-08-02 18:49:49 +0000198 */
djsollen@google.com21830d92012-08-07 19:49:41 +0000199 SkBitmapHeap* getBitmapHeap() { return fBitmapHeap; }
scroggo@google.com15543602012-08-02 18:49:49 +0000200
201 /**
202 * Used during creation of SkFlatData objects. If a typeface recorder is
203 * required to flatten the objects being flattened (i.e. for SkPaints), this
204 * should be set by the protected setTypefaceSet.
205 */
206 SkRefCntSet* getTypefaceSet() { return fTypefaceSet; }
207
208 /**
209 * Used during unflattening of the SkFlatData objects in the
210 * SkFlatDictionary. Needs to be set by the protected setTypefacePlayback
211 * and needs to be reset to the SkRefCntSet passed to setTypefaceSet.
212 */
213 SkTypefacePlayback* getTypefacePlayback() { return fTypefacePlayback; }
214
215 /**
216 * Optional factory recorder used during creation of SkFlatData objects. Set
217 * using the protected method setNamedFactorySet.
218 */
219 SkNamedFactorySet* getNamedFactorySet() { return fFactorySet; }
220
scroggo@google.com664fab12012-08-14 19:22:05 +0000221 /**
222 * Flags to use during creation of SkFlatData objects. Defaults to zero.
223 */
224 uint32_t getWriteBufferFlags() { return fWriteBufferFlags; }
225
scroggo@google.com15543602012-08-02 18:49:49 +0000226protected:
227 /**
djsollen@google.com21830d92012-08-07 19:49:41 +0000228 * Set an SkBitmapHeap to be used to store/read SkBitmaps. Ref counted.
scroggo@google.com15543602012-08-02 18:49:49 +0000229 */
djsollen@google.com21830d92012-08-07 19:49:41 +0000230 void setBitmapHeap(SkBitmapHeap*);
scroggo@google.com15543602012-08-02 18:49:49 +0000231
232 /**
233 * Set an SkRefCntSet to be used to store SkTypefaces during flattening. Ref
234 * counted.
235 */
236 void setTypefaceSet(SkRefCntSet*);
237
238 /**
scroggo@google.com15543602012-08-02 18:49:49 +0000239 * Set an SkTypefacePlayback to be used to find references to SkTypefaces
240 * during unflattening. Should be reset to the set provided to
241 * setTypefaceSet.
242 */
243 void setTypefacePlayback(SkTypefacePlayback*);
244
245 /**
246 * Set an SkNamedFactorySet to be used to store Factorys and their
247 * corresponding names during flattening. Ref counted. Returns the same
248 * set as a convenience.
249 */
250 SkNamedFactorySet* setNamedFactorySet(SkNamedFactorySet*);
251
scroggo@google.com664fab12012-08-14 19:22:05 +0000252 /**
253 * Set the flags to be used during flattening.
254 */
255 void setWriteBufferFlags(uint32_t flags) { fWriteBufferFlags = flags; }
256
scroggo@google.com15543602012-08-02 18:49:49 +0000257private:
djsollen@google.com21830d92012-08-07 19:49:41 +0000258 SkBitmapHeap* fBitmapHeap;
scroggo@google.com15543602012-08-02 18:49:49 +0000259 SkRefCntSet* fTypefaceSet;
scroggo@google.com15543602012-08-02 18:49:49 +0000260 SkTypefacePlayback* fTypefacePlayback;
261 SkNamedFactorySet* fFactorySet;
scroggo@google.com664fab12012-08-14 19:22:05 +0000262 uint32_t fWriteBufferFlags;
robertphillips@google.coma22e2112012-08-16 14:58:06 +0000263
264 typedef SkRefCnt INHERITED;
scroggo@google.com4dffc592012-07-17 16:49:40 +0000265};
266
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267class SkFlatData {
268public:
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000269 // Flatten obj into an SkFlatData with this index. controller owns the SkFlatData*.
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000270 template <typename Traits, typename T>
271 static SkFlatData* Create(SkFlatController* controller, const T& obj, int index) {
272 // A buffer of 256 bytes should fit most paints, regions, and matrices.
273 uint32_t storage[64];
274 SkOrderedWriteBuffer buffer(256, storage, sizeof(storage));
scroggo@google.com4dffc592012-07-17 16:49:40 +0000275
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000276 buffer.setBitmapHeap(controller->getBitmapHeap());
277 buffer.setTypefaceRecorder(controller->getTypefaceSet());
278 buffer.setNamedFactoryRecorder(controller->getNamedFactorySet());
279 buffer.setFlags(controller->getWriteBufferFlags());
280
281 Traits::flatten(buffer, obj);
282 uint32_t size = buffer.size();
283 SkASSERT(SkIsAlign4(size));
284
285 // Allocate enough memory to hold SkFlatData struct and the flat data itself.
286 size_t allocSize = sizeof(SkFlatData) + size;
287 SkFlatData* result = (SkFlatData*) controller->allocThrow(allocSize);
288
289 // Put the serialized contents into the data section of the new allocation.
290 buffer.writeToMemory(result->data());
291 // Stamp the index, size and checksum in the header.
292 result->stampHeader(index, size);
293 return result;
294 }
295
296 // Unflatten this into result, using bitmapHeap and facePlayback for bitmaps and fonts if given
297 template <typename Traits, typename T>
298 void unflatten(T* result,
djsollen@google.com21830d92012-08-07 19:49:41 +0000299 SkBitmapHeap* bitmapHeap = NULL,
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000300 SkTypefacePlayback* facePlayback = NULL) const {
301 SkOrderedReadBuffer buffer(this->data(), fFlatSize);
302
303 if (bitmapHeap) {
304 buffer.setBitmapStorage(bitmapHeap);
305 }
306 if (facePlayback) {
307 facePlayback->setupBuffer(buffer);
308 }
309
310 Traits::unflatten(buffer, result);
311 SkASSERT(fFlatSize == (int32_t)buffer.offset());
312 }
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000313
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000314 // Do these contain the same data? Ignores index() and topBot().
315 bool operator==(const SkFlatData& that) const {
316 if (this->checksum() != that.checksum() || this->flatSize() != that.flatSize()) {
317 return false;
318 }
319 return memcmp(this->data(), that.data(), this->flatSize()) == 0;
reed@google.come2589ae2012-07-10 19:38:01 +0000320 }
321
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000322 int index() const { return fIndex; }
323 const uint8_t* data() const { return (const uint8_t*)this + sizeof(*this); }
324 size_t flatSize() const { return fFlatSize; }
325 uint32_t checksum() const { return fChecksum; }
326
327 // Returns true if fTopBot[] has been recorded.
bungeman@google.com148a3962013-01-17 20:19:13 +0000328 bool isTopBotWritten() const {
329 return !SkScalarIsNaN(fTopBot[0]);
reed@google.com45954262012-12-07 17:14:40 +0000330 }
331
332 // Returns fTopBot array, so it can be passed to a routine to compute them.
333 // For efficiency, we assert that fTopBot have not been recorded yet.
junov@chromium.orgf3b12232013-01-22 17:50:47 +0000334 SkScalar* writableTopBot() const {
bungeman@google.com148a3962013-01-17 20:19:13 +0000335 SkASSERT(!this->isTopBotWritten());
reed@google.com45954262012-12-07 17:14:40 +0000336 return fTopBot;
337 }
338
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000339 // Return the topbot[] after it has been recorded.
reed@google.com45954262012-12-07 17:14:40 +0000340 const SkScalar* topBot() const {
bungeman@google.com148a3962013-01-17 20:19:13 +0000341 SkASSERT(this->isTopBotWritten());
reed@google.com45954262012-12-07 17:14:40 +0000342 return fTopBot;
343 }
344
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000345private:
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000346 // For SkTDynamicHash.
347 static const SkFlatData& Identity(const SkFlatData& flat) { return flat; }
348 static uint32_t Hash(const SkFlatData& flat) { return flat.checksum(); }
349 static bool Equal(const SkFlatData& a, const SkFlatData& b) { return a == b; }
350
351 void setIndex(int index) { fIndex = index; }
352 uint8_t* data() { return (uint8_t*)this + sizeof(*this); }
353
354 // This assumes the payload flat data has already been written and does not modify it.
355 void stampHeader(int index, int32_t size) {
356 SkASSERT(SkIsAlign4(size));
357 fIndex = index;
358 fFlatSize = size;
359 fTopBot[0] = SK_ScalarNaN; // Mark as unwritten.
360 fChecksum = SkChecksum::Compute((uint32_t*)this->data(), size);
361 }
362
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363 int fIndex;
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000364 int32_t fFlatSize;
junov@chromium.orgef760602012-06-27 20:03:16 +0000365 uint32_t fChecksum;
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000366 mutable SkScalar fTopBot[2]; // Cache of FontMetrics fTop, fBottom. Starts as [NaN,?].
367 // uint32_t flattenedData[] implicitly hangs off the end.
reed@google.comb4ed0172012-07-10 13:29:52 +0000368
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000369 template <typename T, typename Traits, int kScratchSizeGuess> friend class SkFlatDictionary;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370};
371
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000372template <typename T, typename Traits, int kScratchSizeGuess=0>
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000373class SkFlatDictionary {
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000374 static const size_t kWriteBufferGrowthBytes = 1024;
375
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376public:
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000377 explicit SkFlatDictionary(SkFlatController* controller)
378 : fController(SkRef(controller))
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000379 , fScratchSize(0)
380 , fScratch(NULL)
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000381 , fWriteBuffer(kWriteBufferGrowthBytes)
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000382 , fReady(false) {
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000383 this->reset();
384 }
385
386 /**
387 * Clears the dictionary of all entries. However, it does NOT free the
388 * memory that was allocated for each entry (that's owned by controller).
389 */
390 void reset() {
391 fIndexedData.rewind();
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000392 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000394 ~SkFlatDictionary() {
395 sk_free(fScratch);
scroggo@google.com4dffc592012-07-17 16:49:40 +0000396 }
397
skia.committer@gmail.com0f12e1f2013-03-11 07:01:23 +0000398 int count() const {
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000399 SkASSERT(fHash.count() == fIndexedData.count());
commit-bot@chromium.orgf85251c2014-01-10 19:37:00 +0000400 return fHash.count();
robertphillips@google.com7d3451b2013-03-10 17:16:41 +0000401 }
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000402
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000403 // For testing only. Index is zero-based.
404 const SkFlatData* operator[](int index) {
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000405 return fIndexedData[index];
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000406 }
407
408 /**
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000409 * Given an element of type T return its 1-based index in the dictionary. If
410 * the element wasn't previously in the dictionary it is automatically
411 * added.
412 *
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000413 */
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000414 int find(const T& element) {
415 return this->findAndReturnFlat(element)->index();
reed@google.com83ca3372012-07-12 15:27:54 +0000416 }
scroggo@google.com4dffc592012-07-17 16:49:40 +0000417
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000418 /**
scroggo@google.com4dffc592012-07-17 16:49:40 +0000419 * Similar to find. Allows the caller to specify an SkFlatData to replace in
420 * the case of an add. Also tells the caller whether a new SkFlatData was
421 * added and whether the old one was replaced. The parameters added and
422 * replaced are required to be non-NULL. Rather than returning the index of
423 * the entry in the dictionary, it returns the actual SkFlatData.
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000424 */
scroggo@google.com4dffc592012-07-17 16:49:40 +0000425 const SkFlatData* findAndReplace(const T& element,
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000426 const SkFlatData* toReplace,
427 bool* added,
scroggo@google.com4dffc592012-07-17 16:49:40 +0000428 bool* replaced) {
429 SkASSERT(added != NULL && replaced != NULL);
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000430
431 const int oldCount = this->count();
432 SkFlatData* flat = this->findAndReturnMutableFlat(element);
433 *added = this->count() > oldCount;
434
435 // If we don't want to replace anything, we're done.
436 if (!*added || toReplace == NULL) {
437 *replaced = false;
438 return flat;
reed@google.com83ca3372012-07-12 15:27:54 +0000439 }
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000440
441 // If we don't have the thing to replace, we're done.
442 const SkFlatData* found = fHash.find(*toReplace);
443 if (found == NULL) {
444 *replaced = false;
445 return flat;
446 }
447
commit-bot@chromium.orgf85251c2014-01-10 19:37:00 +0000448 // findAndReturnMutableFlat put flat at the back. Swap it into found->index() instead.
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000449 // indices in SkFlatData are 1-based, while fIndexedData is 0-based. Watch out!
commit-bot@chromium.orgf85251c2014-01-10 19:37:00 +0000450 SkASSERT(flat->index() == this->count());
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000451 flat->setIndex(found->index());
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000452 fIndexedData.removeShuffle(found->index()-1);
453 SkASSERT(flat == fIndexedData[found->index()-1]);
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000454
455 // findAndReturnMutableFlat already called fHash.add(), so we just clean up the old entry.
456 fHash.remove(*found);
457 fController->unalloc((void*)found);
458 SkASSERT(this->count() == oldCount);
459
460 *replaced = true;
scroggo@google.com4dffc592012-07-17 16:49:40 +0000461 return flat;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 }
463
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000464 /**
reed@google.comf4cc1872012-07-23 15:04:45 +0000465 * Unflatten the objects and return them in SkTRefArray, or return NULL
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000466 * if there no objects. Caller takes ownership of result.
reed@google.comf4cc1872012-07-23 15:04:45 +0000467 */
scroggo@google.com15543602012-08-02 18:49:49 +0000468 SkTRefArray<T>* unflattenToArray() const {
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000469 const int count = this->count();
470 if (count == 0) {
471 return NULL;
472 }
473 SkTRefArray<T>* array = SkTRefArray<T>::Create(count);
474 for (int i = 0; i < count; i++) {
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000475 this->unflatten(&array->writableAt(i), fIndexedData[i]);
reed@google.comf4cc1872012-07-23 15:04:45 +0000476 }
477 return array;
478 }
479
robertphillips@google.come37ad352013-03-01 19:44:30 +0000480 /**
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000481 * Unflatten the specific object at the given index.
482 * Caller takes ownership of the result.
robertphillips@google.come37ad352013-03-01 19:44:30 +0000483 */
484 T* unflatten(int index) const {
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000485 // index is 1-based, while fIndexedData is 0-based.
486 const SkFlatData* element = fIndexedData[index-1];
robertphillips@google.com7d3451b2013-03-10 17:16:41 +0000487 SkASSERT(index == element->index());
robertphillips@google.come37ad352013-03-01 19:44:30 +0000488
robertphillips@google.com7d3451b2013-03-10 17:16:41 +0000489 T* dst = new T;
490 this->unflatten(dst, element);
491 return dst;
robertphillips@google.come37ad352013-03-01 19:44:30 +0000492 }
493
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000494 /**
495 * Find or insert a flattened version of element into the dictionary.
496 * Caller does not take ownership of the result. This will not return NULL.
497 */
scroggo@google.com664fab12012-08-14 19:22:05 +0000498 const SkFlatData* findAndReturnFlat(const T& element) {
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000499 return this->findAndReturnMutableFlat(element);
scroggo@google.com4dffc592012-07-17 16:49:40 +0000500 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000501
junov@chromium.orgf3b12232013-01-22 17:50:47 +0000502private:
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000503 // Layout: [ SkFlatData header, 20 bytes ] [ data ..., 4-byte aligned ]
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000504 static size_t SizeWithPadding(size_t flatDataSize) {
505 SkASSERT(SkIsAlign4(flatDataSize));
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000506 return sizeof(SkFlatData) + flatDataSize;
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000507 }
508
509 // Allocate a new scratch SkFlatData. Must be sk_freed.
510 static SkFlatData* AllocScratch(size_t scratchSize) {
511 return (SkFlatData*) sk_malloc_throw(SizeWithPadding(scratchSize));
512 }
513
514 // We have to delay fWriteBuffer's initialization until its first use; fController might not
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000515 // be fully set up by the time we get it in the constructor. We also delay allocating fScratch
516 // to avoid unnecessary heap allocations, since we're paying the price of the conditional
517 // anyway.
518 void lazyInit() {
519 if (fReady) {
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000520 return;
521 }
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000522
523 fScratchSize = kScratchSizeGuess;
524 fScratch = AllocScratch(fScratchSize);
525
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000526 // Without a bitmap heap, we'll flatten bitmaps into paints. That's never what you want.
527 SkASSERT(fController->getBitmapHeap() != NULL);
528 fWriteBuffer.setBitmapHeap(fController->getBitmapHeap());
529 fWriteBuffer.setTypefaceRecorder(fController->getTypefaceSet());
530 fWriteBuffer.setNamedFactoryRecorder(fController->getNamedFactorySet());
531 fWriteBuffer.setFlags(fController->getWriteBufferFlags());
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000532 fReady = true;
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000533 }
534
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000535 // As findAndReturnFlat, but returns a mutable pointer for internal use.
536 SkFlatData* findAndReturnMutableFlat(const T& element) {
537 // Only valid until the next call to resetScratch().
commit-bot@chromium.orgf85251c2014-01-10 19:37:00 +0000538 const SkFlatData& scratch = this->resetScratch(element, this->count()+1);
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000539
540 SkFlatData* candidate = fHash.find(scratch);
541 if (candidate != NULL) return candidate;
542
543 SkFlatData* detached = this->detachScratch();
544 fHash.add(detached);
commit-bot@chromium.orgf85251c2014-01-10 19:37:00 +0000545 *fIndexedData.append() = detached;
546 SkASSERT(fIndexedData.top()->index() == this->count());
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000547 return detached;
548 }
549
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000550 // This reference is valid only until the next call to resetScratch() or detachScratch().
551 const SkFlatData& resetScratch(const T& element, int index) {
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000552 this->lazyInit();
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000553
554 // Flatten element into fWriteBuffer (using fScratch as storage).
555 fWriteBuffer.reset(fScratch->data(), fScratchSize);
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000556 Traits::flatten(fWriteBuffer, element);
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000557 const size_t bytesWritten = fWriteBuffer.bytesWritten();
558
559 // If all the flattened bytes fit into fScratch, we can skip a call to writeToMemory.
560 if (!fWriteBuffer.wroteOnlyToStorage()) {
561 SkASSERT(bytesWritten > fScratchSize);
562 // It didn't all fit. Copy into a larger replacement SkFlatData.
563 // We can't just realloc because it might move the pointer and confuse writeToMemory.
564 SkFlatData* larger = AllocScratch(bytesWritten);
565 fWriteBuffer.writeToMemory(larger->data());
566
567 // Carry on with this larger scratch to minimize the likelihood of future resizing.
568 sk_free(fScratch);
569 fScratchSize = bytesWritten;
570 fScratch = larger;
571 }
572
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000573 // The data is in fScratch now but we need to stamp its header.
574 fScratch->stampHeader(index, bytesWritten);
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000575 return *fScratch;
576 }
577
578 // This result is owned by fController and lives as long as it does (unless unalloc'd).
579 SkFlatData* detachScratch() {
580 // Allocate a new SkFlatData exactly big enough to hold our current scratch.
581 // We use the controller for this allocation to extend the allocation's lifetime and allow
582 // the controller to do whatever memory management it wants.
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000583 SkASSERT(fScratch != NULL);
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000584 const size_t paddedSize = SizeWithPadding(fScratch->flatSize());
585 SkFlatData* detached = (SkFlatData*)fController->allocThrow(paddedSize);
586
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000587 // Copy scratch into the new SkFlatData.
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000588 memcpy(detached, fScratch, paddedSize);
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000589
590 // We can now reuse fScratch, and detached will live until fController dies.
591 return detached;
592 }
593
robertphillips@google.come37ad352013-03-01 19:44:30 +0000594 void unflatten(T* dst, const SkFlatData* element) const {
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000595 element->unflatten<Traits>(dst,
596 fController->getBitmapHeap(),
597 fController->getTypefacePlayback());
robertphillips@google.come37ad352013-03-01 19:44:30 +0000598 }
599
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000600 // All SkFlatData* stored in fIndexedData and fHash are owned by the controller.
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000601 SkAutoTUnref<SkFlatController> fController;
602 size_t fScratchSize; // How many bytes fScratch has allocated for data itself.
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000603 SkFlatData* fScratch; // Owned, lazily allocated, must be freed with sk_free.
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000604 SkOrderedWriteBuffer fWriteBuffer;
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000605 bool fReady;
robertphillips@google.com7d3451b2013-03-10 17:16:41 +0000606
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000607 // For index -> SkFlatData. 0-based, while all indices in the API are 1-based. Careful!
robertphillips@google.com7d3451b2013-03-10 17:16:41 +0000608 SkTDArray<const SkFlatData*> fIndexedData;
reed@google.com83ca3372012-07-12 15:27:54 +0000609
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000610 // For SkFlatData -> cached SkFlatData, which has index().
611 SkTDynamicHash<SkFlatData, SkFlatData,
612 SkFlatData::Identity, SkFlatData::Hash, SkFlatData::Equal> fHash;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613};
614
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000615///////////////////////////////////////////////////////////////////////////////
616// Some common dictionaries are defined here for both reference and convenience
617///////////////////////////////////////////////////////////////////////////////
618
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000619struct SkMatrixTraits {
620 static void flatten(SkOrderedWriteBuffer& buffer, const SkMatrix& matrix) {
621 buffer.getWriter32()->writeMatrix(matrix);
622 }
623 static void unflatten(SkOrderedReadBuffer& buffer, SkMatrix* matrix) {
624 buffer.getReader32()->readMatrix(matrix);
625 }
626};
627typedef SkFlatDictionary<SkMatrix, SkMatrixTraits, 36> SkMatrixDictionary;
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000628
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000629
630struct SkRegionTraits {
631 static void flatten(SkOrderedWriteBuffer& buffer, const SkRegion& region) {
632 buffer.getWriter32()->writeRegion(region);
633 }
634 static void unflatten(SkOrderedReadBuffer& buffer, SkRegion* region) {
635 buffer.getReader32()->readRegion(region);
636 }
637};
638typedef SkFlatDictionary<SkRegion, SkRegionTraits> SkRegionDictionary;
639
640
641struct SkPaintTraits {
642 static void flatten(SkOrderedWriteBuffer& buffer, const SkPaint& paint) {
643 paint.flatten(buffer);
644 }
645 static void unflatten(SkOrderedReadBuffer& buffer, SkPaint* paint) {
646 paint->unflatten(buffer);
647 }
648};
649typedef SkFlatDictionary<SkPaint, SkPaintTraits, 512> SkPaintDictionary;
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000650
scroggo@google.com4dffc592012-07-17 16:49:40 +0000651class SkChunkFlatController : public SkFlatController {
652public:
653 SkChunkFlatController(size_t minSize)
scroggo@google.com15543602012-08-02 18:49:49 +0000654 : fHeap(minSize)
commit-bot@chromium.org46724e42013-07-30 21:54:10 +0000655 , fTypefaceSet(SkNEW(SkRefCntSet))
656 , fLastAllocated(NULL) {
scroggo@google.com15543602012-08-02 18:49:49 +0000657 this->setTypefaceSet(fTypefaceSet);
scroggo@google.com15543602012-08-02 18:49:49 +0000658 this->setTypefacePlayback(&fTypefacePlayback);
659 }
scroggo@google.com4dffc592012-07-17 16:49:40 +0000660
scroggo@google.com15543602012-08-02 18:49:49 +0000661 virtual void* allocThrow(size_t bytes) SK_OVERRIDE {
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000662 fLastAllocated = fHeap.allocThrow(bytes);
663 return fLastAllocated;
scroggo@google.com4dffc592012-07-17 16:49:40 +0000664 }
665
scroggo@google.com15543602012-08-02 18:49:49 +0000666 virtual void unalloc(void* ptr) SK_OVERRIDE {
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000667 // fHeap can only free a pointer if it was the last one allocated. Otherwise, we'll just
668 // have to wait until fHeap is destroyed.
669 if (ptr == fLastAllocated) (void)fHeap.unalloc(ptr);
scroggo@google.com4dffc592012-07-17 16:49:40 +0000670 }
scroggo@google.com15543602012-08-02 18:49:49 +0000671
djsollen@google.com21830d92012-08-07 19:49:41 +0000672 void setupPlaybacks() const {
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000673 fTypefacePlayback.reset(fTypefaceSet.get());
scroggo@google.com15543602012-08-02 18:49:49 +0000674 }
675
djsollen@google.com21830d92012-08-07 19:49:41 +0000676 void setBitmapStorage(SkBitmapHeap* heap) {
677 this->setBitmapHeap(heap);
scroggo@google.com15543602012-08-02 18:49:49 +0000678 }
679
scroggo@google.com4dffc592012-07-17 16:49:40 +0000680private:
scroggo@google.com15543602012-08-02 18:49:49 +0000681 SkChunkAlloc fHeap;
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000682 SkAutoTUnref<SkRefCntSet> fTypefaceSet;
683 void* fLastAllocated;
scroggo@google.com15543602012-08-02 18:49:49 +0000684 mutable SkTypefacePlayback fTypefacePlayback;
scroggo@google.com4dffc592012-07-17 16:49:40 +0000685};
686
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687#endif