blob: c07e2dae63200af6d05cc87f4f3847c20e7713b6 [file] [log] [blame]
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkVertices_DEFINED
#define SkVertices_DEFINED
#include "include/core/SkColor.h"
#include "include/core/SkData.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
/**
* An immutable set of vertex data that can be used with SkCanvas::drawVertices.
*/
class SK_API SkVertices : public SkNVRefCnt<SkVertices> {
public:
// BoneIndices indicates which (of a maximum of 4 bones) a given vertex will interpolate
// between. To indicate that a slot is not used, the convention is to assign the bone index
// to 0.
struct BoneIndices {
uint32_t indices[4];
uint32_t& operator[] (int i) {
SkASSERT(i >= 0);
SkASSERT(i < 4);
return indices[i];
}
const uint32_t& operator[] (int i) const {
SkASSERT(i >= 0);
SkASSERT(i < 4);
return indices[i];
}
};
// BoneWeights stores the interpolation weight for each of the (maximum of 4) bones a given
// vertex interpolates between. To indicate that a slot is not used, the weight for that
// slot should be 0.
struct BoneWeights {
float weights[4];
float& operator[] (int i) {
SkASSERT(i >= 0);
SkASSERT(i < 4);
return weights[i];
}
const float& operator[] (int i) const {
SkASSERT(i >= 0);
SkASSERT(i < 4);
return weights[i];
}
};
// Bone stores a 3x2 transformation matrix in column major order:
// | scaleX skewX transX |
// | skewY scaleY transY |
// SkRSXform is insufficient because bones can have non uniform scale.
struct Bone {
float values[6];
float& operator[] (int i) {
SkASSERT(i >= 0);
SkASSERT(i < 6);
return values[i];
}
const float& operator[] (int i) const {
SkASSERT(i >= 0);
SkASSERT(i < 6);
return values[i];
}
SkPoint mapPoint(const SkPoint& point) const {
float x = values[0] * point.x() + values[2] * point.y() + values[4];
float y = values[1] * point.x() + values[3] * point.y() + values[5];
return SkPoint::Make(x, y);
}
SkRect mapRect(const SkRect& rect) const {
SkRect dst = SkRect::MakeEmpty();
SkPoint quad[4];
rect.toQuad(quad);
for (int i = 0; i < 4; i ++) {
quad[i] = mapPoint(quad[i]);
}
dst.setBoundsNoCheck(quad, 4);
return dst;
}
};
enum VertexMode {
kTriangles_VertexMode,
kTriangleStrip_VertexMode,
kTriangleFan_VertexMode,
kLast_VertexMode = kTriangleFan_VertexMode,
};
/**
* Create a vertices by copying the specified arrays. texs, colors, boneIndices, and
* boneWeights may be nullptr, and indices is ignored if indexCount == 0.
*
* boneIndices and boneWeights must either both be nullptr or both point to valid data.
* If specified, they must both contain 'vertexCount' entries.
*/
static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
const SkPoint positions[],
const SkPoint texs[],
const SkColor colors[],
const BoneIndices boneIndices[],
const BoneWeights boneWeights[],
int indexCount,
const uint16_t indices[],
bool isVolatile = true);
static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
const SkPoint positions[],
const SkPoint texs[],
const SkColor colors[],
const BoneIndices boneIndices[],
const BoneWeights boneWeights[],
bool isVolatile = true) {
return MakeCopy(mode,
vertexCount,
positions,
texs,
colors,
boneIndices,
boneWeights,
0,
nullptr,
isVolatile);
}
static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
const SkPoint positions[],
const SkPoint texs[],
const SkColor colors[],
int indexCount,
const uint16_t indices[],
bool isVolatile = true) {
return MakeCopy(mode,
vertexCount,
positions,
texs,
colors,
nullptr,
nullptr,
indexCount,
indices,
isVolatile);
}
static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
const SkPoint positions[],
const SkPoint texs[],
const SkColor colors[],
bool isVolatile = true) {
return MakeCopy(mode, vertexCount, positions, texs, colors, nullptr, nullptr, isVolatile);
}
struct Sizes;
enum BuilderFlags {
kHasTexCoords_BuilderFlag = 1 << 0,
kHasColors_BuilderFlag = 1 << 1,
kHasBones_BuilderFlag = 1 << 2,
kIsNonVolatile_BuilderFlag = 1 << 3,
};
class Builder {
public:
Builder(VertexMode mode, int vertexCount, int indexCount, uint32_t flags);
bool isValid() const { return fVertices != nullptr; }
// if the builder is invalid, these will return 0
int vertexCount() const;
int indexCount() const;
bool isVolatile() const;
SkPoint* positions();
SkPoint* texCoords(); // returns null if there are no texCoords
SkColor* colors(); // returns null if there are no colors
BoneIndices* boneIndices(); // returns null if there are no bone indices
BoneWeights* boneWeights(); // returns null if there are no bone weights
uint16_t* indices(); // returns null if there are no indices
// Detach the built vertices object. After the first call, this will always return null.
sk_sp<SkVertices> detach();
private:
Builder(VertexMode mode, int vertexCount, int indexCount, bool isVolatile, const Sizes&);
void init(VertexMode mode, int vertexCount, int indexCount, bool isVolatile, const Sizes&);
// holds a partially complete object. only completed in detach()
sk_sp<SkVertices> fVertices;
// Extra storage for intermediate vertices in the case where the client specifies indexed
// triangle fans. These get converted to indexed triangles when the Builder is finalized.
std::unique_ptr<uint8_t[]> fIntermediateFanIndices;
friend class SkVertices;
};
uint32_t uniqueID() const { return fUniqueID; }
VertexMode mode() const { return fMode; }
const SkRect& bounds() const { return fBounds; }
bool hasColors() const { return SkToBool(this->colors()); }
bool hasTexCoords() const { return SkToBool(this->texCoords()); }
bool hasBones() const { return SkToBool(this->boneIndices()); }
bool hasIndices() const { return SkToBool(this->indices()); }
int vertexCount() const { return fVertexCnt; }
const SkPoint* positions() const { return fPositions; }
const SkPoint* texCoords() const { return fTexs; }
const SkColor* colors() const { return fColors; }
const BoneIndices* boneIndices() const { return fBoneIndices; }
const BoneWeights* boneWeights() const { return fBoneWeights; }
int indexCount() const { return fIndexCnt; }
const uint16_t* indices() const { return fIndices; }
bool isVolatile() const { return fIsVolatile; }
sk_sp<SkVertices> applyBones(const Bone bones[], int boneCount) const;
// returns approximate byte size of the vertices object
size_t approximateSize() const;
/**
* Recreate a vertices from a buffer previously created by calling encode().
* Returns null if the data is corrupt or the length is incorrect for the contents.
*/
static sk_sp<SkVertices> Decode(const void* buffer, size_t length);
/**
* Pack the vertices object into a byte buffer. This can be used to recreate the vertices
* by calling Decode() with the buffer.
*/
sk_sp<SkData> encode() const;
private:
SkVertices() {}
// these are needed since we've manually sized our allocation (see Builder::init)
friend class SkNVRefCnt<SkVertices>;
void operator delete(void* p);
static sk_sp<SkVertices> Alloc(int vCount, int iCount, uint32_t builderFlags,
size_t* arraySize);
// we store this first, to pair with the refcnt in our base-class, so we don't have an
// unnecessary pad between it and the (possibly 8-byte aligned) ptrs.
uint32_t fUniqueID;
// these point inside our allocation, so none of these can be "freed"
SkPoint* fPositions;
SkPoint* fTexs;
SkColor* fColors;
BoneIndices* fBoneIndices;
BoneWeights* fBoneWeights;
uint16_t* fIndices;
SkRect fBounds; // computed to be the union of the fPositions[]
int fVertexCnt;
int fIndexCnt;
bool fIsVolatile;
VertexMode fMode;
// below here is where the actual array data is stored.
};
#endif