/*
* Copyright 2010 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrVertexWriter_DEFINED
#define GrVertexWriter_DEFINED
#include "GrQuad.h"
#include "SkTemplates.h"
#include <type_traits>
/**
* Helper for writing vertex data to a buffer. Usage:
* GrVertexWriter vertices{target->makeVertexSpace(...)};
* vertices.write(A0, B0, C0, ...);
* vertices.write(A1, B1, C1, ...);
*
* Supports any number of arguments. Each argument must be POD (plain old data), or an array
* thereof.
*/
struct GrVertexWriter {
void* fPtr;
template <typename T>
class Conditional {
public:
explicit Conditional(bool condition, const T& value)
: fCondition(condition), fValue(value) {}
private:
friend struct GrVertexWriter;
bool fCondition;
T fValue;
};
template <typename T>
static Conditional<T> If(bool condition, const T& value) {
return Conditional<T>(condition, value);
}
template <typename T>
struct Skip {};
template <typename T, typename... Args>
void write(const T& val, const Args&... remainder) {
static_assert(std::is_pod<T>::value, "");
// This assert is barely related to what we're trying to check - that our vertex data
// matches our attribute layouts, where each attribute is aligned to four bytes. If this
// becomes a problem, just remove it.
static_assert(alignof(T) <= 4, "");
memcpy(fPtr, &val, sizeof(T));
fPtr = SkTAddOffset<void>(fPtr, sizeof(T));
this->write(remainder...);
}
template <typename T, size_t N, typename... Args>
void write(const T(&val)[N], const Args&... remainder) {
static_assert(std::is_pod<T>::value, "");
static_assert(alignof(T) <= 4, "");
memcpy(fPtr, val, N * sizeof(T));
fPtr = SkTAddOffset<void>(fPtr, N * sizeof(T));
this->write(remainder...);
}
template <typename... Args>
void write(const GrVertexColor& color, const Args&... remainder) {
this->write(color.fColor[0]);
if (color.fWideColor) {
this->write(color.fColor[1]);
}
this->write(remainder...);
}
template <typename T, typename... Args>
void write(const Conditional<T>& val, const Args&... remainder) {
if (val.fCondition) {
this->write(val.fValue);
}
this->write(remainder...);
}
template <typename T, typename... Args>
void write(const Skip<T>& val, const Args&... remainder) {
fPtr = SkTAddOffset<void>(fPtr, sizeof(T));
this->write(remainder...);
}
template <typename... Args>
void write(const Sk4f& vector, const Args&... remainder) {
float buffer[4];
vector.store(buffer);
this->write<float, 4>(buffer);
this->write(remainder...);
}
void write() {}
/**
* Specialized utility for writing a four-vertices, with some data being replicated at each
* vertex, and other data being the appropriate 2-components from an SkRect to construct a
* triangle strip.
*
* writeQuad(A, B, C, ...) is similar to write(A, B, C, ...), except that:
*
* - Four sets of data will be written
* - For any arguments of type TriStrip, a unique SkPoint will be written at each vertex,
* in this order: left-top, left-bottom, right-top, right-bottom.
*/
template <typename T>
struct TriStrip { T l, t, r, b; };
static TriStrip<float> TriStripFromRect(const SkRect& r) {
return { r.fLeft, r.fTop, r.fRight, r.fBottom };
}
template <typename T>
struct TriFan { T l, t, r, b; };
static TriFan<float> TriFanFromRect(const SkRect& r) {
return { r.fLeft, r.fTop, r.fRight, r.fBottom };
}
template <typename... Args>
void writeQuad(const Args&... remainder) {
this->writeQuadVert<0>(remainder...);
this->writeQuadVert<1>(remainder...);
this->writeQuadVert<2>(remainder...);
this->writeQuadVert<3>(remainder...);
}
private:
template <int corner, typename T, typename... Args>
void writeQuadVert(const T& val, const Args&... remainder) {
this->writeQuadValue<corner>(val);
this->writeQuadVert<corner>(remainder...);
}
template <int corner>
void writeQuadVert() {}
template <int corner, typename T>
void writeQuadValue(const T& val) {
this->write(val);
}
template <int corner, typename T>
void writeQuadValue(const TriStrip<T>& r) {
switch (corner) {
case 0: this->write(r.l, r.t); break;
case 1: this->write(r.l, r.b); break;
case 2: this->write(r.r, r.t); break;
case 3: this->write(r.r, r.b); break;
}
}
template <int corner, typename T>
void writeQuadValue(const TriFan<T>& r) {
switch (corner) {
case 0: this->write(r.l, r.t); break;
case 1: this->write(r.l, r.b); break;
case 2: this->write(r.r, r.b); break;
case 3: this->write(r.r, r.t); break;
}
}
template <int corner>
void writeQuadValue(const GrQuad& q) {
this->write(q.point(corner));
}
};
#endif