blob: 2060bc496c34d3c1d7d5b15ab35130f573570863 [file]
/*
* Copyright 2025 Rive
*/
#include "gm.hpp"
#include "gmutils.hpp"
using namespace rivegm;
using namespace rive;
constexpr int SIZE = 1600;
// Tests that interior triangulations with negative coverage render correctly
// with clockwise fill, both as paths and as clips.
static void draw_test(Renderer* renderer, bool asClip)
{
PathBuilder checkerboard;
checkerboard.fillRule(FillRule::clockwise);
constexpr static int GRID_COUNT = 50;
constexpr static float CELL_SIZE = (float)SIZE / GRID_COUNT;
for (int y = 0; y < GRID_COUNT; y += 1)
{
checkerboard.addRect(AABB(0, y * CELL_SIZE, SIZE, (y + 1) * CELL_SIZE),
(y & 1) ? rivegm::PathDirection::cw
: rivegm::PathDirection::ccw);
}
for (int x = 0; x < GRID_COUNT; x += 1)
{
checkerboard.addRect(AABB(x * CELL_SIZE, 0, (x + 1) * CELL_SIZE, SIZE),
(x & 1) ? rivegm::PathDirection::cw
: rivegm::PathDirection::ccw);
}
renderer->clipPath(checkerboard.detach());
for (bool negativeDeterminant : {false, true})
{
AutoRestore ar(renderer, true);
if (negativeDeterminant)
{
renderer->translate(SIZE - 7, 207);
renderer->scale(-1, 1);
}
else
{
renderer->translate(29, -100);
}
Path path;
path->fillRule(FillRule::clockwise);
// Add a negative rectangle.
path->addRect(SIZE, 0, -SIZE, SIZE);
// The first path will be completely erased by the negative rectangle.
// It will only show if we draw it over itself twice.
for (float x : {SIZE / 2.f, .0f, SIZE / 2.f})
{
path->moveTo(x + 50, SIZE / 2.5f);
path->cubicTo(x + 50,
0,
x + SIZE / 2.f - 50,
0,
x + SIZE / 2.f - 50,
SIZE / 2.5f);
path->cubicTo(x + SIZE / 2.f - 50,
SIZE,
x + 50,
SIZE,
x + 50,
SIZE / 2.5f);
}
Paint paint;
if (asClip)
{
paint->color(negativeDeterminant ? 0xd0900000 : 0xffff0000);
renderer->clipPath(path.get());
renderer->drawPath(PathBuilder::Rect({0, 0, SIZE, SIZE}).get(),
paint.get());
}
else
{
paint->color(negativeDeterminant ? 0xc0700070 : 0xffff00ff);
renderer->drawPath(path.get(), paint.get());
}
}
}
DEF_SIMPLE_GM_WITH_CLEAR_COLOR(negative_interior_triangles,
0xff00ffff,
SIZE,
SIZE,
renderer)
{
draw_test(renderer, /*asClip=*/false);
}
DEF_SIMPLE_GM_WITH_CLEAR_COLOR(negative_interior_triangles_as_clip,
0xff00ffff,
SIZE,
SIZE,
renderer)
{
draw_test(renderer, /*asClip=*/true);
}