blob: 7973e65398188d720fd06db7fedcb25512a3a438 [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
djsollen@google.com21830d92012-08-07 19:49:41 +000013#include "SkBitmapHeap.h"
mtklein@google.com9e3074e2013-08-20 16:48:47 +000014#include "SkChecksum.h"
15#include "SkChunkAlloc.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000016#include "SkReadBuffer.h"
17#include "SkWriteBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkPaint.h"
mtklein@google.com9e3074e2013-08-20 16:48:47 +000019#include "SkPicture.h"
20#include "SkPtrRecorder.h"
mtklein@google.com9e3074e2013-08-20 16:48:47 +000021#include "SkTDynamicHash.h"
reed@google.comf4cc1872012-07-23 15:04:45 +000022#include "SkTRefArray.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023
24enum DrawType {
25 UNUSED,
26 CLIP_PATH,
27 CLIP_REGION,
28 CLIP_RECT,
reed@google.com4ed0fb72012-12-12 20:48:18 +000029 CLIP_RRECT,
reed@android.com8a1c16f2008-12-17 15:59:43 +000030 CONCAT,
31 DRAW_BITMAP,
32 DRAW_BITMAP_MATRIX,
reed@google.comf0b5e112011-09-07 11:57:34 +000033 DRAW_BITMAP_NINE,
reed@google.com71121732012-09-18 15:14:33 +000034 DRAW_BITMAP_RECT_TO_RECT,
reed@google.com2a981812011-04-14 18:59:28 +000035 DRAW_CLEAR,
reed@android.comcb608442009-12-04 21:32:27 +000036 DRAW_DATA,
reed@google.com4ed0fb72012-12-12 20:48:18 +000037 DRAW_OVAL,
reed@android.com8a1c16f2008-12-17 15:59:43 +000038 DRAW_PAINT,
39 DRAW_PATH,
40 DRAW_PICTURE,
41 DRAW_POINTS,
42 DRAW_POS_TEXT,
reed@google.com9efd9a02012-01-30 15:41:43 +000043 DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT
reed@android.com8a1c16f2008-12-17 15:59:43 +000044 DRAW_POS_TEXT_H,
45 DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
46 DRAW_RECT,
reed@google.com4ed0fb72012-12-12 20:48:18 +000047 DRAW_RRECT,
reed@android.com8a1c16f2008-12-17 15:59:43 +000048 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,
reed@android.com6e073b92009-01-06 15:03:30 +000058 SET_MATRIX,
reed@android.com8a1c16f2008-12-17 15:59:43 +000059 SKEW,
reed@google.comffacd3c2012-08-30 15:31:23 +000060 TRANSLATE,
robertphillips@google.come4ce5b82013-02-15 17:19:15 +000061 NOOP,
robertphillips@google.com0a4805e2013-05-29 13:24:23 +000062 BEGIN_COMMENT_GROUP,
63 COMMENT,
64 END_COMMENT_GROUP,
reed@google.comffacd3c2012-08-30 15:31:23 +000065
robertphillips@google.com0a4805e2013-05-29 13:24:23 +000066 LAST_DRAWTYPE_ENUM = END_COMMENT_GROUP
reed@android.com8a1c16f2008-12-17 15:59:43 +000067};
68
robertphillips@google.comb8f96102013-03-12 15:39:44 +000069// In the 'match' method, this constant will match any flavor of DRAW_BITMAP*
70static const int kDRAW_BITMAP_FLAVOR = LAST_DRAWTYPE_ENUM+1;
71
reed@android.com8a1c16f2008-12-17 15:59:43 +000072enum DrawVertexFlags {
73 DRAW_VERTICES_HAS_TEXS = 0x01,
74 DRAW_VERTICES_HAS_COLORS = 0x02,
reed@google.com85e143c2013-12-30 15:51:25 +000075 DRAW_VERTICES_HAS_INDICES = 0x04,
76 DRAW_VERTICES_HAS_XFER = 0x08,
reed@android.com8a1c16f2008-12-17 15:59:43 +000077};
78
reed@google.com83ab4952011-11-11 21:34:54 +000079///////////////////////////////////////////////////////////////////////////////
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
djsollen@google.com21830d92012-08-07 19:49:41 +000098class SkTypefacePlayback {
reed@android.com8a1c16f2008-12-17 15:59:43 +000099public:
djsollen@google.com21830d92012-08-07 19:49:41 +0000100 SkTypefacePlayback();
101 virtual ~SkTypefacePlayback();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000102
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 int count() const { return fCount; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000104
mike@reedtribe.orge9e08cc2011-04-29 01:44:52 +0000105 void reset(const SkRefCntSet*);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106
107 void setCount(int count);
108 SkRefCnt* set(int index, SkRefCnt*);
109
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000110 void setupBuffer(SkReadBuffer& buffer) const {
djsollen@google.com21830d92012-08-07 19:49:41 +0000111 buffer.setTypefaceArray((SkTypeface**)fArray, fCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000113
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114protected:
115 int fCount;
116 SkRefCnt** fArray;
117};
118
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119class SkFactoryPlayback {
120public:
121 SkFactoryPlayback(int count) : fCount(count) {
122 fArray = SkNEW_ARRAY(SkFlattenable::Factory, count);
123 }
124
125 ~SkFactoryPlayback() {
126 SkDELETE_ARRAY(fArray);
127 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000128
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129 SkFlattenable::Factory* base() const { return fArray; }
130
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000131 void setupBuffer(SkReadBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132 buffer.setFactoryPlayback(fArray, fCount);
133 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000134
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135private:
136 int fCount;
137 SkFlattenable::Factory* fArray;
138};
139
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000140///////////////////////////////////////////////////////////////////////////////
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
commit-bot@chromium.orgfed2ab62014-01-23 15:16:05 +0000150// details of that operation are hidden in the provided traits
scroggo@google.com4dffc592012-07-17 16:49:40 +0000151// SkFlatDictionary: is an abstract templated dictionary that maintains a
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000152// searchable set of SkFlatData objects of type T.
scroggo@google.com4dffc592012-07-17 16:49:40 +0000153// SkFlatController: is an interface provided to SkFlatDictionary which handles
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000154// allocation (and unallocation in some cases). It also holds
scroggo@google.com15543602012-08-02 18:49:49 +0000155// ref count recorders and the like.
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000156//
commit-bot@chromium.orgfed2ab62014-01-23 15:16:05 +0000157// 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.
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000161//
162//
163///////////////////////////////////////////////////////////////////////////////
164
scroggo@google.com4dffc592012-07-17 16:49:40 +0000165class SkFlatData;
166
167class SkFlatController : public SkRefCnt {
168public:
robertphillips@google.coma22e2112012-08-16 14:58:06 +0000169 SK_DECLARE_INST_COUNT(SkFlatController)
170
commit-bot@chromium.orga2bd2d12014-01-30 22:16:32 +0000171 SkFlatController(uint32_t writeBufferFlags = 0);
scroggo@google.com15543602012-08-02 18:49:49 +0000172 virtual ~SkFlatController();
scroggo@google.com4dffc592012-07-17 16:49:40 +0000173 /**
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000174 * 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.
scroggo@google.com4dffc592012-07-17 16:49:40 +0000177 */
178 virtual void* allocThrow(size_t bytes) = 0;
179
180 /**
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000181 * 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.
scroggo@google.com4dffc592012-07-17 16:49:40 +0000183 */
184 virtual void unalloc(void* ptr) = 0;
scroggo@google.com0c3e5fe2012-08-01 19:34:20 +0000185
scroggo@google.com15543602012-08-02 18:49:49 +0000186 /**
djsollen@google.com21830d92012-08-07 19:49:41 +0000187 * 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.
scroggo@google.com15543602012-08-02 18:49:49 +0000191 */
djsollen@google.com21830d92012-08-07 19:49:41 +0000192 SkBitmapHeap* getBitmapHeap() { return fBitmapHeap; }
scroggo@google.com15543602012-08-02 18:49:49 +0000193
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
scroggo@google.com664fab12012-08-14 19:22:05 +0000214 /**
215 * Flags to use during creation of SkFlatData objects. Defaults to zero.
216 */
217 uint32_t getWriteBufferFlags() { return fWriteBufferFlags; }
218
scroggo@google.com15543602012-08-02 18:49:49 +0000219protected:
220 /**
djsollen@google.com21830d92012-08-07 19:49:41 +0000221 * Set an SkBitmapHeap to be used to store/read SkBitmaps. Ref counted.
scroggo@google.com15543602012-08-02 18:49:49 +0000222 */
djsollen@google.com21830d92012-08-07 19:49:41 +0000223 void setBitmapHeap(SkBitmapHeap*);
scroggo@google.com15543602012-08-02 18:49:49 +0000224
225 /**
226 * Set an SkRefCntSet to be used to store SkTypefaces during flattening. Ref
227 * counted.
228 */
229 void setTypefaceSet(SkRefCntSet*);
230
231 /**
scroggo@google.com15543602012-08-02 18:49:49 +0000232 * 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:
djsollen@google.com21830d92012-08-07 19:49:41 +0000246 SkBitmapHeap* fBitmapHeap;
scroggo@google.com15543602012-08-02 18:49:49 +0000247 SkRefCntSet* fTypefaceSet;
scroggo@google.com15543602012-08-02 18:49:49 +0000248 SkTypefacePlayback* fTypefacePlayback;
249 SkNamedFactorySet* fFactorySet;
commit-bot@chromium.orga2bd2d12014-01-30 22:16:32 +0000250 const uint32_t fWriteBufferFlags;
robertphillips@google.coma22e2112012-08-16 14:58:06 +0000251
252 typedef SkRefCnt INHERITED;
scroggo@google.com4dffc592012-07-17 16:49:40 +0000253};
254
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255class SkFlatData {
256public:
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000257 // Flatten obj into an SkFlatData with this index. controller owns the SkFlatData*.
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000258 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];
commit-bot@chromium.orga2bd2d12014-01-30 22:16:32 +0000262 SkWriteBuffer buffer(storage, sizeof(storage), controller->getWriteBufferFlags());
scroggo@google.com4dffc592012-07-17 16:49:40 +0000263
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000264 buffer.setBitmapHeap(controller->getBitmapHeap());
265 buffer.setTypefaceRecorder(controller->getTypefaceSet());
266 buffer.setNamedFactoryRecorder(controller->getNamedFactorySet());
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000267
268 Traits::flatten(buffer, obj);
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000269 size_t size = buffer.bytesWritten();
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000270 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.
reed@google.com7fa2a652014-01-27 13:42:58 +0000279 result->stampHeader(index, SkToS32(size));
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000280 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,
djsollen@google.com21830d92012-08-07 19:49:41 +0000286 SkBitmapHeap* bitmapHeap = NULL,
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000287 SkTypefacePlayback* facePlayback = NULL) const {
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000288 SkReadBuffer buffer(this->data(), fFlatSize);
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000289
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 }
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000300
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000301 // 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;
reed@google.come2589ae2012-07-10 19:38:01 +0000307 }
308
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000309 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.
bungeman@google.com148a3962013-01-17 20:19:13 +0000315 bool isTopBotWritten() const {
316 return !SkScalarIsNaN(fTopBot[0]);
reed@google.com45954262012-12-07 17:14:40 +0000317 }
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.
junov@chromium.orgf3b12232013-01-22 17:50:47 +0000321 SkScalar* writableTopBot() const {
bungeman@google.com148a3962013-01-17 20:19:13 +0000322 SkASSERT(!this->isTopBotWritten());
reed@google.com45954262012-12-07 17:14:40 +0000323 return fTopBot;
324 }
325
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000326 // Return the topbot[] after it has been recorded.
reed@google.com45954262012-12-07 17:14:40 +0000327 const SkScalar* topBot() const {
bungeman@google.com148a3962013-01-17 20:19:13 +0000328 SkASSERT(this->isTopBotWritten());
reed@google.com45954262012-12-07 17:14:40 +0000329 return fTopBot;
330 }
331
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000332private:
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000333 // 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
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 int fIndex;
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000351 int32_t fFlatSize;
junov@chromium.orgef760602012-06-27 20:03:16 +0000352 uint32_t fChecksum;
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000353 mutable SkScalar fTopBot[2]; // Cache of FontMetrics fTop, fBottom. Starts as [NaN,?].
354 // uint32_t flattenedData[] implicitly hangs off the end.
reed@google.comb4ed0172012-07-10 13:29:52 +0000355
commit-bot@chromium.org19382422014-01-14 20:51:26 +0000356 template <typename T, typename Traits> friend class SkFlatDictionary;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357};
358
commit-bot@chromium.org19382422014-01-14 20:51:26 +0000359template <typename T, typename Traits>
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000360class SkFlatDictionary {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361public:
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000362 explicit SkFlatDictionary(SkFlatController* controller)
363 : fController(SkRef(controller))
commit-bot@chromium.orga2bd2d12014-01-30 22:16:32 +0000364 , fScratch(controller->getWriteBufferFlags())
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000365 , fReady(false) {
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000366 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();
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000375 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376
skia.committer@gmail.com0f12e1f2013-03-11 07:01:23 +0000377 int count() const {
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000378 SkASSERT(fHash.count() == fIndexedData.count());
commit-bot@chromium.orgf85251c2014-01-10 19:37:00 +0000379 return fHash.count();
robertphillips@google.com7d3451b2013-03-10 17:16:41 +0000380 }
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000381
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000382 // For testing only. Index is zero-based.
383 const SkFlatData* operator[](int index) {
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000384 return fIndexedData[index];
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000385 }
386
387 /**
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000388 * 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 *
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000392 */
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000393 int find(const T& element) {
394 return this->findAndReturnFlat(element)->index();
reed@google.com83ca3372012-07-12 15:27:54 +0000395 }
scroggo@google.com4dffc592012-07-17 16:49:40 +0000396
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000397 /**
scroggo@google.com4dffc592012-07-17 16:49:40 +0000398 * 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.
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000403 */
scroggo@google.com4dffc592012-07-17 16:49:40 +0000404 const SkFlatData* findAndReplace(const T& element,
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000405 const SkFlatData* toReplace,
406 bool* added,
scroggo@google.com4dffc592012-07-17 16:49:40 +0000407 bool* replaced) {
408 SkASSERT(added != NULL && replaced != NULL);
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000409
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;
reed@google.com83ca3372012-07-12 15:27:54 +0000418 }
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000419
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
commit-bot@chromium.orgf85251c2014-01-10 19:37:00 +0000427 // findAndReturnMutableFlat put flat at the back. Swap it into found->index() instead.
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000428 // indices in SkFlatData are 1-based, while fIndexedData is 0-based. Watch out!
commit-bot@chromium.orgf85251c2014-01-10 19:37:00 +0000429 SkASSERT(flat->index() == this->count());
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000430 flat->setIndex(found->index());
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000431 fIndexedData.removeShuffle(found->index()-1);
432 SkASSERT(flat == fIndexedData[found->index()-1]);
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000433
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;
scroggo@google.com4dffc592012-07-17 16:49:40 +0000440 return flat;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 }
442
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000443 /**
reed@google.comf4cc1872012-07-23 15:04:45 +0000444 * Unflatten the objects and return them in SkTRefArray, or return NULL
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000445 * if there no objects. Caller takes ownership of result.
reed@google.comf4cc1872012-07-23 15:04:45 +0000446 */
scroggo@google.com15543602012-08-02 18:49:49 +0000447 SkTRefArray<T>* unflattenToArray() const {
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000448 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++) {
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000454 this->unflatten(&array->writableAt(i), fIndexedData[i]);
reed@google.comf4cc1872012-07-23 15:04:45 +0000455 }
456 return array;
457 }
458
robertphillips@google.come37ad352013-03-01 19:44:30 +0000459 /**
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000460 * Unflatten the specific object at the given index.
461 * Caller takes ownership of the result.
robertphillips@google.come37ad352013-03-01 19:44:30 +0000462 */
463 T* unflatten(int index) const {
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000464 // index is 1-based, while fIndexedData is 0-based.
465 const SkFlatData* element = fIndexedData[index-1];
robertphillips@google.com7d3451b2013-03-10 17:16:41 +0000466 SkASSERT(index == element->index());
robertphillips@google.come37ad352013-03-01 19:44:30 +0000467
robertphillips@google.com7d3451b2013-03-10 17:16:41 +0000468 T* dst = new T;
469 this->unflatten(dst, element);
470 return dst;
robertphillips@google.come37ad352013-03-01 19:44:30 +0000471 }
472
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000473 /**
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 */
scroggo@google.com664fab12012-08-14 19:22:05 +0000477 const SkFlatData* findAndReturnFlat(const T& element) {
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000478 return this->findAndReturnMutableFlat(element);
scroggo@google.com4dffc592012-07-17 16:49:40 +0000479 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000480
junov@chromium.orgf3b12232013-01-22 17:50:47 +0000481private:
commit-bot@chromium.org19382422014-01-14 20:51:26 +0000482 // 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.
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000484 void lazyInit() {
485 if (fReady) {
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000486 return;
487 }
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000488
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000489 // Without a bitmap heap, we'll flatten bitmaps into paints. That's never what you want.
490 SkASSERT(fController->getBitmapHeap() != NULL);
commit-bot@chromium.org19382422014-01-14 20:51:26 +0000491 fScratch.setBitmapHeap(fController->getBitmapHeap());
492 fScratch.setTypefaceRecorder(fController->getTypefaceSet());
493 fScratch.setNamedFactoryRecorder(fController->getNamedFactorySet());
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000494 fReady = true;
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000495 }
496
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000497 // 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().
commit-bot@chromium.orgf85251c2014-01-10 19:37:00 +0000500 const SkFlatData& scratch = this->resetScratch(element, this->count()+1);
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000501
502 SkFlatData* candidate = fHash.find(scratch);
503 if (candidate != NULL) return candidate;
504
505 SkFlatData* detached = this->detachScratch();
506 fHash.add(detached);
commit-bot@chromium.orgf85251c2014-01-10 19:37:00 +0000507 *fIndexedData.append() = detached;
508 SkASSERT(fIndexedData.top()->index() == this->count());
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000509 return detached;
510 }
511
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000512 // This reference is valid only until the next call to resetScratch() or detachScratch().
513 const SkFlatData& resetScratch(const T& element, int index) {
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000514 this->lazyInit();
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000515
commit-bot@chromium.org19382422014-01-14 20:51:26 +0000516 // 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);
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000521
commit-bot@chromium.org19382422014-01-14 20:51:26 +0000522 // 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;
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000527 }
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.
commit-bot@chromium.org19382422014-01-14 20:51:26 +0000534 SkFlatData* detached = (SkFlatData*)fController->allocThrow(fScratch.bytesWritten());
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000535
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000536 // Copy scratch into the new SkFlatData.
commit-bot@chromium.org19382422014-01-14 20:51:26 +0000537 SkFlatData* scratch = (SkFlatData*)fScratch.getWriter32()->contiguousArray();
538 SkASSERT(scratch != NULL);
539 memcpy(detached, scratch, fScratch.bytesWritten());
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000540
541 // We can now reuse fScratch, and detached will live until fController dies.
542 return detached;
543 }
544
robertphillips@google.come37ad352013-03-01 19:44:30 +0000545 void unflatten(T* dst, const SkFlatData* element) const {
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000546 element->unflatten<Traits>(dst,
547 fController->getBitmapHeap(),
548 fController->getTypefacePlayback());
robertphillips@google.come37ad352013-03-01 19:44:30 +0000549 }
550
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000551 // All SkFlatData* stored in fIndexedData and fHash are owned by the controller.
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000552 SkAutoTUnref<SkFlatController> fController;
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000553 SkWriteBuffer fScratch;
commit-bot@chromium.orgff007e82014-01-09 16:45:45 +0000554 bool fReady;
robertphillips@google.com7d3451b2013-03-10 17:16:41 +0000555
commit-bot@chromium.org6e834232014-01-10 20:13:09 +0000556 // For index -> SkFlatData. 0-based, while all indices in the API are 1-based. Careful!
robertphillips@google.com7d3451b2013-03-10 17:16:41 +0000557 SkTDArray<const SkFlatData*> fIndexedData;
reed@google.com83ca3372012-07-12 15:27:54 +0000558
mtklein@google.com9e3074e2013-08-20 16:48:47 +0000559 // For SkFlatData -> cached SkFlatData, which has index().
560 SkTDynamicHash<SkFlatData, SkFlatData,
561 SkFlatData::Identity, SkFlatData::Hash, SkFlatData::Equal> fHash;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562};
563
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000564struct SkPaintTraits {
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000565 static void flatten(SkWriteBuffer& buffer, const SkPaint& paint) {
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000566 paint.flatten(buffer);
567 }
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000568 static void unflatten(SkReadBuffer& buffer, SkPaint* paint) {
commit-bot@chromium.org07adb632014-01-02 22:20:49 +0000569 paint->unflatten(buffer);
570 }
571};
commit-bot@chromium.org19382422014-01-14 20:51:26 +0000572typedef SkFlatDictionary<SkPaint, SkPaintTraits> SkPaintDictionary;
djsollen@google.comd2700ee2012-05-30 16:54:13 +0000573
scroggo@google.com4dffc592012-07-17 16:49:40 +0000574class SkChunkFlatController : public SkFlatController {
575public:
576 SkChunkFlatController(size_t minSize)
scroggo@google.com15543602012-08-02 18:49:49 +0000577 : fHeap(minSize)
commit-bot@chromium.org46724e42013-07-30 21:54:10 +0000578 , fTypefaceSet(SkNEW(SkRefCntSet))
579 , fLastAllocated(NULL) {
scroggo@google.com15543602012-08-02 18:49:49 +0000580 this->setTypefaceSet(fTypefaceSet);
scroggo@google.com15543602012-08-02 18:49:49 +0000581 this->setTypefacePlayback(&fTypefacePlayback);
582 }
scroggo@google.com4dffc592012-07-17 16:49:40 +0000583
scroggo@google.com15543602012-08-02 18:49:49 +0000584 virtual void* allocThrow(size_t bytes) SK_OVERRIDE {
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000585 fLastAllocated = fHeap.allocThrow(bytes);
586 return fLastAllocated;
scroggo@google.com4dffc592012-07-17 16:49:40 +0000587 }
588
scroggo@google.com15543602012-08-02 18:49:49 +0000589 virtual void unalloc(void* ptr) SK_OVERRIDE {
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000590 // fHeap can only free a pointer if it was the last one allocated. Otherwise, we'll just
591 // have to wait until fHeap is destroyed.
592 if (ptr == fLastAllocated) (void)fHeap.unalloc(ptr);
scroggo@google.com4dffc592012-07-17 16:49:40 +0000593 }
scroggo@google.com15543602012-08-02 18:49:49 +0000594
djsollen@google.com21830d92012-08-07 19:49:41 +0000595 void setupPlaybacks() const {
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000596 fTypefacePlayback.reset(fTypefaceSet.get());
scroggo@google.com15543602012-08-02 18:49:49 +0000597 }
598
djsollen@google.com21830d92012-08-07 19:49:41 +0000599 void setBitmapStorage(SkBitmapHeap* heap) {
600 this->setBitmapHeap(heap);
scroggo@google.com15543602012-08-02 18:49:49 +0000601 }
602
scroggo@google.com4dffc592012-07-17 16:49:40 +0000603private:
scroggo@google.com15543602012-08-02 18:49:49 +0000604 SkChunkAlloc fHeap;
commit-bot@chromium.orgff36a1d2013-07-24 20:37:30 +0000605 SkAutoTUnref<SkRefCntSet> fTypefaceSet;
606 void* fLastAllocated;
scroggo@google.com15543602012-08-02 18:49:49 +0000607 mutable SkTypefacePlayback fTypefacePlayback;
scroggo@google.com4dffc592012-07-17 16:49:40 +0000608};
609
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610#endif