blob: f4cee236f89ca75f5fea07ec16e3a150e00d6704 [file] [log] [blame]
robertphillips@google.comca0c8382013-09-26 12:18:23 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkBuffer.h"
9#include "SkPath.h"
10#include "SkPathRef.h"
11
12SK_DEFINE_INST_COUNT(SkPathRef);
13
robertphillips@google.com3e292aa2013-09-27 17:48:49 +000014//////////////////////////////////////////////////////////////////////////////
15SkPathRef::Editor::Editor(SkAutoTUnref<SkPathRef>* pathRef,
16 int incReserveVerbs,
17 int incReservePoints)
18{
19 if ((*pathRef)->unique()) {
20 (*pathRef)->incReserve(incReserveVerbs, incReservePoints);
21 } else {
22 SkPathRef* copy = SkNEW(SkPathRef);
23 copy->copy(**pathRef, incReserveVerbs, incReservePoints);
24 pathRef->reset(copy);
25 }
26 fPathRef = *pathRef;
27 fPathRef->fGenerationID = 0;
28 SkDEBUGCODE(sk_atomic_inc(&fPathRef->fEditorsAttached);)
robertphillips@google.comca0c8382013-09-26 12:18:23 +000029}
30
31SkPoint* SkPathRef::Editor::growForConic(SkScalar w) {
32 fPathRef->validate();
33 SkPoint* pts = fPathRef->growForVerb(SkPath::kConic_Verb);
34 *fPathRef->fConicWeights.append() = w;
35 return pts;
36}
37
robertphillips@google.com3e292aa2013-09-27 17:48:49 +000038//////////////////////////////////////////////////////////////////////////////
39void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst,
40 const SkPathRef& src,
41 const SkMatrix& matrix) {
42 src.validate();
43 if (matrix.isIdentity()) {
44 if (*dst != &src) {
45 src.ref();
46 dst->reset(const_cast<SkPathRef*>(&src));
47 (*dst)->validate();
48 }
49 return;
50 }
51
52 bool dstUnique = (*dst)->unique();
53 if (!dstUnique) {
54 dst->reset(SkNEW(SkPathRef));
55 (*dst)->resetToSize(src.fVerbCnt, src.fPointCnt, src.fConicWeights.count());
56 memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(), src.fVerbCnt * sizeof(uint8_t));
57 (*dst)->fConicWeights = src.fConicWeights;
58 }
59
60 // Need to check this here in case (&src == dst)
61 bool canXformBounds = !src.fBoundsIsDirty && matrix.rectStaysRect() && src.countPoints() > 1;
62
63 matrix.mapPoints((*dst)->fPoints, src.points(), src.fPointCnt);
64
65 /*
66 * Here we optimize the bounds computation, by noting if the bounds are
67 * already known, and if so, we just transform those as well and mark
68 * them as "known", rather than force the transformed path to have to
69 * recompute them.
70 *
71 * Special gotchas if the path is effectively empty (<= 1 point) or
72 * if it is non-finite. In those cases bounds need to stay empty,
73 * regardless of the matrix.
74 */
75 if (canXformBounds) {
76 (*dst)->fBoundsIsDirty = false;
77 if (src.fIsFinite) {
78 matrix.mapRect(&(*dst)->fBounds, src.fBounds);
79 if (!((*dst)->fIsFinite = (*dst)->fBounds.isFinite())) {
80 (*dst)->fBounds.setEmpty();
81 }
82 } else {
83 (*dst)->fIsFinite = false;
84 (*dst)->fBounds.setEmpty();
85 }
86 } else {
87 (*dst)->fBoundsIsDirty = true;
88 }
89
90 (*dst)->validate();
91}
92
93SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer
94#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO
95 , bool newFormat, int32_t oldPacked
96#endif
97 ) {
98 SkPathRef* ref = SkNEW(SkPathRef);
99#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO
100 if (newFormat) {
101#endif
102 int32_t packed = buffer->readU32();
103
104 ref->fIsFinite = (packed >> kIsFinite_SerializationShift) & 1;
105#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO
106 } else {
107 ref->fIsFinite = (oldPacked >> SkPath::kOldIsFinite_SerializationShift) & 1;
108 }
109#endif
110
111 ref->fGenerationID = buffer->readU32();
112 int32_t verbCount = buffer->readS32();
113 int32_t pointCount = buffer->readS32();
114 int32_t conicCount = buffer->readS32();
115 ref->resetToSize(verbCount, pointCount, conicCount);
116
117 SkASSERT(verbCount == ref->countVerbs());
118 SkASSERT(pointCount == ref->countPoints());
119 SkASSERT(conicCount == ref->fConicWeights.count());
120 buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t));
121 buffer->read(ref->fPoints, pointCount * sizeof(SkPoint));
122 buffer->read(ref->fConicWeights.begin(), conicCount * sizeof(SkScalar));
123 buffer->read(&ref->fBounds, sizeof(SkRect));
124 ref->fBoundsIsDirty = false;
125 return ref;
126}
127
128void SkPathRef::Rewind(SkAutoTUnref<SkPathRef>* pathRef) {
129 if ((*pathRef)->unique()) {
130 (*pathRef)->validate();
131 (*pathRef)->fBoundsIsDirty = true; // this also invalidates fIsFinite
132 (*pathRef)->fVerbCnt = 0;
133 (*pathRef)->fPointCnt = 0;
134 (*pathRef)->fFreeSpace = (*pathRef)->currSize();
135 (*pathRef)->fGenerationID = 0;
136 (*pathRef)->fConicWeights.rewind();
137 (*pathRef)->validate();
138 } else {
139 int oldVCnt = (*pathRef)->countVerbs();
140 int oldPCnt = (*pathRef)->countPoints();
141 pathRef->reset(SkNEW(SkPathRef));
142 (*pathRef)->resetToSize(0, 0, 0, oldVCnt, oldPCnt);
143 }
144}
145
146bool SkPathRef::operator== (const SkPathRef& ref) const {
147 this->validate();
148 ref.validate();
149 bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID;
150#ifdef SK_RELEASE
151 if (genIDMatch) {
152 return true;
153 }
154#endif
155 if (fPointCnt != ref.fPointCnt ||
156 fVerbCnt != ref.fVerbCnt) {
157 SkASSERT(!genIDMatch);
158 return false;
159 }
160 if (0 != memcmp(this->verbsMemBegin(),
161 ref.verbsMemBegin(),
162 ref.fVerbCnt * sizeof(uint8_t))) {
163 SkASSERT(!genIDMatch);
164 return false;
165 }
166 if (0 != memcmp(this->points(),
167 ref.points(),
168 ref.fPointCnt * sizeof(SkPoint))) {
169 SkASSERT(!genIDMatch);
170 return false;
171 }
172 if (fConicWeights != ref.fConicWeights) {
173 SkASSERT(!genIDMatch);
174 return false;
175 }
176 // We've done the work to determine that these are equal. If either has a zero genID, copy
177 // the other's. If both are 0 then genID() will compute the next ID.
178 if (0 == fGenerationID) {
179 fGenerationID = ref.genID();
180 } else if (0 == ref.fGenerationID) {
181 ref.fGenerationID = this->genID();
182 }
183 return true;
184}
185
186void SkPathRef::writeToBuffer(SkWBuffer* buffer) {
187 this->validate();
188 SkDEBUGCODE(size_t beforePos = buffer->pos();)
189
190 // Call getBounds() to ensure (as a side-effect) that fBounds
191 // and fIsFinite are computed.
192 const SkRect& bounds = this->getBounds();
193
194 int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift);
195 buffer->write32(packed);
196
197 // TODO: write gen ID here. Problem: We don't know if we're cross process or not from
198 // SkWBuffer. Until this is fixed we write 0.
199 buffer->write32(0);
200 buffer->write32(fVerbCnt);
201 buffer->write32(fPointCnt);
202 buffer->write32(fConicWeights.count());
203 buffer->write(verbsMemBegin(), fVerbCnt * sizeof(uint8_t));
204 buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
205 buffer->write(fConicWeights.begin(), fConicWeights.bytes());
206 buffer->write(&bounds, sizeof(bounds));
207
208 SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize());
209}
210
211uint32_t SkPathRef::writeSize() {
212 return uint32_t(5 * sizeof(uint32_t) +
213 fVerbCnt * sizeof(uint8_t) +
214 fPointCnt * sizeof(SkPoint) +
215 fConicWeights.bytes() +
216 sizeof(SkRect));
217}
218
219void SkPathRef::copy(const SkPathRef& ref,
220 int additionalReserveVerbs,
221 int additionalReservePoints) {
222 this->validate();
223 this->resetToSize(ref.fVerbCnt, ref.fPointCnt, ref.fConicWeights.count(),
224 additionalReserveVerbs, additionalReservePoints);
225 memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt * sizeof(uint8_t));
226 memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint));
227 fConicWeights = ref.fConicWeights;
228 // We could call genID() here to force a real ID (instead of 0). However, if we're making
229 // a copy then presumably we intend to make a modification immediately afterwards.
230 fGenerationID = ref.fGenerationID;
231 fBoundsIsDirty = ref.fBoundsIsDirty;
232 if (!fBoundsIsDirty) {
233 fBounds = ref.fBounds;
234 fIsFinite = ref.fIsFinite;
235 }
236 this->validate();
237}
238
239void SkPathRef::resetToSize(int verbCount, int pointCount, int conicCount,
240 int reserveVerbs, int reservePoints) {
241 this->validate();
242 fBoundsIsDirty = true; // this also invalidates fIsFinite
243 fGenerationID = 0;
244
245 size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount;
246 size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reservePoints;
247 size_t minSize = newSize + newReserve;
248
249 ptrdiff_t sizeDelta = this->currSize() - minSize;
250
251 if (sizeDelta < 0 || static_cast<size_t>(sizeDelta) >= 3 * minSize) {
252 sk_free(fPoints);
253 fPoints = NULL;
254 fVerbs = NULL;
255 fFreeSpace = 0;
256 fVerbCnt = 0;
257 fPointCnt = 0;
258 this->makeSpace(minSize);
259 fVerbCnt = verbCount;
260 fPointCnt = pointCount;
261 fFreeSpace -= newSize;
262 } else {
263 fPointCnt = pointCount;
264 fVerbCnt = verbCount;
265 fFreeSpace = this->currSize() - minSize;
266 }
267 fConicWeights.setCount(conicCount);
268 this->validate();
269}
270
robertphillips@google.comca0c8382013-09-26 12:18:23 +0000271SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) {
272 this->validate();
273 int pCnt;
274 switch (verb) {
275 case SkPath::kMove_Verb:
276 pCnt = 1;
277 break;
278 case SkPath::kLine_Verb:
279 pCnt = 1;
280 break;
281 case SkPath::kQuad_Verb:
282 // fall through
283 case SkPath::kConic_Verb:
284 pCnt = 2;
285 break;
286 case SkPath::kCubic_Verb:
287 pCnt = 3;
288 break;
289 case SkPath::kClose_Verb:
290 pCnt = 0;
291 break;
292 case SkPath::kDone_Verb:
293 SkDEBUGFAIL("growForVerb called for kDone");
294 // fall through
295 default:
296 SkDEBUGFAIL("default is not reached");
297 pCnt = 0;
298 }
299 size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint);
300 this->makeSpace(space);
301 this->fVerbs[~fVerbCnt] = verb;
302 SkPoint* ret = fPoints + fPointCnt;
303 fVerbCnt += 1;
304 fPointCnt += pCnt;
305 fFreeSpace -= space;
306 fBoundsIsDirty = true; // this also invalidates fIsFinite
307 this->validate();
308 return ret;
309}
310
robertphillips@google.com3e292aa2013-09-27 17:48:49 +0000311void SkPathRef::makeSpace(size_t size) {
312 this->validate();
313 ptrdiff_t growSize = size - fFreeSpace;
314 if (growSize <= 0) {
315 return;
robertphillips@google.comca0c8382013-09-26 12:18:23 +0000316 }
robertphillips@google.com3e292aa2013-09-27 17:48:49 +0000317 size_t oldSize = this->currSize();
318 // round to next multiple of 8 bytes
319 growSize = (growSize + 7) & ~static_cast<size_t>(7);
320 // we always at least double the allocation
321 if (static_cast<size_t>(growSize) < oldSize) {
322 growSize = oldSize;
323 }
324 if (growSize < kMinSize) {
325 growSize = kMinSize;
326 }
327 size_t newSize = oldSize + growSize;
328 // Note that realloc could memcpy more than we need. It seems to be a win anyway. TODO:
329 // encapsulate this.
330 fPoints = reinterpret_cast<SkPoint*>(sk_realloc_throw(fPoints, newSize));
331 size_t oldVerbSize = fVerbCnt * sizeof(uint8_t);
332 void* newVerbsDst = reinterpret_cast<void*>(
333 reinterpret_cast<intptr_t>(fPoints) + newSize - oldVerbSize);
334 void* oldVerbsSrc = reinterpret_cast<void*>(
335 reinterpret_cast<intptr_t>(fPoints) + oldSize - oldVerbSize);
336 memmove(newVerbsDst, oldVerbsSrc, oldVerbSize);
337 fVerbs = reinterpret_cast<uint8_t*>(reinterpret_cast<intptr_t>(fPoints) + newSize);
338 fFreeSpace += growSize;
339 this->validate();
robertphillips@google.comca0c8382013-09-26 12:18:23 +0000340}
341
robertphillips@google.com3e292aa2013-09-27 17:48:49 +0000342int32_t SkPathRef::genID() const {
343 SkASSERT(!fEditorsAttached);
344 if (!fGenerationID) {
345 if (0 == fPointCnt && 0 == fVerbCnt) {
346 fGenerationID = kEmptyGenID;
347 } else {
348 static int32_t gPathRefGenerationID;
349 // do a loop in case our global wraps around, as we never want to return a 0 or the
350 // empty ID
351 do {
352 fGenerationID = sk_atomic_inc(&gPathRefGenerationID) + 1;
353 } while (fGenerationID <= kEmptyGenID);
354 }
355 }
356 return fGenerationID;
357}
robertphillips@google.comca0c8382013-09-26 12:18:23 +0000358
robertphillips@google.com3e292aa2013-09-27 17:48:49 +0000359void SkPathRef::validate() const {
360 SkASSERT(static_cast<ptrdiff_t>(fFreeSpace) >= 0);
361 SkASSERT(reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints) >= 0);
362 SkASSERT((NULL == fPoints) == (NULL == fVerbs));
363 SkASSERT(!(NULL == fPoints && 0 != fFreeSpace));
364 SkASSERT(!(NULL == fPoints && 0 != fFreeSpace));
365 SkASSERT(!(NULL == fPoints && fPointCnt));
366 SkASSERT(!(NULL == fVerbs && fVerbCnt));
367 SkASSERT(this->currSize() ==
368 fFreeSpace + sizeof(SkPoint) * fPointCnt + sizeof(uint8_t) * fVerbCnt);
robertphillips@google.comca0c8382013-09-26 12:18:23 +0000369
robertphillips@google.com3e292aa2013-09-27 17:48:49 +0000370#ifdef SK_DEBUG
371 if (!fBoundsIsDirty && !fBounds.isEmpty()) {
372 bool isFinite = true;
373 for (int i = 0; i < fPointCnt; ++i) {
374 SkASSERT(fPoints[i].fX >= fBounds.fLeft && fPoints[i].fX <= fBounds.fRight &&
375 fPoints[i].fY >= fBounds.fTop && fPoints[i].fY <= fBounds.fBottom);
376 if (!fPoints[i].isFinite()) {
377 isFinite = false;
378 }
379 }
380 SkASSERT(SkToBool(fIsFinite) == isFinite);
381 }
382#endif
robertphillips@google.comca0c8382013-09-26 12:18:23 +0000383}