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 #ifndef SkVertices_DEFINED
9 #define SkVertices_DEFINED
10 
11 #include "include/core/SkColor.h"
12 #include "include/core/SkData.h"
13 #include "include/core/SkPoint.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkRefCnt.h"
16 
17 /**
18  * An immutable set of vertex data that can be used with SkCanvas::drawVertices.
19  */
20 class SK_API SkVertices : public SkNVRefCnt<SkVertices> {
21 public:
22     // BoneIndices indicates which (of a maximum of 4 bones) a given vertex will interpolate
23     // between. To indicate that a slot is not used, the convention is to assign the bone index
24     // to 0.
25     struct BoneIndices {
26         uint32_t indices[4];
27 
28         uint32_t& operator[] (int i) {
29             SkASSERT(i >= 0);
30             SkASSERT(i < 4);
31             return indices[i];
32         }
33 
34         const uint32_t& operator[] (int i) const {
35             SkASSERT(i >= 0);
36             SkASSERT(i < 4);
37             return indices[i];
38         }
39     };
40 
41     // BoneWeights stores the interpolation weight for each of the (maximum of 4) bones a given
42     // vertex interpolates between. To indicate that a slot is not used, the weight for that
43     // slot should be 0.
44     struct BoneWeights {
45         float weights[4];
46 
47         float& operator[] (int i) {
48             SkASSERT(i >= 0);
49             SkASSERT(i < 4);
50             return weights[i];
51         }
52 
53         const float& operator[] (int i) const {
54             SkASSERT(i >= 0);
55             SkASSERT(i < 4);
56             return weights[i];
57         }
58     };
59 
60     // Bone stores a 3x2 transformation matrix in column major order:
61     // | scaleX   skewX transX |
62     // |  skewY  scaleY transY |
63     // SkRSXform is insufficient because bones can have non uniform scale.
64     struct Bone {
65         float values[6];
66 
67         float& operator[] (int i) {
68             SkASSERT(i >= 0);
69             SkASSERT(i < 6);
70             return values[i];
71         }
72 
73         const float& operator[] (int i) const {
74             SkASSERT(i >= 0);
75             SkASSERT(i < 6);
76             return values[i];
77         }
78 
mapPointBone79         SkPoint mapPoint(const SkPoint& point) const {
80             float x = values[0] * point.x() + values[2] * point.y() + values[4];
81             float y = values[1] * point.x() + values[3] * point.y() + values[5];
82             return SkPoint::Make(x, y);
83         }
84 
mapRectBone85         SkRect mapRect(const SkRect& rect) const {
86             SkRect dst = SkRect::MakeEmpty();
87             SkPoint quad[4];
88             rect.toQuad(quad);
89             for (int i = 0; i < 4; i ++) {
90                 quad[i] = mapPoint(quad[i]);
91             }
92             dst.setBoundsNoCheck(quad, 4);
93             return dst;
94         }
95     };
96 
97     enum VertexMode {
98         kTriangles_VertexMode,
99         kTriangleStrip_VertexMode,
100         kTriangleFan_VertexMode,
101 
102         kLast_VertexMode = kTriangleFan_VertexMode,
103     };
104 
105     /**
106      *  Create a vertices by copying the specified arrays. texs, colors, boneIndices, and
107      *  boneWeights may be nullptr, and indices is ignored if indexCount == 0.
108      *
109      *  boneIndices and boneWeights must either both be nullptr or both point to valid data.
110      *  If specified, they must both contain 'vertexCount' entries.
111      */
112     static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
113                                       const SkPoint positions[],
114                                       const SkPoint texs[],
115                                       const SkColor colors[],
116                                       const BoneIndices boneIndices[],
117                                       const BoneWeights boneWeights[],
118                                       int indexCount,
119                                       const uint16_t indices[],
120                                       bool isVolatile = true);
121 
122     static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
123                                       const SkPoint positions[],
124                                       const SkPoint texs[],
125                                       const SkColor colors[],
126                                       const BoneIndices boneIndices[],
127                                       const BoneWeights boneWeights[],
128                                       bool isVolatile = true) {
129         return MakeCopy(mode,
130                         vertexCount,
131                         positions,
132                         texs,
133                         colors,
134                         boneIndices,
135                         boneWeights,
136                         0,
137                         nullptr,
138                         isVolatile);
139     }
140 
141     static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
142                                       const SkPoint positions[],
143                                       const SkPoint texs[],
144                                       const SkColor colors[],
145                                       int indexCount,
146                                       const uint16_t indices[],
147                                       bool isVolatile = true) {
148         return MakeCopy(mode,
149                         vertexCount,
150                         positions,
151                         texs,
152                         colors,
153                         nullptr,
154                         nullptr,
155                         indexCount,
156                         indices,
157                         isVolatile);
158     }
159 
160     static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
161                                       const SkPoint positions[],
162                                       const SkPoint texs[],
163                                       const SkColor colors[],
164                                       bool isVolatile = true) {
165         return MakeCopy(mode, vertexCount, positions, texs, colors, nullptr, nullptr, isVolatile);
166     }
167 
168     struct Sizes;
169 
170     enum BuilderFlags {
171         kHasTexCoords_BuilderFlag   = 1 << 0,
172         kHasColors_BuilderFlag      = 1 << 1,
173         kHasBones_BuilderFlag       = 1 << 2,
174         kIsNonVolatile_BuilderFlag  = 1 << 3,
175     };
176     class Builder {
177     public:
178         Builder(VertexMode mode, int vertexCount, int indexCount, uint32_t flags);
179 
isValid()180         bool isValid() const { return fVertices != nullptr; }
181 
182         // if the builder is invalid, these will return 0
183         int vertexCount() const;
184         int indexCount() const;
185         bool isVolatile() const;
186         SkPoint* positions();
187         SkPoint* texCoords();       // returns null if there are no texCoords
188         SkColor* colors();          // returns null if there are no colors
189         BoneIndices* boneIndices(); // returns null if there are no bone indices
190         BoneWeights* boneWeights(); // returns null if there are no bone weights
191         uint16_t* indices();        // returns null if there are no indices
192 
193         // Detach the built vertices object. After the first call, this will always return null.
194         sk_sp<SkVertices> detach();
195 
196     private:
197         Builder(VertexMode mode, int vertexCount, int indexCount, bool isVolatile, const Sizes&);
198 
199         void init(VertexMode mode, int vertexCount, int indexCount, bool isVolatile, const Sizes&);
200 
201         // holds a partially complete object. only completed in detach()
202         sk_sp<SkVertices> fVertices;
203         // Extra storage for intermediate vertices in the case where the client specifies indexed
204         // triangle fans. These get converted to indexed triangles when the Builder is finalized.
205         std::unique_ptr<uint8_t[]> fIntermediateFanIndices;
206 
207         friend class SkVertices;
208     };
209 
uniqueID()210     uint32_t uniqueID() const { return fUniqueID; }
mode()211     VertexMode mode() const { return fMode; }
bounds()212     const SkRect& bounds() const { return fBounds; }
213 
hasColors()214     bool hasColors() const { return SkToBool(this->colors()); }
hasTexCoords()215     bool hasTexCoords() const { return SkToBool(this->texCoords()); }
hasBones()216     bool hasBones() const { return SkToBool(this->boneIndices()); }
hasIndices()217     bool hasIndices() const { return SkToBool(this->indices()); }
218 
vertexCount()219     int vertexCount() const { return fVertexCnt; }
positions()220     const SkPoint* positions() const { return fPositions; }
texCoords()221     const SkPoint* texCoords() const { return fTexs; }
colors()222     const SkColor* colors() const { return fColors; }
223 
boneIndices()224     const BoneIndices* boneIndices() const { return fBoneIndices; }
boneWeights()225     const BoneWeights* boneWeights() const { return fBoneWeights; }
226 
indexCount()227     int indexCount() const { return fIndexCnt; }
indices()228     const uint16_t* indices() const { return fIndices; }
229 
isVolatile()230     bool isVolatile() const { return fIsVolatile; }
231 
232     sk_sp<SkVertices> applyBones(const Bone bones[], int boneCount) const;
233 
234     // returns approximate byte size of the vertices object
235     size_t approximateSize() const;
236 
237     /**
238      *  Recreate a vertices from a buffer previously created by calling encode().
239      *  Returns null if the data is corrupt or the length is incorrect for the contents.
240      */
241     static sk_sp<SkVertices> Decode(const void* buffer, size_t length);
242 
243     /**
244      *  Pack the vertices object into a byte buffer. This can be used to recreate the vertices
245      *  by calling Decode() with the buffer.
246      */
247     sk_sp<SkData> encode() const;
248 
249 private:
SkVertices()250     SkVertices() {}
251 
252     // these are needed since we've manually sized our allocation (see Builder::init)
253     friend class SkNVRefCnt<SkVertices>;
254     void operator delete(void* p);
255 
256     static sk_sp<SkVertices> Alloc(int vCount, int iCount, uint32_t builderFlags,
257                                    size_t* arraySize);
258 
259     // we store this first, to pair with the refcnt in our base-class, so we don't have an
260     // unnecessary pad between it and the (possibly 8-byte aligned) ptrs.
261     uint32_t fUniqueID;
262 
263     // these point inside our allocation, so none of these can be "freed"
264     SkPoint*     fPositions;
265     SkPoint*     fTexs;
266     SkColor*     fColors;
267     BoneIndices* fBoneIndices;
268     BoneWeights* fBoneWeights;
269     uint16_t*    fIndices;
270 
271     SkRect  fBounds;    // computed to be the union of the fPositions[]
272     int     fVertexCnt;
273     int     fIndexCnt;
274 
275     bool fIsVolatile;
276 
277     VertexMode fMode;
278     // below here is where the actual array data is stored.
279 };
280 
281 #endif
282