Home | History | Annotate | Download | only in core
      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 "SkAtomics.h"
      9 #include "SkVertices.h"
     10 #include "SkData.h"
     11 #include "SkReader32.h"
     12 #include "SkSafeMath.h"
     13 #include "SkSafeRange.h"
     14 #include "SkWriter32.h"
     15 
     16 static int32_t gNextID = 1;
     17 static int32_t next_id() {
     18     int32_t id;
     19     do {
     20         id = sk_atomic_inc(&gNextID);
     21     } while (id == SK_InvalidGenID);
     22     return id;
     23 }
     24 
     25 struct SkVertices::Sizes {
     26     Sizes(int vertexCount, int indexCount, bool hasTexs, bool hasColors) {
     27         SkSafeMath safe;
     28 
     29         fVSize = safe.mul(vertexCount, sizeof(SkPoint));
     30         fTSize = hasTexs ? safe.mul(vertexCount, sizeof(SkPoint)) : 0;
     31         fCSize = hasColors ? safe.mul(vertexCount, sizeof(SkColor)) : 0;
     32         fISize = safe.mul(indexCount, sizeof(uint16_t));
     33         fTotal = safe.add(sizeof(SkVertices),
     34                  safe.add(fVSize,
     35                  safe.add(fTSize,
     36                  safe.add(fCSize,
     37                           fISize))));
     38 
     39         if (safe.ok()) {
     40             fArrays = fTotal - sizeof(SkVertices);  // just the sum of the arrays
     41         } else {
     42             sk_bzero(this, sizeof(*this));
     43         }
     44     }
     45 
     46     bool isValid() const { return fTotal != 0; }
     47 
     48     size_t fTotal;  // size of entire SkVertices allocation (obj + arrays)
     49     size_t fArrays; // size of all the arrays (V + T + C + I)
     50     size_t fVSize;
     51     size_t fTSize;
     52     size_t fCSize;
     53     size_t fISize;
     54 };
     55 
     56 SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount,
     57                              uint32_t builderFlags) {
     58     bool hasTexs = SkToBool(builderFlags & SkVertices::kHasTexCoords_BuilderFlag);
     59     bool hasColors = SkToBool(builderFlags & SkVertices::kHasColors_BuilderFlag);
     60     this->init(mode, vertexCount, indexCount,
     61                SkVertices::Sizes(vertexCount, indexCount, hasTexs, hasColors));
     62 }
     63 
     64 SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount,
     65                              const SkVertices::Sizes& sizes) {
     66     this->init(mode, vertexCount, indexCount, sizes);
     67 }
     68 
     69 void SkVertices::Builder::init(VertexMode mode, int vertexCount, int indexCount,
     70                                const SkVertices::Sizes& sizes) {
     71     if (!sizes.isValid()) {
     72         return; // fVertices will already be null
     73     }
     74 
     75     void* storage = ::operator new (sizes.fTotal);
     76     fVertices.reset(new (storage) SkVertices);
     77 
     78     // need to point past the object to store the arrays
     79     char* ptr = (char*)storage + sizeof(SkVertices);
     80 
     81     fVertices->fPositions = (SkPoint*)ptr;                          ptr += sizes.fVSize;
     82     fVertices->fTexs = sizes.fTSize ? (SkPoint*)ptr : nullptr;      ptr += sizes.fTSize;
     83     fVertices->fColors = sizes.fCSize ? (SkColor*)ptr : nullptr;    ptr += sizes.fCSize;
     84     fVertices->fIndices = sizes.fISize ? (uint16_t*)ptr : nullptr;
     85     fVertices->fVertexCnt = vertexCount;
     86     fVertices->fIndexCnt = indexCount;
     87     fVertices->fMode = mode;
     88     // We defer assigning fBounds and fUniqueID until detach() is called
     89 }
     90 
     91 sk_sp<SkVertices> SkVertices::Builder::detach() {
     92     if (fVertices) {
     93         fVertices->fBounds.set(fVertices->fPositions, fVertices->fVertexCnt);
     94         fVertices->fUniqueID = next_id();
     95         return std::move(fVertices);        // this will null fVertices after the return
     96     }
     97     return nullptr;
     98 }
     99 
    100 int SkVertices::Builder::vertexCount() const {
    101     return fVertices ? fVertices->vertexCount() : 0;
    102 }
    103 
    104 int SkVertices::Builder::indexCount() const {
    105     return fVertices ? fVertices->indexCount() : 0;
    106 }
    107 
    108 SkPoint* SkVertices::Builder::positions() {
    109     return fVertices ? const_cast<SkPoint*>(fVertices->positions()) : nullptr;
    110 }
    111 
    112 SkPoint* SkVertices::Builder::texCoords() {
    113     return fVertices ? const_cast<SkPoint*>(fVertices->texCoords()) : nullptr;
    114 }
    115 
    116 SkColor* SkVertices::Builder::colors() {
    117     return fVertices ? const_cast<SkColor*>(fVertices->colors()) : nullptr;
    118 }
    119 
    120 uint16_t* SkVertices::Builder::indices() {
    121     return fVertices ? const_cast<uint16_t*>(fVertices->indices()) : nullptr;
    122 }
    123 
    124 ///////////////////////////////////////////////////////////////////////////////////////////////////
    125 
    126 sk_sp<SkVertices> SkVertices::MakeCopy(VertexMode mode, int vertexCount,
    127                                        const SkPoint pos[], const SkPoint texs[],
    128                                        const SkColor colors[], int indexCount,
    129                                        const uint16_t indices[]) {
    130     Sizes sizes(vertexCount, indexCount, texs != nullptr, colors != nullptr);
    131     if (!sizes.isValid()) {
    132         return nullptr;
    133     }
    134 
    135     Builder builder(mode, vertexCount, indexCount, sizes);
    136     SkASSERT(builder.isValid());
    137 
    138     sk_careful_memcpy(builder.positions(), pos, sizes.fVSize);
    139     sk_careful_memcpy(builder.texCoords(), texs, sizes.fTSize);
    140     sk_careful_memcpy(builder.colors(), colors, sizes.fCSize);
    141     sk_careful_memcpy(builder.indices(), indices, sizes.fISize);
    142 
    143     return builder.detach();
    144 }
    145 
    146 size_t SkVertices::approximateSize() const {
    147     Sizes sizes(fVertexCnt, fIndexCnt, this->hasTexCoords(), this->hasColors());
    148     SkASSERT(sizes.isValid());
    149     return sizeof(SkVertices) + sizes.fArrays;
    150 }
    151 
    152 ///////////////////////////////////////////////////////////////////////////////////////////////////
    153 
    154 // storage = packed | vertex_count | index_count | pos[] | texs[] | colors[] | indices[]
    155 //         = header + arrays
    156 
    157 #define kMode_Mask          0x0FF
    158 #define kHasTexs_Mask       0x100
    159 #define kHasColors_Mask     0x200
    160 #define kHeaderSize         (3 * sizeof(uint32_t))
    161 
    162 sk_sp<SkData> SkVertices::encode() const {
    163     // packed has room for addtional flags in the future (e.g. versioning)
    164     uint32_t packed = static_cast<uint32_t>(fMode);
    165     SkASSERT((packed & ~kMode_Mask) == 0);  // our mode fits in the mask bits
    166     if (this->hasTexCoords()) {
    167         packed |= kHasTexs_Mask;
    168     }
    169     if (this->hasColors()) {
    170         packed |= kHasColors_Mask;
    171     }
    172 
    173     Sizes sizes(fVertexCnt, fIndexCnt, this->hasTexCoords(), this->hasColors());
    174     SkASSERT(sizes.isValid());
    175     // need to force alignment to 4 for SkWriter32 -- will pad w/ 0s as needed
    176     const size_t size = SkAlign4(kHeaderSize + sizes.fArrays);
    177 
    178     sk_sp<SkData> data = SkData::MakeUninitialized(size);
    179     SkWriter32 writer(data->writable_data(), data->size());
    180 
    181     writer.write32(packed);
    182     writer.write32(fVertexCnt);
    183     writer.write32(fIndexCnt);
    184     writer.write(fPositions, sizes.fVSize);
    185     writer.write(fTexs, sizes.fTSize);
    186     writer.write(fColors, sizes.fCSize);
    187     // if index-count is odd, we won't be 4-bytes aligned, so we call the pad version
    188     writer.writePad(fIndices, sizes.fISize);
    189 
    190     return data;
    191 }
    192 
    193 sk_sp<SkVertices> SkVertices::Decode(const void* data, size_t length) {
    194     if (length < kHeaderSize) {
    195         return nullptr;
    196     }
    197 
    198     SkReader32 reader(data, length);
    199     SkSafeRange safe;
    200 
    201     const uint32_t packed = reader.readInt();
    202     const int vertexCount = safe.checkGE(reader.readInt(), 0);
    203     const int indexCount = safe.checkGE(reader.readInt(), 0);
    204     const VertexMode mode = safe.checkLE<VertexMode>(packed & kMode_Mask,
    205                                                      SkVertices::kLast_VertexMode);
    206     if (!safe) {
    207         return nullptr;
    208     }
    209     const bool hasTexs = SkToBool(packed & kHasTexs_Mask);
    210     const bool hasColors = SkToBool(packed & kHasColors_Mask);
    211     Sizes sizes(vertexCount, indexCount, hasTexs, hasColors);
    212     if (!sizes.isValid()) {
    213         return nullptr;
    214     }
    215     // logically we can be only 2-byte aligned, but our buffer is always 4-byte aligned
    216     if (SkAlign4(kHeaderSize + sizes.fArrays) != length) {
    217         return nullptr;
    218     }
    219 
    220     Builder builder(mode, vertexCount, indexCount, sizes);
    221 
    222     reader.read(builder.positions(), sizes.fVSize);
    223     reader.read(builder.texCoords(), sizes.fTSize);
    224     reader.read(builder.colors(), sizes.fCSize);
    225     reader.read(builder.indices(), sizes.fISize);
    226     if (indexCount > 0) {
    227         // validate that the indicies are in range
    228         SkASSERT(indexCount == builder.indexCount());
    229         const uint16_t* indices = builder.indices();
    230         for (int i = 0; i < indexCount; ++i) {
    231             if (indices[i] >= (unsigned)vertexCount) {
    232                 return nullptr;
    233             }
    234         }
    235     }
    236     return builder.detach();
    237 }
    238