| /* | |
| * 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;) |