1 /*
2 * Copyright 2017 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 "SkVertices.h"
9
10 #include "SkData.h"
11 #include "SkReader32.h"
12 #include "SkSafeMath.h"
13 #include "SkSafeRange.h"
14 #include "SkTo.h"
15 #include "SkWriter32.h"
16 #include <atomic>
17 #include <new>
18
next_id()19 static int32_t next_id() {
20 static std::atomic<int32_t> nextID{1};
21
22 int32_t id;
23 do {
24 id = nextID++;
25 } while (id == SK_InvalidGenID);
26 return id;
27 }
28
29 struct SkVertices::Sizes {
SizesSkVertices::Sizes30 Sizes(SkVertices::VertexMode mode, int vertexCount, int indexCount, bool hasTexs,
31 bool hasColors, bool hasBones) {
32 SkSafeMath safe;
33
34 fVSize = safe.mul(vertexCount, sizeof(SkPoint));
35 fTSize = hasTexs ? safe.mul(vertexCount, sizeof(SkPoint)) : 0;
36 fCSize = hasColors ? safe.mul(vertexCount, sizeof(SkColor)) : 0;
37 fBISize = hasBones ? safe.mul(vertexCount, sizeof(BoneIndices)) : 0;
38 fBWSize = hasBones ? safe.mul(vertexCount, sizeof(BoneWeights)) : 0;
39
40 fBuilderTriFanISize = 0;
41 fISize = safe.mul(indexCount, sizeof(uint16_t));
42 if (kTriangleFan_VertexMode == mode) {
43 int numFanTris = 0;
44 if (indexCount) {
45 fBuilderTriFanISize = fISize;
46 numFanTris = indexCount - 2;
47 } else {
48 numFanTris = vertexCount - 2;
49 // By forcing this to become indexed we are adding a constraint to the maximum
50 // number of vertices.
51 if (vertexCount > (SkTo<int>(UINT16_MAX) + 1)) {
52 sk_bzero(this, sizeof(*this));
53 return;
54 }
55 }
56 if (numFanTris <= 0) {
57 sk_bzero(this, sizeof(*this));
58 return;
59 }
60 fISize = safe.mul(numFanTris, 3 * sizeof(uint16_t));
61 }
62
63 fTotal = safe.add(sizeof(SkVertices),
64 safe.add(fVSize,
65 safe.add(fTSize,
66 safe.add(fCSize,
67 safe.add(fBISize,
68 safe.add(fBWSize,
69 fISize))))));
70
71 if (safe.ok()) {
72 fArrays = fTotal - sizeof(SkVertices); // just the sum of the arrays
73 } else {
74 sk_bzero(this, sizeof(*this));
75 }
76 }
77
isValidSkVertices::Sizes78 bool isValid() const { return fTotal != 0; }
79
80 size_t fTotal; // size of entire SkVertices allocation (obj + arrays)
81 size_t fArrays; // size of all the arrays (V + T + C + BI + BW + I)
82 size_t fVSize;
83 size_t fTSize;
84 size_t fCSize;
85 size_t fBISize;
86 size_t fBWSize;
87 size_t fISize;
88
89 // For indexed tri-fans this is the number of amount of space fo indices needed in the builder
90 // before conversion to indexed triangles (or zero if not indexed or not a triangle fan).
91 size_t fBuilderTriFanISize;
92 };
93
Builder(VertexMode mode,int vertexCount,int indexCount,uint32_t builderFlags)94 SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount,
95 uint32_t builderFlags) {
96 bool hasTexs = SkToBool(builderFlags & SkVertices::kHasTexCoords_BuilderFlag);
97 bool hasColors = SkToBool(builderFlags & SkVertices::kHasColors_BuilderFlag);
98 bool hasBones = SkToBool(builderFlags & SkVertices::kHasBones_BuilderFlag);
99 bool isVolatile = !SkToBool(builderFlags & SkVertices::kIsNonVolatile_BuilderFlag);
100 this->init(mode, vertexCount, indexCount, isVolatile,
101 SkVertices::Sizes(mode, vertexCount, indexCount, hasTexs, hasColors, hasBones));
102 }
103
Builder(VertexMode mode,int vertexCount,int indexCount,bool isVolatile,const SkVertices::Sizes & sizes)104 SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount, bool isVolatile,
105 const SkVertices::Sizes& sizes) {
106 this->init(mode, vertexCount, indexCount, isVolatile, sizes);
107 }
108
init(VertexMode mode,int vertexCount,int indexCount,bool isVolatile,const SkVertices::Sizes & sizes)109 void SkVertices::Builder::init(VertexMode mode, int vertexCount, int indexCount, bool isVolatile,
110 const SkVertices::Sizes& sizes) {
111 if (!sizes.isValid()) {
112 return; // fVertices will already be null
113 }
114
115 void* storage = ::operator new (sizes.fTotal);
116 if (sizes.fBuilderTriFanISize) {
117 fIntermediateFanIndices.reset(new uint8_t[sizes.fBuilderTriFanISize]);
118 }
119
120 fVertices.reset(new (storage) SkVertices);
121
122 // need to point past the object to store the arrays
123 char* ptr = (char*)storage + sizeof(SkVertices);
124
125 fVertices->fPositions = (SkPoint*)ptr; ptr += sizes.fVSize;
126 fVertices->fTexs = sizes.fTSize ? (SkPoint*)ptr : nullptr; ptr += sizes.fTSize;
127 fVertices->fColors = sizes.fCSize ? (SkColor*)ptr : nullptr; ptr += sizes.fCSize;
128 fVertices->fBoneIndices = sizes.fBISize ? (BoneIndices*) ptr : nullptr; ptr += sizes.fBISize;
129 fVertices->fBoneWeights = sizes.fBWSize ? (BoneWeights*) ptr : nullptr; ptr += sizes.fBWSize;
130 fVertices->fIndices = sizes.fISize ? (uint16_t*)ptr : nullptr;
131 fVertices->fVertexCnt = vertexCount;
132 fVertices->fIndexCnt = indexCount;
133 fVertices->fIsVolatile = isVolatile;
134 fVertices->fMode = mode;
135
136 // We defer assigning fBounds and fUniqueID until detach() is called
137 }
138
detach()139 sk_sp<SkVertices> SkVertices::Builder::detach() {
140 if (fVertices) {
141 fVertices->fBounds.set(fVertices->fPositions, fVertices->fVertexCnt);
142 if (fVertices->fMode == kTriangleFan_VertexMode) {
143 if (fIntermediateFanIndices.get()) {
144 SkASSERT(fVertices->fIndexCnt);
145 auto tempIndices = this->indices();
146 for (int t = 0; t < fVertices->fIndexCnt - 2; ++t) {
147 fVertices->fIndices[3 * t + 0] = tempIndices[0];
148 fVertices->fIndices[3 * t + 1] = tempIndices[t + 1];
149 fVertices->fIndices[3 * t + 2] = tempIndices[t + 2];
150 }
151 fVertices->fIndexCnt = 3 * (fVertices->fIndexCnt - 2);
152 } else {
153 SkASSERT(!fVertices->fIndexCnt);
154 for (int t = 0; t < fVertices->fVertexCnt - 2; ++t) {
155 fVertices->fIndices[3 * t + 0] = 0;
156 fVertices->fIndices[3 * t + 1] = SkToU16(t + 1);
157 fVertices->fIndices[3 * t + 2] = SkToU16(t + 2);
158 }
159 fVertices->fIndexCnt = 3 * (fVertices->fVertexCnt - 2);
160 }
161 fVertices->fMode = kTriangles_VertexMode;
162 }
163 fVertices->fUniqueID = next_id();
164 return std::move(fVertices); // this will null fVertices after the return
165 }
166 return nullptr;
167 }
168
vertexCount() const169 int SkVertices::Builder::vertexCount() const {
170 return fVertices ? fVertices->vertexCount() : 0;
171 }
172
indexCount() const173 int SkVertices::Builder::indexCount() const {
174 return fVertices ? fVertices->indexCount() : 0;
175 }
176
isVolatile() const177 bool SkVertices::Builder::isVolatile() const {
178 return fVertices ? fVertices->isVolatile() : true;
179 }
180
positions()181 SkPoint* SkVertices::Builder::positions() {
182 return fVertices ? const_cast<SkPoint*>(fVertices->positions()) : nullptr;
183 }
184
texCoords()185 SkPoint* SkVertices::Builder::texCoords() {
186 return fVertices ? const_cast<SkPoint*>(fVertices->texCoords()) : nullptr;
187 }
188
colors()189 SkColor* SkVertices::Builder::colors() {
190 return fVertices ? const_cast<SkColor*>(fVertices->colors()) : nullptr;
191 }
192
boneIndices()193 SkVertices::BoneIndices* SkVertices::Builder::boneIndices() {
194 return fVertices ? const_cast<BoneIndices*>(fVertices->boneIndices()) : nullptr;
195 }
196
boneWeights()197 SkVertices::BoneWeights* SkVertices::Builder::boneWeights() {
198 return fVertices ? const_cast<BoneWeights*>(fVertices->boneWeights()) : nullptr;
199 }
200
indices()201 uint16_t* SkVertices::Builder::indices() {
202 if (!fVertices) {
203 return nullptr;
204 }
205 if (fIntermediateFanIndices) {
206 return reinterpret_cast<uint16_t*>(fIntermediateFanIndices.get());
207 }
208 return const_cast<uint16_t*>(fVertices->indices());
209 }
210
211 /** Makes a copy of the SkVertices and applies a set of bones, then returns the deformed
212 vertices.
213
214 @param bones The bones to apply.
215 @param boneCount The number of bones.
216 @return The transformed SkVertices.
217 */
applyBones(const SkVertices::Bone bones[],int boneCount) const218 sk_sp<SkVertices> SkVertices::applyBones(const SkVertices::Bone bones[], int boneCount) const {
219 // If there aren't any bones, then nothing changes.
220 // We don't check if the SkVertices object has bone indices/weights because there is the case
221 // where the object can have no indices/weights but still have a world transform applied.
222 if (!bones || !boneCount) {
223 return sk_ref_sp(this);
224 }
225 SkASSERT(boneCount >= 1);
226
227 // Copy the SkVertices.
228 sk_sp<SkVertices> copy = SkVertices::MakeCopy(this->mode(),
229 this->vertexCount(),
230 this->positions(),
231 this->texCoords(),
232 this->colors(),
233 nullptr,
234 nullptr,
235 this->indexCount(),
236 this->indices());
237
238 // Transform the positions.
239 for (int i = 0; i < this->vertexCount(); i++) {
240 SkPoint& position = copy->fPositions[i];
241
242 // Apply the world transform.
243 position = bones[0].mapPoint(position);
244
245 // Apply the bone deformations.
246 if (boneCount > 1) {
247 SkASSERT(this->boneIndices());
248 SkASSERT(this->boneWeights());
249
250 SkPoint result = SkPoint::Make(0.0f, 0.0f);
251 const SkVertices::BoneIndices& indices = this->boneIndices()[i];
252 const SkVertices::BoneWeights& weights = this->boneWeights()[i];
253 for (int j = 0; j < 4; j++) {
254 int index = indices[j];
255 float weight = weights[j];
256 if (index == 0 || weight == 0.0f) {
257 continue;
258 }
259 SkASSERT(index < boneCount);
260
261 // result += M * v * w.
262 result += bones[index].mapPoint(position) * weight;
263 }
264 position = result;
265 }
266 }
267
268 // Recalculate the bounds.
269 copy->fBounds.set(copy->fPositions, copy->fVertexCnt);
270
271 return copy;
272 }
273
274 ///////////////////////////////////////////////////////////////////////////////////////////////////
275
MakeCopy(VertexMode mode,int vertexCount,const SkPoint pos[],const SkPoint texs[],const SkColor colors[],const BoneIndices boneIndices[],const BoneWeights boneWeights[],int indexCount,const uint16_t indices[],bool isVolatile)276 sk_sp<SkVertices> SkVertices::MakeCopy(VertexMode mode, int vertexCount,
277 const SkPoint pos[], const SkPoint texs[],
278 const SkColor colors[],
279 const BoneIndices boneIndices[],
280 const BoneWeights boneWeights[],
281 int indexCount, const uint16_t indices[],
282 bool isVolatile) {
283 SkASSERT((!boneIndices && !boneWeights) || (boneIndices && boneWeights));
284 Sizes sizes(mode,
285 vertexCount,
286 indexCount,
287 texs != nullptr,
288 colors != nullptr,
289 boneIndices != nullptr);
290 if (!sizes.isValid()) {
291 return nullptr;
292 }
293
294 Builder builder(mode, vertexCount, indexCount, isVolatile, sizes);
295 SkASSERT(builder.isValid());
296
297 sk_careful_memcpy(builder.positions(), pos, sizes.fVSize);
298 sk_careful_memcpy(builder.texCoords(), texs, sizes.fTSize);
299 sk_careful_memcpy(builder.colors(), colors, sizes.fCSize);
300 sk_careful_memcpy(builder.boneIndices(), boneIndices, sizes.fBISize);
301 sk_careful_memcpy(builder.boneWeights(), boneWeights, sizes.fBWSize);
302 size_t isize = (mode == kTriangleFan_VertexMode) ? sizes.fBuilderTriFanISize : sizes.fISize;
303 sk_careful_memcpy(builder.indices(), indices, isize);
304
305 return builder.detach();
306 }
307
approximateSize() const308 size_t SkVertices::approximateSize() const {
309 Sizes sizes(fMode,
310 fVertexCnt,
311 fIndexCnt,
312 this->hasTexCoords(),
313 this->hasColors(),
314 this->hasBones());
315 SkASSERT(sizes.isValid());
316 return sizeof(SkVertices) + sizes.fArrays;
317 }
318
319 ///////////////////////////////////////////////////////////////////////////////////////////////////
320
321 // storage = packed | vertex_count | index_count | pos[] | texs[] | colors[] | boneIndices[] |
322 // boneWeights[] | indices[]
323 // = header + arrays
324
325 #define kMode_Mask 0x0FF
326 #define kHasTexs_Mask 0x100
327 #define kHasColors_Mask 0x200
328 #define kHasBones_Mask 0x400
329 #define kIsNonVolatile_Mask 0x800
330 #define kHeaderSize (3 * sizeof(uint32_t))
331
encode() const332 sk_sp<SkData> SkVertices::encode() const {
333 // packed has room for addtional flags in the future (e.g. versioning)
334 uint32_t packed = static_cast<uint32_t>(fMode);
335 SkASSERT((packed & ~kMode_Mask) == 0); // our mode fits in the mask bits
336 if (this->hasTexCoords()) {
337 packed |= kHasTexs_Mask;
338 }
339 if (this->hasColors()) {
340 packed |= kHasColors_Mask;
341 }
342 if (this->hasBones()) {
343 packed |= kHasBones_Mask;
344 }
345 if (!this->isVolatile()) {
346 packed |= kIsNonVolatile_Mask;
347 }
348
349 Sizes sizes(fMode,
350 fVertexCnt,
351 fIndexCnt,
352 this->hasTexCoords(),
353 this->hasColors(),
354 this->hasBones());
355 SkASSERT(sizes.isValid());
356 SkASSERT(!sizes.fBuilderTriFanISize);
357 // need to force alignment to 4 for SkWriter32 -- will pad w/ 0s as needed
358 const size_t size = SkAlign4(kHeaderSize + sizes.fArrays);
359
360 sk_sp<SkData> data = SkData::MakeUninitialized(size);
361 SkWriter32 writer(data->writable_data(), data->size());
362
363 writer.write32(packed);
364 writer.write32(fVertexCnt);
365 writer.write32(fIndexCnt);
366 writer.write(fPositions, sizes.fVSize);
367 writer.write(fTexs, sizes.fTSize);
368 writer.write(fColors, sizes.fCSize);
369 writer.write(fBoneIndices, sizes.fBISize);
370 writer.write(fBoneWeights, sizes.fBWSize);
371 // if index-count is odd, we won't be 4-bytes aligned, so we call the pad version
372 writer.writePad(fIndices, sizes.fISize);
373
374 return data;
375 }
376
Decode(const void * data,size_t length)377 sk_sp<SkVertices> SkVertices::Decode(const void* data, size_t length) {
378 if (length < kHeaderSize) {
379 return nullptr;
380 }
381
382 SkReader32 reader(data, length);
383 SkSafeRange safe;
384
385 const uint32_t packed = reader.readInt();
386 const int vertexCount = safe.checkGE(reader.readInt(), 0);
387 const int indexCount = safe.checkGE(reader.readInt(), 0);
388 const VertexMode mode = safe.checkLE<VertexMode>(packed & kMode_Mask,
389 SkVertices::kLast_VertexMode);
390 if (!safe) {
391 return nullptr;
392 }
393 const bool hasTexs = SkToBool(packed & kHasTexs_Mask);
394 const bool hasColors = SkToBool(packed & kHasColors_Mask);
395 const bool hasBones = SkToBool(packed & kHasBones_Mask);
396 const bool isVolatile = !SkToBool(packed & kIsNonVolatile_Mask);
397 Sizes sizes(mode, vertexCount, indexCount, hasTexs, hasColors, hasBones);
398 if (!sizes.isValid()) {
399 return nullptr;
400 }
401 // logically we can be only 2-byte aligned, but our buffer is always 4-byte aligned
402 if (SkAlign4(kHeaderSize + sizes.fArrays) != length) {
403 return nullptr;
404 }
405
406 Builder builder(mode, vertexCount, indexCount, isVolatile, sizes);
407
408 reader.read(builder.positions(), sizes.fVSize);
409 reader.read(builder.texCoords(), sizes.fTSize);
410 reader.read(builder.colors(), sizes.fCSize);
411 reader.read(builder.boneIndices(), sizes.fBISize);
412 reader.read(builder.boneWeights(), sizes.fBWSize);
413 size_t isize = (mode == kTriangleFan_VertexMode) ? sizes.fBuilderTriFanISize : sizes.fISize;
414 reader.read(builder.indices(), isize);
415 if (indexCount > 0) {
416 // validate that the indicies are in range
417 SkASSERT(indexCount == builder.indexCount());
418 const uint16_t* indices = builder.indices();
419 for (int i = 0; i < indexCount; ++i) {
420 if (indices[i] >= (unsigned)vertexCount) {
421 return nullptr;
422 }
423 }
424 }
425 return builder.detach();
426 }
427
operator delete(void * p)428 void SkVertices::operator delete(void* p)
429 {
430 ::operator delete(p);
431 }
432