/* * Copyright 2017 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkVertices.h" #include "SkData.h" #include "SkReader32.h" #include "SkSafeMath.h" #include "SkSafeRange.h" #include "SkTo.h" #include "SkWriter32.h" #include #include static int32_t next_id() { static std::atomic nextID{1}; int32_t id; do { id = nextID++; } while (id == SK_InvalidGenID); return id; } struct SkVertices::Sizes { Sizes(SkVertices::VertexMode mode, int vertexCount, int indexCount, bool hasTexs, bool hasColors, bool hasBones) { SkSafeMath safe; fVSize = safe.mul(vertexCount, sizeof(SkPoint)); fTSize = hasTexs ? safe.mul(vertexCount, sizeof(SkPoint)) : 0; fCSize = hasColors ? safe.mul(vertexCount, sizeof(SkColor)) : 0; fBISize = hasBones ? safe.mul(vertexCount, sizeof(BoneIndices)) : 0; fBWSize = hasBones ? safe.mul(vertexCount, sizeof(BoneWeights)) : 0; fBuilderTriFanISize = 0; fISize = safe.mul(indexCount, sizeof(uint16_t)); if (kTriangleFan_VertexMode == mode) { int numFanTris = 0; if (indexCount) { fBuilderTriFanISize = fISize; numFanTris = indexCount - 2; } else { numFanTris = vertexCount - 2; // By forcing this to become indexed we are adding a constraint to the maximum // number of vertices. if (vertexCount > (SkTo(UINT16_MAX) + 1)) { sk_bzero(this, sizeof(*this)); return; } } if (numFanTris <= 0) { sk_bzero(this, sizeof(*this)); return; } fISize = safe.mul(numFanTris, 3 * sizeof(uint16_t)); } fTotal = safe.add(sizeof(SkVertices), safe.add(fVSize, safe.add(fTSize, safe.add(fCSize, safe.add(fBISize, safe.add(fBWSize, fISize)))))); if (safe.ok()) { fArrays = fTotal - sizeof(SkVertices); // just the sum of the arrays } else { sk_bzero(this, sizeof(*this)); } } bool isValid() const { return fTotal != 0; } size_t fTotal; // size of entire SkVertices allocation (obj + arrays) size_t fArrays; // size of all the arrays (V + T + C + BI + BW + I) size_t fVSize; size_t fTSize; size_t fCSize; size_t fBISize; size_t fBWSize; size_t fISize; // For indexed tri-fans this is the number of amount of space fo indices needed in the builder // before conversion to indexed triangles (or zero if not indexed or not a triangle fan). size_t fBuilderTriFanISize; }; SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount, uint32_t builderFlags) { bool hasTexs = SkToBool(builderFlags & SkVertices::kHasTexCoords_BuilderFlag); bool hasColors = SkToBool(builderFlags & SkVertices::kHasColors_BuilderFlag); bool hasBones = SkToBool(builderFlags & SkVertices::kHasBones_BuilderFlag); bool isVolatile = !SkToBool(builderFlags & SkVertices::kIsNonVolatile_BuilderFlag); this->init(mode, vertexCount, indexCount, isVolatile, SkVertices::Sizes(mode, vertexCount, indexCount, hasTexs, hasColors, hasBones)); } SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount, bool isVolatile, const SkVertices::Sizes& sizes) { this->init(mode, vertexCount, indexCount, isVolatile, sizes); } void SkVertices::Builder::init(VertexMode mode, int vertexCount, int indexCount, bool isVolatile, const SkVertices::Sizes& sizes) { if (!sizes.isValid()) { return; // fVertices will already be null } void* storage = ::operator new (sizes.fTotal); if (sizes.fBuilderTriFanISize) { fIntermediateFanIndices.reset(new uint8_t[sizes.fBuilderTriFanISize]); } fVertices.reset(new (storage) SkVertices); // need to point past the object to store the arrays char* ptr = (char*)storage + sizeof(SkVertices); fVertices->fPositions = (SkPoint*)ptr; ptr += sizes.fVSize; fVertices->fTexs = sizes.fTSize ? (SkPoint*)ptr : nullptr; ptr += sizes.fTSize; fVertices->fColors = sizes.fCSize ? (SkColor*)ptr : nullptr; ptr += sizes.fCSize; fVertices->fBoneIndices = sizes.fBISize ? (BoneIndices*) ptr : nullptr; ptr += sizes.fBISize; fVertices->fBoneWeights = sizes.fBWSize ? (BoneWeights*) ptr : nullptr; ptr += sizes.fBWSize; fVertices->fIndices = sizes.fISize ? (uint16_t*)ptr : nullptr; fVertices->fVertexCnt = vertexCount; fVertices->fIndexCnt = indexCount; fVertices->fIsVolatile = isVolatile; fVertices->fMode = mode; // We defer assigning fBounds and fUniqueID until detach() is called } sk_sp SkVertices::Builder::detach() { if (fVertices) { fVertices->fBounds.set(fVertices->fPositions, fVertices->fVertexCnt); if (fVertices->fMode == kTriangleFan_VertexMode) { if (fIntermediateFanIndices.get()) { SkASSERT(fVertices->fIndexCnt); auto tempIndices = this->indices(); for (int t = 0; t < fVertices->fIndexCnt - 2; ++t) { fVertices->fIndices[3 * t + 0] = tempIndices[0]; fVertices->fIndices[3 * t + 1] = tempIndices[t + 1]; fVertices->fIndices[3 * t + 2] = tempIndices[t + 2]; } fVertices->fIndexCnt = 3 * (fVertices->fIndexCnt - 2); } else { SkASSERT(!fVertices->fIndexCnt); for (int t = 0; t < fVertices->fVertexCnt - 2; ++t) { fVertices->fIndices[3 * t + 0] = 0; fVertices->fIndices[3 * t + 1] = SkToU16(t + 1); fVertices->fIndices[3 * t + 2] = SkToU16(t + 2); } fVertices->fIndexCnt = 3 * (fVertices->fVertexCnt - 2); } fVertices->fMode = kTriangles_VertexMode; } fVertices->fUniqueID = next_id(); return std::move(fVertices); // this will null fVertices after the return } return nullptr; } int SkVertices::Builder::vertexCount() const { return fVertices ? fVertices->vertexCount() : 0; } int SkVertices::Builder::indexCount() const { return fVertices ? fVertices->indexCount() : 0; } bool SkVertices::Builder::isVolatile() const { return fVertices ? fVertices->isVolatile() : true; } SkPoint* SkVertices::Builder::positions() { return fVertices ? const_cast(fVertices->positions()) : nullptr; } SkPoint* SkVertices::Builder::texCoords() { return fVertices ? const_cast(fVertices->texCoords()) : nullptr; } SkColor* SkVertices::Builder::colors() { return fVertices ? const_cast(fVertices->colors()) : nullptr; } SkVertices::BoneIndices* SkVertices::Builder::boneIndices() { return fVertices ? const_cast(fVertices->boneIndices()) : nullptr; } SkVertices::BoneWeights* SkVertices::Builder::boneWeights() { return fVertices ? const_cast(fVertices->boneWeights()) : nullptr; } uint16_t* SkVertices::Builder::indices() { if (!fVertices) { return nullptr; } if (fIntermediateFanIndices) { return reinterpret_cast(fIntermediateFanIndices.get()); } return const_cast(fVertices->indices()); } /** Makes a copy of the SkVertices and applies a set of bones, then returns the deformed vertices. @param bones The bones to apply. @param boneCount The number of bones. @return The transformed SkVertices. */ sk_sp SkVertices::applyBones(const SkVertices::Bone bones[], int boneCount) const { // If there aren't any bones, then nothing changes. // We don't check if the SkVertices object has bone indices/weights because there is the case // where the object can have no indices/weights but still have a world transform applied. if (!bones || !boneCount) { return sk_ref_sp(this); } SkASSERT(boneCount >= 1); // Copy the SkVertices. sk_sp copy = SkVertices::MakeCopy(this->mode(), this->vertexCount(), this->positions(), this->texCoords(), this->colors(), nullptr, nullptr, this->indexCount(), this->indices()); // Transform the positions. for (int i = 0; i < this->vertexCount(); i++) { SkPoint& position = copy->fPositions[i]; // Apply the world transform. position = bones[0].mapPoint(position); // Apply the bone deformations. if (boneCount > 1) { SkASSERT(this->boneIndices()); SkASSERT(this->boneWeights()); SkPoint result = SkPoint::Make(0.0f, 0.0f); const SkVertices::BoneIndices& indices = this->boneIndices()[i]; const SkVertices::BoneWeights& weights = this->boneWeights()[i]; for (int j = 0; j < 4; j++) { int index = indices[j]; float weight = weights[j]; if (index == 0 || weight == 0.0f) { continue; } SkASSERT(index < boneCount); // result += M * v * w. result += bones[index].mapPoint(position) * weight; } position = result; } } // Recalculate the bounds. copy->fBounds.set(copy->fPositions, copy->fVertexCnt); return copy; } /////////////////////////////////////////////////////////////////////////////////////////////////// sk_sp SkVertices::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) { SkASSERT((!boneIndices && !boneWeights) || (boneIndices && boneWeights)); Sizes sizes(mode, vertexCount, indexCount, texs != nullptr, colors != nullptr, boneIndices != nullptr); if (!sizes.isValid()) { return nullptr; } Builder builder(mode, vertexCount, indexCount, isVolatile, sizes); SkASSERT(builder.isValid()); sk_careful_memcpy(builder.positions(), pos, sizes.fVSize); sk_careful_memcpy(builder.texCoords(), texs, sizes.fTSize); sk_careful_memcpy(builder.colors(), colors, sizes.fCSize); sk_careful_memcpy(builder.boneIndices(), boneIndices, sizes.fBISize); sk_careful_memcpy(builder.boneWeights(), boneWeights, sizes.fBWSize); size_t isize = (mode == kTriangleFan_VertexMode) ? sizes.fBuilderTriFanISize : sizes.fISize; sk_careful_memcpy(builder.indices(), indices, isize); return builder.detach(); } size_t SkVertices::approximateSize() const { Sizes sizes(fMode, fVertexCnt, fIndexCnt, this->hasTexCoords(), this->hasColors(), this->hasBones()); SkASSERT(sizes.isValid()); return sizeof(SkVertices) + sizes.fArrays; } /////////////////////////////////////////////////////////////////////////////////////////////////// // storage = packed | vertex_count | index_count | pos[] | texs[] | colors[] | boneIndices[] | // boneWeights[] | indices[] // = header + arrays #define kMode_Mask 0x0FF #define kHasTexs_Mask 0x100 #define kHasColors_Mask 0x200 #define kHasBones_Mask 0x400 #define kIsNonVolatile_Mask 0x800 #define kHeaderSize (3 * sizeof(uint32_t)) sk_sp SkVertices::encode() const { // packed has room for addtional flags in the future (e.g. versioning) uint32_t packed = static_cast(fMode); SkASSERT((packed & ~kMode_Mask) == 0); // our mode fits in the mask bits if (this->hasTexCoords()) { packed |= kHasTexs_Mask; } if (this->hasColors()) { packed |= kHasColors_Mask; } if (this->hasBones()) { packed |= kHasBones_Mask; } if (!this->isVolatile()) { packed |= kIsNonVolatile_Mask; } Sizes sizes(fMode, fVertexCnt, fIndexCnt, this->hasTexCoords(), this->hasColors(), this->hasBones()); SkASSERT(sizes.isValid()); SkASSERT(!sizes.fBuilderTriFanISize); // need to force alignment to 4 for SkWriter32 -- will pad w/ 0s as needed const size_t size = SkAlign4(kHeaderSize + sizes.fArrays); sk_sp data = SkData::MakeUninitialized(size); SkWriter32 writer(data->writable_data(), data->size()); writer.write32(packed); writer.write32(fVertexCnt); writer.write32(fIndexCnt); writer.write(fPositions, sizes.fVSize); writer.write(fTexs, sizes.fTSize); writer.write(fColors, sizes.fCSize); writer.write(fBoneIndices, sizes.fBISize); writer.write(fBoneWeights, sizes.fBWSize); // if index-count is odd, we won't be 4-bytes aligned, so we call the pad version writer.writePad(fIndices, sizes.fISize); return data; } sk_sp SkVertices::Decode(const void* data, size_t length) { if (length < kHeaderSize) { return nullptr; } SkReader32 reader(data, length); SkSafeRange safe; const uint32_t packed = reader.readInt(); const int vertexCount = safe.checkGE(reader.readInt(), 0); const int indexCount = safe.checkGE(reader.readInt(), 0); const VertexMode mode = safe.checkLE(packed & kMode_Mask, SkVertices::kLast_VertexMode); if (!safe) { return nullptr; } const bool hasTexs = SkToBool(packed & kHasTexs_Mask); const bool hasColors = SkToBool(packed & kHasColors_Mask); const bool hasBones = SkToBool(packed & kHasBones_Mask); const bool isVolatile = !SkToBool(packed & kIsNonVolatile_Mask); Sizes sizes(mode, vertexCount, indexCount, hasTexs, hasColors, hasBones); if (!sizes.isValid()) { return nullptr; } // logically we can be only 2-byte aligned, but our buffer is always 4-byte aligned if (SkAlign4(kHeaderSize + sizes.fArrays) != length) { return nullptr; } Builder builder(mode, vertexCount, indexCount, isVolatile, sizes); reader.read(builder.positions(), sizes.fVSize); reader.read(builder.texCoords(), sizes.fTSize); reader.read(builder.colors(), sizes.fCSize); reader.read(builder.boneIndices(), sizes.fBISize); reader.read(builder.boneWeights(), sizes.fBWSize); size_t isize = (mode == kTriangleFan_VertexMode) ? sizes.fBuilderTriFanISize : sizes.fISize; reader.read(builder.indices(), isize); if (indexCount > 0) { // validate that the indicies are in range SkASSERT(indexCount == builder.indexCount()); const uint16_t* indices = builder.indices(); for (int i = 0; i < indexCount; ++i) { if (indices[i] >= (unsigned)vertexCount) { return nullptr; } } } return builder.detach(); } void SkVertices::operator delete(void* p) { ::operator delete(p); }