| /* | |
| * Copyright 2026 Rive | |
| * | |
| * Draws the same scene immediately and via a DeferredFactory recording | |
| * replayed against the real factory and renderer. The goldens must be byte | |
| * identical. | |
| */ | |
| #include "gm.hpp" | |
| #include "gmutils.hpp" | |
| #include "rive/renderer/cmd/deferred_render_factory.hpp" | |
| #include "rive/renderer/cmd/render_replay.hpp" | |
| #include "rive/math/raw_path.hpp" | |
| #include "rive/math/mat2d.hpp" | |
| #include "rive/shapes/paint/image_sampler.hpp" | |
| #include "assets/batdude.png.hpp" | |
| using namespace rivegm; | |
| using namespace rive; | |
| static RawPath kShape() | |
| { | |
| RawPath p; | |
| p.move({40, 40}); | |
| p.line({160, 70}); | |
| p.line({120, 110}); | |
| p.line({200, 200}); | |
| p.line({120, 160}); | |
| p.line({60, 210}); | |
| p.close(); | |
| return p; | |
| } | |
| // Fill and stroke so all paint properties get recorded. | |
| static void drawScene(Factory* factory, Renderer* renderer) | |
| { | |
| RawPath shape = kShape(); | |
| auto path = factory->makeRenderPath(shape, FillRule::nonZero); | |
| auto fill = factory->makeRenderPaint(); | |
| fill->style(RenderPaintStyle::fill); | |
| fill->color(0xFFFFA030); | |
| renderer->drawPath(path.get(), fill.get()); | |
| auto stroke = factory->makeRenderPaint(); | |
| stroke->style(RenderPaintStyle::stroke); | |
| stroke->color(0xFF3050FF); | |
| stroke->thickness(10); | |
| stroke->join(StrokeJoin::round); | |
| stroke->cap(StrokeCap::round); | |
| renderer->drawPath(path.get(), stroke.get()); | |
| // Exercises gradient shaders. | |
| RawPath box; | |
| box.move({30, 30}); | |
| box.line({226, 30}); | |
| box.line({226, 90}); | |
| box.line({30, 90}); | |
| box.close(); | |
| auto boxPath = factory->makeRenderPath(box, FillRule::nonZero); | |
| const ColorInt colors[] = {0xFF00E0A0, 0xFFE000A0}; | |
| const float stops[] = {0.0f, 1.0f}; | |
| auto grad = factory->makeLinearGradient(30, 30, 226, 90, colors, stops, 2); | |
| auto gradPaint = factory->makeRenderPaint(); | |
| gradPaint->style(RenderPaintStyle::fill); | |
| gradPaint->shader(grad); | |
| renderer->drawPath(boxPath.get(), gradPaint.get()); | |
| // Exercises paths built verb by verb. | |
| auto tri = factory->makeEmptyRenderPath(); | |
| tri->fillRule(FillRule::nonZero); | |
| tri->moveTo(20, 195); | |
| tri->lineTo(80, 195); | |
| tri->lineTo(50, 150); | |
| tri->close(); | |
| auto triPaint = factory->makeRenderPaint(); | |
| triPaint->style(RenderPaintStyle::fill); | |
| triPaint->color(0xFFFFFFFF); | |
| renderer->drawPath(tri.get(), triPaint.get()); | |
| // Exercises image decode and draw. | |
| auto img = factory->decodeImage(assets::batdude_png()); | |
| if (img) | |
| { | |
| renderer->save(); | |
| renderer->transform(Mat2D(0.12f, 0, 0, 0.12f, 150, 110)); | |
| renderer->drawImage(img.get(), | |
| ImageSampler::LinearClamp(), | |
| BlendMode::srcOver, | |
| 1.0f); | |
| renderer->restore(); | |
| } | |
| } | |
| class RenderDeferred2DGM : public GM | |
| { | |
| public: | |
| RenderDeferred2DGM(bool deferred) : GM(256, 256), m_deferred(deferred) {} | |
| ColorInt clearColor() const override { return 0xff202028; } | |
| void onDraw(rive::Renderer* renderer) override | |
| { | |
| Factory* factory = TestingWindow::Get()->factory(); | |
| if (!factory) | |
| { | |
| return; | |
| } | |
| if (m_deferred) | |
| { | |
| cmd::DeferredFactory df; | |
| auto dr = df.makeRenderer(); | |
| drawScene(&df, dr.get()); | |
| cmd::replayRenderCommands(factory, renderer, df.commandBuffer()); | |
| } | |
| else | |
| { | |
| drawScene(factory, renderer); | |
| } | |
| } | |
| private: | |
| bool m_deferred; | |
| }; | |
| GMREGISTER(render_deferred_2d_immediate, return new RenderDeferred2DGM(false)) | |
| GMREGISTER(render_deferred_2d, return new RenderDeferred2DGM(true)) |