blob: 7117cc96bf42f5571b177d08133b7eefad1950db [file] [log] [blame] [edit]
/*
* Copyright 2011 Google Inc.
* Copyright 2022 Rive
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm.hpp"
#include "gmutils.hpp"
#include "rive/shapes/paint/color.hpp"
#include "rive/math/mat2d.hpp"
#include "rive/renderer.hpp"
using namespace rivegm;
using namespace rive;
// https://bug.skia.org/1316 shows that this cubic, when slightly clipped,
// creates big (incorrect) changes to its control points.
class ClippedCubicGM : public GM
{
public:
ClippedCubicGM() : GM(600, 500) {}
private:
void onDraw(Renderer* canvas) override
{
canvas->translate(25, 25);
Path path;
path->moveTo(0, 0);
path->cubicTo(140, 150, 40, 10, 170, 150);
Paint paint;
AABB bounds = {0, 0, 170, 150}; // path.getBounds();
for (float dy = -1; dy <= 1; dy += 1)
{
canvas->save();
for (float dx = -1; dx <= 1; dx += 1)
{
canvas->save();
canvas->clipPath(PathBuilder::Rect(bounds));
canvas->translate(dx, dy);
canvas->drawPath(path, paint);
canvas->restore();
canvas->translate(bounds.width(), 0);
}
canvas->restore();
canvas->translate(0, bounds.height());
}
}
};
class ClippedCubic2GM : public GM
{
public:
ClippedCubic2GM() : GM(725, 325) {}
private:
void onDraw(Renderer* canvas) override
{
canvas->translate(25, 25);
canvas->save();
canvas->translate(-2, 120);
drawOne(canvas, fPath, {0, 0, 80, 150});
canvas->translate(170, 0);
drawOne(canvas, fPath, {0, 0, 80, 100});
canvas->translate(170, 0);
drawOne(canvas, fPath, {0, 0, 30, 150});
canvas->translate(170, 0);
drawOne(canvas, fPath, {0, 0, 10, 150});
canvas->restore();
canvas->save();
canvas->translate(20, -2);
drawOne(canvas, fFlipped, {0, 0, 150, 80});
canvas->translate(170, 0);
drawOne(canvas, fFlipped, {0, 0, 100, 80});
canvas->translate(170, 0);
drawOne(canvas, fFlipped, {0, 0, 150, 30});
canvas->translate(170, 0);
drawOne(canvas, fFlipped, {0, 0, 150, 10});
canvas->restore();
}
void drawOne(Renderer* canvas, const Path& path, const AABB& clip)
{
Paint framePaint, fillPaint;
framePaint->style(RenderPaintStyle::stroke);
canvas->drawPath(PathBuilder::Rect(clip), framePaint);
canvas->drawPath(path, framePaint);
canvas->save();
canvas->clipPath(PathBuilder::Rect(clip));
canvas->drawPath(path, fillPaint);
canvas->restore();
}
void onOnceBeforeDraw() override
{
fPath->moveTo(69.7030518991886f, 0);
fPath->cubicTo(69.7030518991886f,
21.831149999999997f,
58.08369508178456f,
43.66448333333333f,
34.8449814469765f,
65.5f);
fPath->cubicTo(11.608591683531916f,
87.33115f,
-0.010765133872116195f,
109.16448333333332f,
-0.013089005235602302f,
131);
fPath->close();
Mat2D matrix;
matrix[0] = 0.0f;
matrix[1] = 1.0f;
matrix[2] = 1.0f;
matrix[3] = 0.0f;
fFlipped->addRenderPath(fPath, matrix);
}
Path fPath;
Path fFlipped;
};
class CubicPathGM : public GM
{
public:
CubicPathGM() : GM(825, 175) {}
private:
void drawPath(Path& path,
Renderer* canvas,
ColorInt color,
const AABB& clip,
StrokeCap cap,
StrokeJoin join,
RenderPaintStyle style,
FillRule fill,
float strokeWidth)
{
path->fillRule(fill);
Paint paint;
paint->cap(cap);
paint->thickness(strokeWidth);
paint->join(join);
paint->color(color);
paint->style(style);
canvas->save();
canvas->clipPath(PathBuilder::Rect(clip));
canvas->drawPath(path, paint);
canvas->restore();
}
void onDraw(Renderer* canvas) override
{
struct FillAndName
{
FillRule fFill;
const char* fName;
};
constexpr FillAndName gFills[] = {
{FillRule::nonZero, "Winding"},
{FillRule::evenOdd, "Even / Odd"},
};
struct StyleAndName
{
RenderPaintStyle fStyle;
const char* fName;
};
constexpr StyleAndName gStyles[] = {
{RenderPaintStyle::fill, "Fill"},
{RenderPaintStyle::stroke, "Stroke"},
};
struct CapAndName
{
StrokeCap fCap;
StrokeJoin fJoin;
const char* fName;
};
constexpr CapAndName gCaps[] = {
{StrokeCap::butt, StrokeJoin::bevel, "Butt"},
{StrokeCap::round, StrokeJoin::round, "Round"},
{StrokeCap::square, StrokeJoin::bevel, "Square"}};
struct PathAndName
{
Path fPath;
const char* fName;
};
PathAndName path;
path.fPath->moveTo(25 * 1.0f, 10 * 1.0f);
path.fPath->cubicTo(40 * 1.0f,
20 * 1.0f,
60 * 1.0f,
20 * 1.0f,
75 * 1.0f,
10 * 1.0f);
path.fName = "moveTo-cubic";
AABB rect = {0, 0, 100 * 1.0f, 30 * 1.0f};
canvas->save();
canvas->translate(10 * 1.0f, 30 * 1.0f);
canvas->save();
for (size_t cap = 0; cap < std::size(gCaps); ++cap)
{
if (0 < cap)
{
canvas->translate((rect.width() + 40 * 1.0f) *
std::size(gStyles),
0);
}
canvas->save();
for (size_t fill = 0; fill < std::size(gFills); ++fill)
{
if (0 < fill)
{
canvas->translate(0, rect.height() + 40 * 1.0f);
}
canvas->save();
for (size_t style = 0; style < std::size(gStyles); ++style)
{
if (0 < style)
{
canvas->translate(rect.width() + 40 * 1.0f, 0);
}
ColorInt color = 0xff007000;
this->drawPath(path.fPath,
canvas,
color,
rect,
gCaps[cap].fCap,
gCaps[cap].fJoin,
gStyles[style].fStyle,
gFills[fill].fFill,
1.0f * 10);
Paint rectPaint;
rectPaint->color(0xFF000000);
rectPaint->style(RenderPaintStyle::stroke);
rectPaint->thickness(-1);
canvas->drawPath(PathBuilder::Rect(rect), rectPaint);
}
canvas->restore();
}
canvas->restore();
}
canvas->restore();
canvas->restore();
}
};
class CubicClosePathGM : public GM
{
public:
CubicClosePathGM() : GM(825, 175) {}
private:
void drawPath(Path& path,
Renderer* canvas,
ColorInt color,
const AABB& clip,
StrokeCap cap,
StrokeJoin join,
RenderPaintStyle style,
FillRule fill,
float strokeWidth)
{
path->fillRule(fill);
Paint paint;
paint->cap(cap);
paint->thickness(strokeWidth);
paint->join(join);
paint->color(color);
paint->style(style);
canvas->save();
canvas->clipPath(PathBuilder::Rect(clip));
canvas->drawPath(path, paint);
canvas->restore();
}
void onDraw(Renderer* canvas) override
{
struct FillAndName
{
FillRule fFill;
const char* fName;
};
constexpr FillAndName gFills[] = {
{FillRule::nonZero, "Winding"},
{FillRule::evenOdd, "Even / Odd"},
};
struct StyleAndName
{
RenderPaintStyle fStyle;
const char* fName;
};
constexpr StyleAndName gStyles[] = {
{RenderPaintStyle::fill, "Fill"},
{RenderPaintStyle::stroke, "Stroke"},
};
struct CapAndName
{
StrokeCap fCap;
StrokeJoin fJoin;
const char* fName;
};
constexpr CapAndName gCaps[] = {
{StrokeCap::butt, StrokeJoin::bevel, "Butt"},
{StrokeCap::round, StrokeJoin::round, "Round"},
{StrokeCap::square, StrokeJoin::bevel, "Square"}};
struct PathAndName
{
Path fPath;
const char* fName;
};
PathAndName path;
path.fPath->moveTo(25 * 1.0f, 10 * 1.0f);
path.fPath->cubicTo(40 * 1.0f,
20 * 1.0f,
60 * 1.0f,
20 * 1.0f,
75 * 1.0f,
10 * 1.0f);
path.fPath->close();
path.fName = "moveTo-cubic-close";
AABB rect = {0, 0, 100 * 1.0f, 30 * 1.0f};
canvas->save();
canvas->translate(10 * 1.0f, 30 * 1.0f);
canvas->save();
for (size_t cap = 0; cap < std::size(gCaps); ++cap)
{
if (0 < cap)
{
canvas->translate((rect.width() + 40 * 1.0f) *
std::size(gStyles),
0);
}
canvas->save();
for (size_t fill = 0; fill < std::size(gFills); ++fill)
{
if (0 < fill)
{
canvas->translate(0, rect.height() + 40 * 1.0f);
}
canvas->save();
for (size_t style = 0; style < std::size(gStyles); ++style)
{
if (0 < style)
{
canvas->translate(rect.width() + 40 * 1.0f, 0);
}
ColorInt color = 0xff007000;
this->drawPath(path.fPath,
canvas,
color,
rect,
gCaps[cap].fCap,
gCaps[cap].fJoin,
gStyles[style].fStyle,
gFills[fill].fFill,
1.0f * 10);
Paint rectPaint;
rectPaint->color(0xFF000000);
rectPaint->style(RenderPaintStyle::stroke);
rectPaint->thickness(-1);
canvas->drawPath(PathBuilder::Rect(rect), rectPaint);
}
canvas->restore();
}
canvas->restore();
}
canvas->restore();
canvas->restore();
}
};
DEF_SIMPLE_GM(bug5099, 50, 50, canvas)
{
Paint p;
p->color(0xFFFF0000);
p->style(RenderPaintStyle::stroke);
p->thickness(10);
Path path;
path->moveTo(6, 27);
path->cubicTo(31.5f, 1.5f, 3.5f, 4.5f, 29, 29);
canvas->drawPath(path, p);
}
DEF_SIMPLE_GM(bug6083, 100, 50, canvas)
{
Paint p;
p->color(0xFFFF0000);
p->style(RenderPaintStyle::stroke);
p->thickness(15);
canvas->translate(-500, -130);
Path path;
path->moveTo(500.988f, 155.200f);
path->lineTo(526.109f, 155.200f);
Vec2D p1 = {526.109f, 155.200f};
Vec2D p2 = {525.968f, 212.968f};
Vec2D p3 = {526.109f, 241.840f};
path->cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
canvas->drawPath(path, p);
canvas->translate(50, 0);
path = Path();
p2 = Vec2D(525.968f, 213.172f);
path->moveTo(500.988f, 155.200f);
path->lineTo(526.109f, 155.200f);
path->cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
canvas->drawPath(path, p);
}
//////////////////////////////////////////////////////////////////////////////
GMREGISTER(cubicpath, return new CubicPathGM;)
GMREGISTER(cubicclosepath, return new CubicClosePathGM;)
GMREGISTER(clippedcubic, return new ClippedCubicGM;)
GMREGISTER(clippedcubic2, return new ClippedCubic2GM;)