clang-format updates

- Put braces on their own line
- Turn off single-line case labels

Diffs=
32e79999a clang-format updates
This commit is contained in:
csmartdalton
2022-10-03 20:23:45 +00:00
parent 23fecfb953
commit 9c3168ef9d
628 changed files with 10396 additions and 5021 deletions

View File

@@ -3,9 +3,11 @@
#include <cstddef>
namespace rive {
namespace rive
{
class Mat2D;
class Mat4 {
class Mat4
{
private:
float m_Buffer[16];
@@ -18,7 +20,8 @@ public:
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
// clang-format on
} {}
}
{}
Mat4(const Mat4& copy) = default;
// Construct a 4x4 Matrix with the provided elements stored in row-major
@@ -38,7 +41,8 @@ public:
x3, y3, z3, w3,
tx, ty, tz, tw,
// clang-format on
} {}
}
{}
Mat4(const Mat2D& mat2d);
@@ -47,12 +51,14 @@ public:
float& operator[](std::size_t idx) { return m_Buffer[idx]; }
const float& operator[](std::size_t idx) const { return m_Buffer[idx]; }
Mat4& operator*=(const Mat4& rhs) {
Mat4& operator*=(const Mat4& rhs)
{
*this = Mat4::multiply(*this, rhs);
return *this;
}
Mat4& operator*=(const Mat2D& rhs) {
Mat4& operator*=(const Mat2D& rhs)
{
*this = Mat4::multiply(*this, rhs);
return *this;
}

View File

@@ -7,13 +7,15 @@
#include <vector>
#include <cstdint>
namespace rive {
namespace rive
{
class SegmentedContour;
///
/// Builds a triangle strip vertex buffer from a SegmentedContour.
///
class ContourStroke {
class ContourStroke
{
protected:
std::vector<Vec2D> m_TriangleStrip;
std::vector<std::size_t> m_Offsets;

View File

@@ -6,11 +6,13 @@
#include "rive/math/aabb.hpp"
#include <vector>
namespace rive {
namespace rive
{
class RawPath;
/// Utilty for converting a RawPath into a contour segments.
class SegmentedContour {
class SegmentedContour
{
private:
std::vector<Vec2D> m_contourPoints;

View File

@@ -7,9 +7,11 @@
#include "rive/factory.hpp"
namespace rive {
namespace rive
{
class SokolFactory : public Factory {
class SokolFactory : public Factory
{
public:
SokolFactory();

View File

@@ -8,10 +8,12 @@
#include "rive/tess/tess_renderer.hpp"
#include "sokol_gfx.h"
namespace rive {
namespace rive
{
// The actual graphics device image.
class SokolRenderImageResource : public RefCnt<SokolRenderImageResource> {
class SokolRenderImageResource : public RefCnt<SokolRenderImageResource>
{
private:
sg_image m_gpuResource;
@@ -25,7 +27,8 @@ public:
// The unique render image associated with a given source Rive asset. Can be stored in sub-region of
// an actual graphics device image (SokolRenderImageResource).
class SokolRenderImage : public RenderImage {
class SokolRenderImage : public RenderImage
{
private:
rcp<SokolRenderImageResource> m_gpuImage;
sg_buffer m_vertexBuffer;
@@ -47,7 +50,8 @@ public:
sg_buffer uvBuffer() const { return m_uvBuffer; }
};
class SokolTessRenderer : public TessRenderer {
class SokolTessRenderer : public TessRenderer
{
private:
static const std::size_t maxClippingPaths = 16;
sg_pipeline m_meshPipeline;

View File

@@ -4,12 +4,14 @@
#include "rive/renderer.hpp"
#include "rive/math/mat2d.hpp"
namespace rive {
namespace rive
{
///
/// A reference to a sub-path added to a TessRenderPath with its relative
/// transform.
///
class SubPath {
class SubPath
{
private:
RenderPath* m_Path;
Mat2D m_Transform;

View File

@@ -8,10 +8,12 @@
#include "rive/tess/sub_path.hpp"
#include "earcut.hpp"
namespace rive {
namespace rive
{
class ContourStroke;
class TessRenderPath : public RenderPath {
class TessRenderPath : public RenderPath
{
private:
// TessRenderPath stores a RawPath so that it can use utility classes
// that will work off of RawPath (like segmenting the contour and then

View File

@@ -12,14 +12,17 @@
#include <vector>
#include <list>
namespace rive {
namespace rive
{
struct RenderState {
struct RenderState
{
Mat2D transform;
std::vector<SubPath> clipPaths;
};
class TessRenderer : public Renderer {
class TessRenderer : public Renderer
{
protected:
Mat4 m_Projection;
std::list<RenderState> m_Stack;

View File

@@ -9,15 +9,18 @@ using namespace rive;
static const int subdivisionArcLength = 4.0f;
void ContourStroke::reset() {
void ContourStroke::reset()
{
m_TriangleStrip.clear();
m_Offsets.clear();
}
void ContourStroke::resetRenderOffset() { m_RenderOffset = 0; }
bool ContourStroke::nextRenderOffset(std::size_t& start, std::size_t& end) {
if (m_RenderOffset == m_Offsets.size()) {
bool ContourStroke::nextRenderOffset(std::size_t& start, std::size_t& end)
{
if (m_RenderOffset == m_Offsets.size())
{
return false;
}
start = m_RenderOffset == 0 ? 0 : m_Offsets[m_RenderOffset - 1];
@@ -29,12 +32,14 @@ void ContourStroke::extrude(const SegmentedContour* contour,
bool isClosed,
StrokeJoin join,
StrokeCap cap,
float strokeWidth) {
float strokeWidth)
{
auto contourPoints = contour->contourPoints();
std::vector<Vec2D> points(contourPoints.begin(), contourPoints.end());
auto pointCount = points.size();
if (pointCount < 2) {
if (pointCount < 2)
{
return;
}
auto startOffset = m_TriangleStrip.size();
@@ -49,9 +54,12 @@ void ContourStroke::extrude(const SegmentedContour* contour,
Vec2D lastA = lastPoint + perpendicularStrokeDiff;
Vec2D lastB = lastPoint - perpendicularStrokeDiff;
if (!isClosed) {
switch (cap) {
case StrokeCap::square: {
if (!isClosed)
{
switch (cap)
{
case StrokeCap::square:
{
Vec2D strokeDiff = lastDiffNormalized * strokeWidth;
Vec2D squareA = lastA - strokeDiff;
Vec2D squareB = lastB - strokeDiff;
@@ -59,7 +67,8 @@ void ContourStroke::extrude(const SegmentedContour* contour,
m_TriangleStrip.push_back(squareB);
break;
}
case StrokeCap::round: {
case StrokeCap::round:
{
Vec2D capDirection = Vec2D(-lastDiffNormalized.y, lastDiffNormalized.x);
float arcLength = std::abs(math::PI * strokeWidth);
int steps = (int)std::ceil(arcLength / subdivisionArcLength);
@@ -67,7 +76,8 @@ void ContourStroke::extrude(const SegmentedContour* contour,
float inc = math::PI / steps;
float angle = angleTo;
// make sure to draw the full cap due triangle strip
for (int j = 0; j <= steps; j++) {
for (int j = 0; j <= steps; j++)
{
m_TriangleStrip.push_back(lastPoint);
m_TriangleStrip.push_back(Vec2D(lastPoint.x + std::cos(angle) * strokeWidth,
lastPoint.y + std::sin(angle) * strokeWidth));
@@ -75,7 +85,8 @@ void ContourStroke::extrude(const SegmentedContour* contour,
}
break;
}
default: break;
default:
break;
}
}
m_TriangleStrip.push_back(lastA);
@@ -84,15 +95,19 @@ void ContourStroke::extrude(const SegmentedContour* contour,
pointCount -= isClosed ? 1 : 0;
std::size_t adjustedPointCount = isClosed ? pointCount + 1 : pointCount;
for (std::size_t i = 1; i < adjustedPointCount; i++) {
for (std::size_t i = 1; i < adjustedPointCount; i++)
{
const Vec2D& point = points[i % pointCount];
Vec2D diff, diffNormalized, next;
float length;
if (i < adjustedPointCount - 1 || isClosed) {
if (i < adjustedPointCount - 1 || isClosed)
{
diff = (next = points[(i + 1) % pointCount]) - point;
length = diff.length();
diffNormalized = diff / length;
} else {
}
else
{
diff = lastDiff;
next = point;
length = lastLength;
@@ -116,26 +131,34 @@ void ContourStroke::extrude(const SegmentedContour* contour,
bool bevel = join == StrokeJoin::miter ? dot < 0.1f : dot < 0.999f;
// Scale bisector to match stroke size.
if (dot > 0.000001f) {
if (dot > 0.000001f)
{
float scale = 1.0f / dot * strokeWidth;
float limit = lengthLimit / strokeWidth;
if (dot * limit * limit < 1.0f) {
if (dot * limit * limit < 1.0f)
{
bevelInner = true;
}
bisector *= scale;
} else {
}
else
{
bisector *= strokeWidth;
}
if (!bevel) {
if (!bevel)
{
Vec2D c = point + bisector;
Vec2D d = point - bisector;
if (!bevelInner) {
if (!bevelInner)
{
// Normal mitered edge.
m_TriangleStrip.push_back(c);
m_TriangleStrip.push_back(d);
} else if (cross <= 0) {
}
else if (cross <= 0)
{
// Overlap the inner (in this case right) edge (sometimes called
// miter inner).
Vec2D c1 = point + Vec2D(lastDiffNormalized.y * -strokeWidth,
@@ -147,7 +170,9 @@ void ContourStroke::extrude(const SegmentedContour* contour,
m_TriangleStrip.push_back(d);
m_TriangleStrip.push_back(c2);
m_TriangleStrip.push_back(d);
} else {
}
else
{
// Overlap the inner (in this case left) edge (sometimes called
// miter inner).
Vec2D d1 = point - Vec2D(lastDiffNormalized.y * -strokeWidth,
@@ -160,19 +185,25 @@ void ContourStroke::extrude(const SegmentedContour* contour,
m_TriangleStrip.push_back(c);
m_TriangleStrip.push_back(d2);
}
} else {
}
else
{
Vec2D ldPStroke =
Vec2D(lastDiffNormalized.y * -strokeWidth, lastDiffNormalized.x * strokeWidth);
Vec2D dPStroke = Vec2D(diffNormalized.y * -strokeWidth, diffNormalized.x * strokeWidth);
if (cross <= 0) {
if (cross <= 0)
{
// Bevel the outer (left in this case) edge.
Vec2D a1;
Vec2D a2;
if (bevelInner) {
if (bevelInner)
{
a1 = point + ldPStroke;
a2 = point + dPStroke;
} else {
}
else
{
a1 = point + bisector;
a2 = a1;
}
@@ -182,13 +213,15 @@ void ContourStroke::extrude(const SegmentedContour* contour,
m_TriangleStrip.push_back(a1);
m_TriangleStrip.push_back(b);
if (join == StrokeJoin::round) {
if (join == StrokeJoin::round)
{
const Vec2D& pivot = bevelInner ? point : a1;
Vec2D toPrev = bn - point;
Vec2D toNext = b - point;
float angleFrom = std::atan2(toPrev.y, toPrev.x);
float angleTo = std::atan2(toNext.y, toNext.x);
if (angleTo > angleFrom) {
if (angleTo > angleFrom)
{
angleTo -= math::PI * 2.0f;
}
float range = angleTo - angleFrom;
@@ -197,7 +230,8 @@ void ContourStroke::extrude(const SegmentedContour* contour,
float inc = range / steps;
float angle = angleTo - inc;
for (int j = 0; j < steps - 1; j++) {
for (int j = 0; j < steps - 1; j++)
{
m_TriangleStrip.push_back(pivot);
m_TriangleStrip.emplace_back(
Vec2D(point.x + std::cos(angle) * strokeWidth,
@@ -208,14 +242,19 @@ void ContourStroke::extrude(const SegmentedContour* contour,
}
m_TriangleStrip.push_back(a2);
m_TriangleStrip.push_back(bn);
} else {
}
else
{
// Bevel the outer (right in this case) edge.
Vec2D b1;
Vec2D b2;
if (bevelInner) {
if (bevelInner)
{
b1 = point - ldPStroke;
b2 = point - dPStroke;
} else {
}
else
{
b1 = point - bisector;
b2 = b1;
}
@@ -226,13 +265,15 @@ void ContourStroke::extrude(const SegmentedContour* contour,
m_TriangleStrip.push_back(a);
m_TriangleStrip.push_back(b1);
if (join == StrokeJoin::round) {
if (join == StrokeJoin::round)
{
const Vec2D& pivot = bevelInner ? point : b1;
Vec2D toPrev = a - point;
Vec2D toNext = an - point;
float angleFrom = std::atan2(toPrev.y, toPrev.x);
float angleTo = std::atan2(toNext.y, toNext.x);
if (angleTo > angleFrom) {
if (angleTo > angleFrom)
{
angleTo -= math::PI * 2.0f;
}
@@ -242,7 +283,8 @@ void ContourStroke::extrude(const SegmentedContour* contour,
float inc = range / steps;
float angle = angleFrom + inc;
for (int j = 0; j < steps - 1; j++) {
for (int j = 0; j < steps - 1; j++)
{
m_TriangleStrip.emplace_back(
Vec2D(point.x + std::cos(angle) * strokeWidth,
point.y + std::sin(angle) * strokeWidth));
@@ -260,13 +302,18 @@ void ContourStroke::extrude(const SegmentedContour* contour,
lastDiffNormalized = diffNormalized;
}
if (isClosed) {
if (isClosed)
{
auto last = m_TriangleStrip.size() - 1;
m_TriangleStrip[startOffset] = m_TriangleStrip[last - 1];
m_TriangleStrip[startOffset + 1] = m_TriangleStrip[last];
} else {
switch (cap) {
case StrokeCap::square: {
}
else
{
switch (cap)
{
case StrokeCap::square:
{
auto l = m_TriangleStrip.size();
Vec2D strokeDiff = lastDiffNormalized * strokeWidth;
@@ -277,7 +324,8 @@ void ContourStroke::extrude(const SegmentedContour* contour,
m_TriangleStrip.push_back(squareB);
break;
}
case StrokeCap::round: {
case StrokeCap::round:
{
Vec2D capDirection = Vec2D(-lastDiffNormalized.y, lastDiffNormalized.x);
float arcLength = std::abs(math::PI * strokeWidth);
int steps = (int)std::ceil(arcLength / subdivisionArcLength);
@@ -285,7 +333,8 @@ void ContourStroke::extrude(const SegmentedContour* contour,
float inc = math::PI / steps;
float angle = angleTo;
// make sure to draw the full cap due triangle strip
for (int j = 0; j <= steps; j++) {
for (int j = 0; j <= steps; j++)
{
m_TriangleStrip.push_back(lastPoint);
m_TriangleStrip.push_back(Vec2D(lastPoint.x + std::cos(angle) * strokeWidth,
lastPoint.y + std::sin(angle) * strokeWidth));
@@ -293,7 +342,8 @@ void ContourStroke::extrude(const SegmentedContour* contour,
}
break;
}
default: break;
default:
break;
}
}

View File

@@ -12,9 +12,11 @@ Mat4::Mat4(const Mat2D& mat2d) :
0.0f, 0.0f, 1.0f, 0.0f,
mat2d[4], mat2d[5], 0.0f, 1.0f,
// clang-format on
} {}
}
{}
Mat4 Mat4::multiply(const Mat4& a, const Mat4& b) {
Mat4 Mat4::multiply(const Mat4& a, const Mat4& b)
{
return {
// clang-format off
b[0] * a[0] + b[1] * a[4] + b[2] * a[8] + b[3] * a[12],
@@ -40,7 +42,8 @@ Mat4 Mat4::multiply(const Mat4& a, const Mat4& b) {
};
}
Mat4 Mat4::multiply(const Mat4& a, const Mat2D& b) {
Mat4 Mat4::multiply(const Mat4& a, const Mat2D& b)
{
return {
// clang-format off
b[0] * a[0] + b[1] * a[4],

View File

@@ -7,22 +7,26 @@ using namespace rive;
SegmentedContour::SegmentedContour(float threshold) :
m_bounds(AABB::forExpansion()),
m_threshold(threshold),
m_thresholdSquared(threshold * threshold) {}
m_thresholdSquared(threshold * threshold)
{}
float SegmentedContour::threshold() const { return m_threshold; }
void SegmentedContour::threshold(float value) {
void SegmentedContour::threshold(float value)
{
m_threshold = value;
m_thresholdSquared = value * value;
}
const AABB& SegmentedContour::bounds() const { return m_bounds; }
void SegmentedContour::addVertex(Vec2D vertex) {
void SegmentedContour::addVertex(Vec2D vertex)
{
m_contourPoints.push_back(vertex);
AABB::expandTo(m_bounds, vertex);
}
const std::size_t SegmentedContour::contourSize() const { return m_contourPoints.size(); }
const Span<const Vec2D> SegmentedContour::contourPoints(uint32_t endOffset) const {
const Span<const Vec2D> SegmentedContour::contourPoints(uint32_t endOffset) const
{
assert(endOffset <= m_contourPoints.size());
return Span<const Vec2D>(m_contourPoints.data(), m_contourPoints.size() - endOffset);
}
@@ -32,8 +36,10 @@ void SegmentedContour::segmentCubic(const Vec2D& from,
const Vec2D& toIn,
const Vec2D& to,
float t1,
float t2) {
if (CubicUtilities::shouldSplitCubic(from, fromOut, toIn, to, m_threshold)) {
float t2)
{
if (CubicUtilities::shouldSplitCubic(from, fromOut, toIn, to, m_threshold))
{
float halfT = (t1 + t2) / 2.0f;
Vec2D hull[6];
@@ -42,23 +48,33 @@ void SegmentedContour::segmentCubic(const Vec2D& from,
segmentCubic(from, hull[0], hull[3], hull[5], t1, halfT);
segmentCubic(hull[5], hull[4], hull[2], to, halfT, t2);
} else {
if (Vec2D::distanceSquared(from, to) > m_thresholdSquared) {
}
else
{
if (Vec2D::distanceSquared(from, to) > m_thresholdSquared)
{
addVertex(Vec2D(CubicUtilities::cubicAt(t2, from.x, fromOut.x, toIn.x, to.x),
CubicUtilities::cubicAt(t2, from.y, fromOut.y, toIn.y, to.y)));
}
}
}
void SegmentedContour::contour(const RawPath& rawPath, const Mat2D& transform) {
void SegmentedContour::contour(const RawPath& rawPath, const Mat2D& transform)
{
m_contourPoints.clear();
// Possible perf consideration: could add second path that doesn't transform
// if transform is the identity.
for (const auto [verb, pts] : rawPath) {
switch (verb) {
case PathVerb::move: addVertex(transform * pts[0]); break;
case PathVerb::line: addVertex(transform * pts[1]); break;
for (const auto [verb, pts] : rawPath)
{
switch (verb)
{
case PathVerb::move:
addVertex(transform * pts[0]);
break;
case PathVerb::line:
addVertex(transform * pts[1]);
break;
case PathVerb::cubic:
segmentCubic(transform * pts[0],
transform * pts[1],
@@ -67,7 +83,8 @@ void SegmentedContour::contour(const RawPath& rawPath, const Mat2D& transform) {
0.0f,
1.0f);
break;
case PathVerb::close: break;
case PathVerb::close:
break;
case PathVerb::quad:
// TODO: not currently used by render paths, however might be
// necessary for fonts.

View File

@@ -2,7 +2,8 @@
using namespace rive;
class NoOpRenderPaint : public RenderPaint {
class NoOpRenderPaint : public RenderPaint
{
public:
void color(unsigned int value) override {}
void style(RenderPaintStyle value) override {}
@@ -14,7 +15,8 @@ public:
void invalidateStroke() override {}
};
class NoOpRenderPath : public RenderPath {
class NoOpRenderPath : public RenderPath
{
public:
void reset() override {}

View File

@@ -7,19 +7,22 @@
using namespace rive;
static void fillColorBuffer(float* buffer, ColorInt value) {
static void fillColorBuffer(float* buffer, ColorInt value)
{
buffer[0] = (float)colorRed(value) / 0xFF;
buffer[1] = (float)colorGreen(value) / 0xFF;
buffer[2] = (float)colorBlue(value) / 0xFF;
buffer[3] = colorOpacity(value);
}
class SokolRenderPath : public TessRenderPath {
class SokolRenderPath : public TessRenderPath
{
public:
SokolRenderPath() {}
SokolRenderPath(RawPath& rawPath, FillRule fillRule) : TessRenderPath(rawPath, fillRule) {}
~SokolRenderPath() {
~SokolRenderPath()
{
sg_destroy_buffer(m_vertexBuffer);
sg_destroy_buffer(m_indexBuffer);
}
@@ -34,12 +37,14 @@ private:
std::size_t m_boundsIndex = 0;
protected:
void addTriangles(rive::Span<const rive::Vec2D> vts, rive::Span<const uint16_t> idx) override {
void addTriangles(rive::Span<const rive::Vec2D> vts, rive::Span<const uint16_t> idx) override
{
m_vertices.insert(m_vertices.end(), vts.begin(), vts.end());
m_indices.insert(m_indices.end(), idx.begin(), idx.end());
}
void setTriangulatedBounds(const AABB& value) override {
void setTriangulatedBounds(const AABB& value) override
{
m_boundsIndex = m_vertices.size();
m_vertices.emplace_back(Vec2D(value.minX, value.minY));
m_vertices.emplace_back(Vec2D(value.maxX, value.minY));
@@ -48,15 +53,19 @@ protected:
}
public:
void reset() override {
void reset() override
{
TessRenderPath::reset();
m_vertices.clear();
m_indices.clear();
}
void drawStroke(ContourStroke* stroke) {
if (isContainer()) {
for (auto& subPath : m_subPaths) {
void drawStroke(ContourStroke* stroke)
{
if (isContainer())
{
for (auto& subPath : m_subPaths)
{
reinterpret_cast<SokolRenderPath*>(subPath.path())->drawStroke(stroke);
}
return;
@@ -66,11 +75,14 @@ public:
sg_draw(start < 2 ? 0 : (start - 2) * 3, end - start < 2 ? 0 : (end - start - 2) * 3, 1);
}
void drawFill() {
if (triangulate()) {
void drawFill()
{
if (triangulate())
{
sg_destroy_buffer(m_vertexBuffer);
sg_destroy_buffer(m_indexBuffer);
if (m_indices.size() == 0 || m_vertices.size() == 0) {
if (m_indices.size() == 0 || m_vertices.size() == 0)
{
m_vertexBuffer = {0};
m_indexBuffer = {0};
return;
@@ -95,7 +107,8 @@ public:
});
}
if (m_vertexBuffer.id == 0) {
if (m_vertexBuffer.id == 0)
{
return;
}
@@ -108,8 +121,10 @@ public:
sg_draw(0, m_indices.size(), 1);
}
void drawBounds(const sg_buffer& boundsIndexBuffer) {
if (m_vertexBuffer.id == 0) {
void drawBounds(const sg_buffer& boundsIndexBuffer)
{
if (m_vertexBuffer.id == 0)
{
return;
}
sg_bindings bind = {
@@ -124,27 +139,32 @@ public:
};
// Returns a full-formed RenderPath -- can be treated as immutable
std::unique_ptr<RenderPath> SokolFactory::makeRenderPath(RawPath& rawPath, FillRule rule) {
std::unique_ptr<RenderPath> SokolFactory::makeRenderPath(RawPath& rawPath, FillRule rule)
{
return std::make_unique<SokolRenderPath>(rawPath, rule);
}
std::unique_ptr<RenderPath> SokolFactory::makeEmptyRenderPath() {
std::unique_ptr<RenderPath> SokolFactory::makeEmptyRenderPath()
{
return std::make_unique<SokolRenderPath>();
}
class SokolBuffer : public RenderBuffer {
class SokolBuffer : public RenderBuffer
{
private:
sg_buffer m_Buffer;
public:
SokolBuffer(size_t count, const sg_buffer_desc& desc) :
RenderBuffer(count), m_Buffer(sg_make_buffer(desc)) {}
RenderBuffer(count), m_Buffer(sg_make_buffer(desc))
{}
~SokolBuffer() { sg_destroy_buffer(m_Buffer); }
sg_buffer buffer() { return m_Buffer; }
};
rcp<RenderBuffer> SokolFactory::makeBufferU16(Span<const uint16_t> span) {
rcp<RenderBuffer> SokolFactory::makeBufferU16(Span<const uint16_t> span)
{
return rcp<RenderBuffer>(new SokolBuffer(span.size(),
(sg_buffer_desc){
.type = SG_BUFFERTYPE_INDEXBUFFER,
@@ -156,7 +176,8 @@ rcp<RenderBuffer> SokolFactory::makeBufferU16(Span<const uint16_t> span) {
}));
}
rcp<RenderBuffer> SokolFactory::makeBufferU32(Span<const uint32_t> span) {
rcp<RenderBuffer> SokolFactory::makeBufferU32(Span<const uint32_t> span)
{
return rcp<RenderBuffer>(new SokolBuffer(span.size(),
(sg_buffer_desc){
.type = SG_BUFFERTYPE_INDEXBUFFER,
@@ -167,7 +188,8 @@ rcp<RenderBuffer> SokolFactory::makeBufferU32(Span<const uint32_t> span) {
},
}));
}
rcp<RenderBuffer> SokolFactory::makeBufferF32(Span<const float> span) {
rcp<RenderBuffer> SokolFactory::makeBufferF32(Span<const float> span)
{
return rcp<RenderBuffer>(new SokolBuffer(span.size(),
(sg_buffer_desc){
.type = SG_BUFFERTYPE_VERTEXBUFFER,
@@ -182,7 +204,8 @@ rcp<RenderBuffer> SokolFactory::makeBufferF32(Span<const float> span) {
sg_pipeline vectorPipeline(sg_shader shader,
sg_blend_state blend,
sg_stencil_state stencil,
sg_color_mask colorMask = SG_COLORMASK_RGBA) {
sg_color_mask colorMask = SG_COLORMASK_RGBA)
{
return sg_make_pipeline((sg_pipeline_desc){
.layout =
{
@@ -216,7 +239,8 @@ sg_pipeline vectorPipeline(sg_shader shader,
});
}
SokolTessRenderer::SokolTessRenderer() {
SokolTessRenderer::SokolTessRenderer()
{
m_meshPipeline = sg_make_pipeline((sg_pipeline_desc){
.layout =
{
@@ -266,7 +290,8 @@ SokolTessRenderer::SokolTessRenderer() {
.enabled = false,
});
for (std::size_t i = 1; i <= maxClippingPaths; i++) {
for (std::size_t i = 1; i <= maxClippingPaths; i++)
{
m_pathPipeline[i] =
vectorPipeline(uberShader,
{
@@ -306,7 +331,8 @@ SokolTessRenderer::SokolTessRenderer() {
.enabled = false,
});
for (std::size_t i = 1; i <= maxClippingPaths; i++) {
for (std::size_t i = 1; i <= maxClippingPaths; i++)
{
m_pathScreenPipeline[i] =
vectorPipeline(uberShader,
{
@@ -345,7 +371,8 @@ SokolTessRenderer::SokolTessRenderer() {
.enabled = false,
});
for (std::size_t i = 1; i <= maxClippingPaths; i++) {
for (std::size_t i = 1; i <= maxClippingPaths; i++)
{
m_pathAdditivePipeline[i] =
vectorPipeline(uberShader,
{
@@ -383,7 +410,8 @@ SokolTessRenderer::SokolTessRenderer() {
.enabled = false,
});
for (std::size_t i = 1; i <= maxClippingPaths; i++) {
for (std::size_t i = 1; i <= maxClippingPaths; i++)
{
m_pathMultiplyPipeline[i] =
vectorPipeline(uberShader,
{
@@ -466,12 +494,14 @@ SokolTessRenderer::SokolTessRenderer() {
});
}
SokolTessRenderer::~SokolTessRenderer() {
SokolTessRenderer::~SokolTessRenderer()
{
sg_destroy_buffer(m_boundsIndices);
sg_destroy_pipeline(m_meshPipeline);
sg_destroy_pipeline(m_incClipPipeline);
sg_destroy_pipeline(m_decClipPipeline);
for (std::size_t i = 0; i <= maxClippingPaths; i++) {
for (std::size_t i = 0; i <= maxClippingPaths; i++)
{
sg_destroy_pipeline(m_pathPipeline[i]);
sg_destroy_pipeline(m_pathScreenPipeline[i]);
}
@@ -482,7 +512,8 @@ void SokolTessRenderer::orthographicProjection(float left,
float bottom,
float top,
float near,
float far) {
float far)
{
m_Projection[0] = 2.0f / (right - left);
m_Projection[1] = 0.0f;
m_Projection[2] = 0.0f;
@@ -526,7 +557,8 @@ void SokolTessRenderer::orthographicProjection(float left,
// }
}
void SokolTessRenderer::drawImage(const RenderImage* image, BlendMode, float opacity) {
void SokolTessRenderer::drawImage(const RenderImage* image, BlendMode, float opacity)
{
vs_params_t vs_params;
const Mat2D& world = transform();
@@ -551,7 +583,8 @@ void SokolTessRenderer::drawImageMesh(const RenderImage* renderImage,
rcp<RenderBuffer> uvCoords_f32,
rcp<RenderBuffer> indices_u16,
BlendMode blendMode,
float opacity) {
float opacity)
{
vs_params_t vs_params;
const Mat2D& world = transform();
@@ -570,7 +603,8 @@ void SokolTessRenderer::drawImageMesh(const RenderImage* renderImage,
sg_draw(0, indices_u16->count(), 1);
}
class SokolGradient : public RenderShader {
class SokolGradient : public RenderShader
{
private:
Vec2D m_start;
Vec2D m_end;
@@ -582,13 +616,16 @@ private:
private:
// General gradient
SokolGradient(int type, const ColorInt colors[], const float stops[], size_t count) :
m_type(type) {
m_type(type)
{
m_stops.resize(count);
m_colors.resize(count * 4);
for (int i = 0, colorIndex = 0; i < count; i++, colorIndex += 4) {
for (int i = 0, colorIndex = 0; i < count; i++, colorIndex += 4)
{
fillColorBuffer(&m_colors[colorIndex], colors[i]);
m_stops[i] = stops[i];
if (m_colors[colorIndex + 3] > 0.0f) {
if (m_colors[colorIndex + 3] > 0.0f)
{
m_isVisible = true;
}
}
@@ -603,7 +640,8 @@ public:
const ColorInt colors[],
const float stops[],
size_t count) :
SokolGradient(1, colors, stops, count) {
SokolGradient(1, colors, stops, count)
{
m_start = Vec2D(sx, sy);
m_end = Vec2D(ex, ey);
}
@@ -614,20 +652,24 @@ public:
const ColorInt colors[], // [count]
const float stops[], // [count]
size_t count) :
SokolGradient(2, colors, stops, count) {
SokolGradient(2, colors, stops, count)
{
m_start = Vec2D(cx, cy);
m_end = Vec2D(cx + radius, cy);
}
void bind(vs_path_params_t& vertexUniforms, fs_path_uniforms_t& fragmentUniforms) {
void bind(vs_path_params_t& vertexUniforms, fs_path_uniforms_t& fragmentUniforms)
{
auto stopCount = m_stops.size();
vertexUniforms.fillType = fragmentUniforms.fillType = m_type;
vertexUniforms.gradientStart = m_start;
vertexUniforms.gradientEnd = m_end;
fragmentUniforms.stopCount = stopCount;
for (int i = 0; i < stopCount; i++) {
for (int i = 0; i < stopCount; i++)
{
auto colorBufferIndex = i * 4;
for (int j = 0; j < 4; j++) {
for (int j = 0; j < 4; j++)
{
fragmentUniforms.colors[i][j] = m_colors[colorBufferIndex + j];
}
fragmentUniforms.stops[i / 4][i % 4] = m_stops[i];
@@ -641,7 +683,8 @@ rcp<RenderShader> SokolFactory::makeLinearGradient(float sx,
float ey,
const ColorInt colors[],
const float stops[],
size_t count) {
size_t count)
{
return rcp<RenderShader>(new SokolGradient(sx, sy, ex, ey, colors, stops, count));
}
@@ -650,11 +693,13 @@ rcp<RenderShader> SokolFactory::makeRadialGradient(float cx,
float radius,
const ColorInt colors[], // [count]
const float stops[], // [count]
size_t count) {
size_t count)
{
return rcp<RenderShader>(new SokolGradient(cx, cy, radius, colors, stops, count));
}
class SokolRenderPaint : public RenderPaint {
class SokolRenderPaint : public RenderPaint
{
private:
fs_path_uniforms_t m_uniforms = {0};
rcp<RenderShader> m_shader;
@@ -672,20 +717,24 @@ private:
BlendMode m_blendMode = BlendMode::srcOver;
public:
~SokolRenderPaint() override {
~SokolRenderPaint() override
{
sg_destroy_buffer(m_strokeVertexBuffer);
sg_destroy_buffer(m_strokeIndexBuffer);
}
void color(ColorInt value) override {
void color(ColorInt value) override
{
fillColorBuffer(m_uniforms.colors[0], value);
m_uniforms.fillType = 0;
}
void style(RenderPaintStyle value) override {
void style(RenderPaintStyle value) override
{
m_style = value;
switch (m_style) {
switch (m_style)
{
case RenderPaintStyle::fill:
m_stroke = nullptr;
m_strokeDirty = false;
@@ -699,23 +748,28 @@ public:
RenderPaintStyle style() const { return m_style; }
void thickness(float value) override {
void thickness(float value) override
{
m_strokeThickness = value;
m_strokeDirty = true;
}
void join(StrokeJoin value) override {
void join(StrokeJoin value) override
{
m_strokeJoin = value;
m_strokeDirty = true;
}
void cap(StrokeCap value) override {
void cap(StrokeCap value) override
{
m_strokeCap = value;
m_strokeDirty = true;
}
void invalidateStroke() override {
if (m_stroke) {
void invalidateStroke() override
{
if (m_stroke)
{
m_strokeDirty = true;
}
}
@@ -725,15 +779,19 @@ public:
void shader(rcp<RenderShader> shader) override { m_shader = shader; }
void draw(vs_path_params_t& vertexUniforms, SokolRenderPath* path) {
if (m_shader) {
void draw(vs_path_params_t& vertexUniforms, SokolRenderPath* path)
{
if (m_shader)
{
static_cast<SokolGradient*>(m_shader.get())->bind(vertexUniforms, m_uniforms);
}
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_vs_path_params, SG_RANGE_REF(vertexUniforms));
sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_fs_path_uniforms, SG_RANGE_REF(m_uniforms));
if (m_stroke != nullptr) {
if (m_strokeDirty) {
if (m_stroke != nullptr)
{
if (m_strokeDirty)
{
static Mat2D identity;
m_stroke->reset();
path->extrudeStroke(m_stroke.get(),
@@ -748,7 +806,8 @@ public:
sg_destroy_buffer(m_strokeVertexBuffer);
sg_destroy_buffer(m_strokeIndexBuffer);
auto size = strip.size();
if (size <= 2) {
if (size <= 2)
{
m_strokeVertexBuffer = {0};
m_strokeIndexBuffer = {0};
return;
@@ -770,19 +829,26 @@ public:
// to)
m_stroke->resetRenderOffset();
m_StrokeOffsets.clear();
while (true) {
while (true)
{
std::size_t strokeStart, strokeEnd;
if (!m_stroke->nextRenderOffset(strokeStart, strokeEnd)) {
if (!m_stroke->nextRenderOffset(strokeStart, strokeEnd))
{
break;
}
std::size_t length = strokeEnd - strokeStart;
if (length > 2) {
for (std::size_t i = 0, end = length - 2; i < end; i++) {
if ((i % 2) == 1) {
if (length > 2)
{
for (std::size_t i = 0, end = length - 2; i < end; i++)
{
if ((i % 2) == 1)
{
indices.push_back(i + strokeStart);
indices.push_back(i + 1 + strokeStart);
indices.push_back(i + 2 + strokeStart);
} else {
}
else
{
indices.push_back(i + strokeStart);
indices.push_back(i + 2 + strokeStart);
indices.push_back(i + 1 + strokeStart);
@@ -801,7 +867,8 @@ public:
},
});
}
if (m_strokeVertexBuffer.id == 0) {
if (m_strokeVertexBuffer.id == 0)
{
return;
}
@@ -815,48 +882,59 @@ public:
m_stroke->resetRenderOffset();
// path->drawStroke(m_stroke.get());
std::size_t start = 0;
for (auto end : m_StrokeOffsets) {
for (auto end : m_StrokeOffsets)
{
sg_draw(start, end - start, 1);
start = end;
}
} else {
}
else
{
path->drawFill();
}
}
};
std::unique_ptr<RenderPaint> SokolFactory::makeRenderPaint() {
std::unique_ptr<RenderPaint> SokolFactory::makeRenderPaint()
{
return std::make_unique<SokolRenderPaint>();
}
void SokolTessRenderer::restore() {
void SokolTessRenderer::restore()
{
TessRenderer::restore();
if (m_Stack.size() == 1) {
if (m_Stack.size() == 1)
{
// When we've fully restored, immediately update clip to not wait for next draw.
applyClipping();
m_currentPipeline = {0};
}
}
void SokolTessRenderer::applyClipping() {
if (!m_IsClippingDirty) {
void SokolTessRenderer::applyClipping()
{
if (!m_IsClippingDirty)
{
return;
}
m_IsClippingDirty = false;
RenderState& state = m_Stack.back();
auto currentClipLength = m_ClipPaths.size();
if (currentClipLength == state.clipPaths.size()) {
if (currentClipLength == state.clipPaths.size())
{
// Same length so now check if they're all the same.
bool allSame = true;
for (std::size_t i = 0; i < currentClipLength; i++) {
if (state.clipPaths[i].path() != m_ClipPaths[i].path()) {
for (std::size_t i = 0; i < currentClipLength; i++)
{
if (state.clipPaths[i].path() != m_ClipPaths[i].path())
{
allSame = false;
break;
}
}
if (allSame) {
if (allSame)
{
return;
}
}
@@ -867,16 +945,20 @@ void SokolTessRenderer::applyClipping() {
// Decr any paths from the last clip that are gone.
std::unordered_set<RenderPath*> alreadyApplied;
for (auto appliedPath : m_ClipPaths) {
for (auto appliedPath : m_ClipPaths)
{
bool decr = true;
for (auto nextClipPath : state.clipPaths) {
if (nextClipPath.path() == appliedPath.path()) {
for (auto nextClipPath : state.clipPaths)
{
if (nextClipPath.path() == appliedPath.path())
{
decr = false;
alreadyApplied.insert(appliedPath.path());
break;
}
}
if (decr) {
if (decr)
{
// Draw appliedPath.path() with decr pipeline
setPipeline(m_decClipPipeline);
vs_params.mvp = m_Projection * appliedPath.transform();
@@ -888,8 +970,10 @@ void SokolTessRenderer::applyClipping() {
}
// Incr any paths that are added.
for (auto nextClipPath : state.clipPaths) {
if (alreadyApplied.count(nextClipPath.path())) {
for (auto nextClipPath : state.clipPaths)
{
if (alreadyApplied.count(nextClipPath.path()))
{
// Already applied.
continue;
}
@@ -910,15 +994,18 @@ void SokolTessRenderer::applyClipping() {
}
void SokolTessRenderer::reset() { m_currentPipeline = {0}; }
void SokolTessRenderer::setPipeline(sg_pipeline pipeline) {
if (m_currentPipeline.id == pipeline.id) {
void SokolTessRenderer::setPipeline(sg_pipeline pipeline)
{
if (m_currentPipeline.id == pipeline.id)
{
return;
}
m_currentPipeline = pipeline;
sg_apply_pipeline(pipeline);
}
void SokolTessRenderer::drawPath(RenderPath* path, RenderPaint* paint) {
void SokolTessRenderer::drawPath(RenderPath* path, RenderPaint* paint)
{
auto sokolPaint = static_cast<SokolRenderPaint*>(paint);
applyClipping();
@@ -926,12 +1013,23 @@ void SokolTessRenderer::drawPath(RenderPath* path, RenderPaint* paint) {
const Mat2D& world = transform();
vs_params.mvp = m_Projection * world;
switch (sokolPaint->blendMode()) {
case BlendMode::srcOver: setPipeline(m_pathPipeline[m_clipCount]); break;
case BlendMode::screen: setPipeline(m_pathScreenPipeline[m_clipCount]); break;
case BlendMode::colorDodge: setPipeline(m_pathAdditivePipeline[m_clipCount]); break;
case BlendMode::multiply: setPipeline(m_pathMultiplyPipeline[m_clipCount]); break;
default: setPipeline(m_pathScreenPipeline[m_clipCount]); break;
switch (sokolPaint->blendMode())
{
case BlendMode::srcOver:
setPipeline(m_pathPipeline[m_clipCount]);
break;
case BlendMode::screen:
setPipeline(m_pathScreenPipeline[m_clipCount]);
break;
case BlendMode::colorDodge:
setPipeline(m_pathAdditivePipeline[m_clipCount]);
break;
case BlendMode::multiply:
setPipeline(m_pathMultiplyPipeline[m_clipCount]);
break;
default:
setPipeline(m_pathScreenPipeline[m_clipCount]);
break;
}
static_cast<SokolRenderPaint*>(paint)->draw(vs_params, static_cast<SokolRenderPath*>(path));
@@ -945,7 +1043,8 @@ SokolRenderImageResource::SokolRenderImageResource(const uint8_t* bytes,
.height = (int)height,
.data.subimage[0][0] = {bytes, width * height * 4},
.pixel_format = SG_PIXELFORMAT_RGBA8,
})) {}
}))
{}
SokolRenderImageResource::~SokolRenderImageResource() { sg_destroy_image(m_gpuResource); }
SokolRenderImage::SokolRenderImage(rcp<SokolRenderImageResource> image,
@@ -953,8 +1052,7 @@ SokolRenderImage::SokolRenderImage(rcp<SokolRenderImageResource> image,
uint32_t height,
const Mat2D& uvTransform) :
RenderImage(uvTransform),
m_gpuImage(image)
RenderImage(uvTransform), m_gpuImage(image)
{
float halfWidth = width / 2.0f;
@@ -983,7 +1081,8 @@ SokolRenderImage::SokolRenderImage(rcp<SokolRenderImageResource> image,
});
}
SokolRenderImage::~SokolRenderImage() {
SokolRenderImage::~SokolRenderImage()
{
sg_destroy_buffer(m_vertexBuffer);
sg_destroy_buffer(m_uvBuffer);
}

View File

@@ -7,13 +7,15 @@ static const float contourThreshold = 1.0f;
using namespace rive;
TessRenderPath::TessRenderPath() : m_segmentedContour(contourThreshold) {}
TessRenderPath::TessRenderPath(RawPath& rawPath, FillRule fillRule) :
m_fillRule(fillRule), m_segmentedContour(contourThreshold) {
m_fillRule(fillRule), m_segmentedContour(contourThreshold)
{
m_rawPath.swap(rawPath);
}
TessRenderPath::~TessRenderPath() {}
void TessRenderPath::reset() {
void TessRenderPath::reset()
{
m_rawPath.rewind();
m_subPaths.clear();
m_isContourDirty = m_isTriangulationDirty = true;
@@ -23,28 +25,35 @@ void TessRenderPath::fillRule(FillRule value) { m_fillRule = value; }
void TessRenderPath::moveTo(float x, float y) { m_rawPath.moveTo(x, y); }
void TessRenderPath::lineTo(float x, float y) { m_rawPath.lineTo(x, y); }
void TessRenderPath::cubicTo(float ox, float oy, float ix, float iy, float x, float y) {
void TessRenderPath::cubicTo(float ox, float oy, float ix, float iy, float x, float y)
{
m_rawPath.cubicTo(ox, oy, ix, iy, x, y);
}
void TessRenderPath::close() {
void TessRenderPath::close()
{
m_rawPath.close();
m_isClosed = true;
}
void TessRenderPath::addRenderPath(RenderPath* path, const Mat2D& transform) {
void TessRenderPath::addRenderPath(RenderPath* path, const Mat2D& transform)
{
m_subPaths.emplace_back(SubPath(path, transform));
}
const SegmentedContour& TessRenderPath::segmentedContour() const { return m_segmentedContour; }
// Helper for earcut to understand Vec2D
namespace mapbox {
namespace util {
namespace mapbox
{
namespace util
{
template <> struct nth<0, Vec2D> {
template <> struct nth<0, Vec2D>
{
inline static auto get(const Vec2D& t) { return t.x; };
};
template <> struct nth<1, Vec2D> {
template <> struct nth<1, Vec2D>
{
inline static auto get(const Vec2D& t) { return t.y; };
};
@@ -53,20 +62,24 @@ template <> struct nth<1, Vec2D> {
const RawPath& TessRenderPath::rawPath() const { return m_rawPath; }
void* stdAlloc(void* userData, unsigned int size) {
void* stdAlloc(void* userData, unsigned int size)
{
int* allocated = (int*)userData;
TESS_NOTUSED(userData);
*allocated += (int)size;
return malloc(size);
}
void stdFree(void* userData, void* ptr) {
void stdFree(void* userData, void* ptr)
{
TESS_NOTUSED(userData);
free(ptr);
}
bool TessRenderPath::triangulate() {
if (!m_isTriangulationDirty) {
bool TessRenderPath::triangulate()
{
if (!m_isTriangulationDirty)
{
return false;
}
m_isTriangulationDirty = false;
@@ -74,13 +87,16 @@ bool TessRenderPath::triangulate() {
return true;
}
void TessRenderPath::triangulate(TessRenderPath* containerPath) {
void TessRenderPath::triangulate(TessRenderPath* containerPath)
{
AABB bounds = AABB::forExpansion();
// If there's a single path, we're going to try to assume the user isn't
// doing any funky self overlapping winding and we'll try to triangulate it
// quickly as a single polygon.
if (m_subPaths.size() == 0) {
if (!empty()) {
if (m_subPaths.size() == 0)
{
if (!empty())
{
Mat2D identity;
contour(identity);
@@ -92,15 +108,20 @@ void TessRenderPath::triangulate(TessRenderPath* containerPath) {
containerPath->addTriangles(contour, m_earcut.indices);
}
} else if (m_subPaths.size() == 1) {
}
else if (m_subPaths.size() == 1)
{
// We're a container but we only have 1 path, let's see if we can use
// our fast triangulator.
SubPath& subPath = m_subPaths.front();
auto subRenderPath = static_cast<TessRenderPath*>(subPath.path());
if (subRenderPath->isContainer()) {
if (subRenderPath->isContainer())
{
// Nope, subpath is also a container, keep going.
subRenderPath->triangulate(containerPath);
} else if (!subRenderPath->empty()) {
}
else if (!subRenderPath->empty())
{
// Yes, it's a single path with commands, triangulate it.
subRenderPath->contour(subPath.transform());
const SegmentedContour& segmentedContour = subRenderPath->segmentedContour();
@@ -110,15 +131,22 @@ void TessRenderPath::triangulate(TessRenderPath* containerPath) {
containerPath->addTriangles(contour, m_earcut.indices);
}
} else {
}
else
{
// We're a container with multiple sub-paths.
TESStesselator* tess = nullptr;
for (SubPath& subPath : m_subPaths) {
for (SubPath& subPath : m_subPaths)
{
auto subRenderPath = static_cast<TessRenderPath*>(subPath.path());
if (subRenderPath->isContainer()) {
if (subRenderPath->isContainer())
{
subRenderPath->triangulate(containerPath);
} else if (!subRenderPath->empty()) {
if (tess == nullptr) {
}
else if (!subRenderPath->empty())
{
if (tess == nullptr)
{
tess = tessNewTess(nullptr);
}
subRenderPath->contour(subPath.transform());
@@ -128,8 +156,10 @@ void TessRenderPath::triangulate(TessRenderPath* containerPath) {
bounds.expand(segmentedContour.bounds());
}
}
if (tess != nullptr) {
if (tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, 3, 2, 0)) {
if (tess != nullptr)
{
if (tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, 3, 2, 0))
{
auto verts = tessGetVertices(tess);
// const int* vinds = tessGetVertexIndices(tess);
auto nverts = tessGetVertexCount(tess);
@@ -137,7 +167,8 @@ void TessRenderPath::triangulate(TessRenderPath* containerPath) {
auto nelems = tessGetElementCount(tess);
std::vector<uint16_t> indices;
for (int i = 0; i < nelems * 3; i++) {
for (int i = 0; i < nelems * 3; i++)
{
indices.push_back(elems[i]);
}
@@ -151,8 +182,10 @@ void TessRenderPath::triangulate(TessRenderPath* containerPath) {
containerPath->setTriangulatedBounds(bounds);
}
void TessRenderPath::contour(const Mat2D& transform) {
if (!m_isContourDirty && transform == m_contourTransform) {
void TessRenderPath::contour(const Mat2D& transform)
{
if (!m_isContourDirty && transform == m_contourTransform)
{
return;
}
@@ -165,9 +198,12 @@ void TessRenderPath::extrudeStroke(ContourStroke* stroke,
StrokeJoin join,
StrokeCap cap,
float strokeWidth,
const Mat2D& transform) {
if (isContainer()) {
for (auto& subPath : m_subPaths) {
const Mat2D& transform)
{
if (isContainer())
{
for (auto& subPath : m_subPaths)
{
static_cast<TessRenderPath*>(subPath.path())
->extrudeStroke(stroke, join, cap, strokeWidth, subPath.transform());
}

View File

@@ -8,24 +8,28 @@ TessRenderer::TessRenderer() { m_Stack.emplace_back(RenderState()); }
void TessRenderer::projection(const Mat4& value) { m_Projection = value; }
void TessRenderer::save() { m_Stack.push_back(m_Stack.back()); }
void TessRenderer::restore() {
void TessRenderer::restore()
{
assert(m_Stack.size() > 1);
RenderState& state = m_Stack.back();
m_Stack.pop_back();
// We can only add clipping paths so if they're still the same, nothing has
// changed.
if (state.clipPaths.size() != m_Stack.back().clipPaths.size()) {
if (state.clipPaths.size() != m_Stack.back().clipPaths.size())
{
m_IsClippingDirty = true;
}
}
void TessRenderer::transform(const Mat2D& transform) {
void TessRenderer::transform(const Mat2D& transform)
{
Mat2D& stackMat = m_Stack.back().transform;
stackMat = stackMat * transform;
}
void TessRenderer::clipPath(RenderPath* path) {
void TessRenderer::clipPath(RenderPath* path)
{
RenderState& state = m_Stack.back();
state.clipPaths.emplace_back(SubPath(path, state.transform));
@@ -38,4 +42,5 @@ void TessRenderer::drawImageMesh(const RenderImage*,
rcp<RenderBuffer> uvCoords_f32,
rcp<RenderBuffer> indices_u16,
BlendMode,
float opacity) {}
float opacity)
{}

View File

@@ -2,7 +2,8 @@
#include "rive/math/mat4.hpp"
#include "rive/math/mat2d.hpp"
TEST_CASE("Mat2D to Mat4 works", "[mat4]") {
TEST_CASE("Mat2D to Mat4 works", "[mat4]")
{
rive::Mat2D matrix2D(0.1f, 0.2f, 0.0f, 2.0f, 22.0f, 33.0f);
rive::Mat4 matrix4x4 = matrix2D;
REQUIRE(matrix4x4[0] == 0.1f);
@@ -13,7 +14,8 @@ TEST_CASE("Mat2D to Mat4 works", "[mat4]") {
REQUIRE(matrix4x4[13] == 33.0f);
}
TEST_CASE("Mat4 times Mat2 works", "[mat4]") {
TEST_CASE("Mat4 times Mat2 works", "[mat4]")
{
rive::Mat2D matrix2D(0.1f, 0.2f, 0.0f, 2.0f, 22.0f, 33.0f);
rive::Mat4 identity4x4;
rive::Mat4 matrix4x4 = identity4x4 * matrix2D;
@@ -25,7 +27,8 @@ TEST_CASE("Mat4 times Mat2 works", "[mat4]") {
REQUIRE(matrix4x4[13] == 33.0f);
}
TEST_CASE("Mat4 times Mat4 works", "[mat4]") {
TEST_CASE("Mat4 times Mat4 works", "[mat4]")
{
rive::Mat4 a(
// clang-format off
5.0f, 7.0f, 9.0f, 10.0f,

View File

@@ -3,14 +3,16 @@
#include "rive/tess/tess_render_path.hpp"
#include "rive_file_reader.hpp"
class TestRenderPath : public rive::TessRenderPath {
class TestRenderPath : public rive::TessRenderPath
{
public:
std::vector<rive::Vec2D> vertices;
std::vector<uint16_t> indices;
protected:
virtual void addTriangles(rive::Span<const rive::Vec2D> vts,
rive::Span<const uint16_t> idx) override {
rive::Span<const uint16_t> idx) override
{
vertices.insert(vertices.end(), vts.begin(), vts.end());
indices.insert(indices.end(), idx.begin(), idx.end());
}
@@ -18,7 +20,8 @@ protected:
void setTriangulatedBounds(const rive::AABB& value) override {}
};
TEST_CASE("simple triangle path triangulates as expected", "[file]") {
TEST_CASE("simple triangle path triangulates as expected", "[file]")
{
auto file = ReadRiveFile("../test/assets/triangle.riv");
auto artboard = file->artboard();
artboard->advance(0.0f);