mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
|
||||
#include "rive/factory.hpp"
|
||||
|
||||
namespace rive {
|
||||
namespace rive
|
||||
{
|
||||
|
||||
class SokolFactory : public Factory {
|
||||
class SokolFactory : public Factory
|
||||
{
|
||||
|
||||
public:
|
||||
SokolFactory();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{}
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user