blob: 125b8941032f33645f1cbbb67220414551f913d7 [file] [log] [blame]
/*
* 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());
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);
}
if (asClip)
{
Paint red;
red->color(0xffff0000);
renderer->clipPath(path.get());
renderer->drawPath(PathBuilder::Rect({0, 0, SIZE, SIZE}).get(),
red.get());
}
else
{
Paint magenta;
magenta->color(0xffff00ff);
renderer->drawPath(path.get(), magenta.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);
}