| /* | |
| * Copyright 2013 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/renderer.hpp" | |
| #include "rive/math/math_types.hpp" | |
| #include "rive/math/vec2d.hpp" | |
| #include "rive/math/mat2d.hpp" | |
| #include "common/rand.hpp" | |
| using namespace rivegm; | |
| using namespace rive; | |
| /** | |
| * Renders overlapping shapes with colorburn against a checkerboard. | |
| */ | |
| class DstReadShuffle : public GM | |
| { | |
| public: | |
| DstReadShuffle() : GM(530, 690) {} | |
| protected: | |
| enum ShapeType | |
| { | |
| kCircle_ShapeType, | |
| kRoundRect_ShapeType, | |
| kRect_ShapeType, | |
| kConvexPath_ShapeType, | |
| kPolygonFan_ShapeType, | |
| kConcavePath_ShapeType, | |
| kNumShapeTypes | |
| }; | |
| static void fan_polygon(PathBuilder& builder, | |
| float a0, | |
| float a1, | |
| float x, | |
| float y, | |
| float rx, | |
| float ry, | |
| int depth) | |
| { | |
| if (depth <= 0) | |
| { | |
| return; | |
| } | |
| float ai = (a0 + a1) / 2; | |
| builder.moveTo(cosf(a0) * rx + x, sinf(a0) * ry + y); | |
| builder.lineTo(cosf(ai) * rx + x, sinf(ai) * ry + y); | |
| builder.lineTo(cosf(a1) * rx + x, sinf(a1) * ry + y); | |
| builder.close(); | |
| fan_polygon(builder, a0, ai, x, y, rx, ry, depth - 1); | |
| fan_polygon(builder, ai, a1, x, y, rx, ry, depth - 1); | |
| } | |
| void drawShape(Renderer* renderer, const Paint& paint, ShapeType type) | |
| { | |
| const float l = 0, t = 0, r = 75, b = 85; | |
| const AABB kRect(l, t, r, b); | |
| PathBuilder builder; | |
| switch (type) | |
| { | |
| case kCircle_ShapeType: | |
| builder.addOval(kRect); | |
| break; | |
| case kRoundRect_ShapeType: | |
| builder.addRRect(kRect, 15.f, 15.f); | |
| break; | |
| case kRect_ShapeType: | |
| builder.addRect(kRect); | |
| break; | |
| case kConvexPath_ShapeType: | |
| { | |
| builder.moveTo(l, t); | |
| builder.quadTo(r, t, r, b); | |
| builder.quadTo(l, b, l, t); | |
| break; | |
| } | |
| case kPolygonFan_ShapeType: | |
| { | |
| float a0 = 0, a1 = 2 * math::PI / 3, a2 = 4 * math::PI / 3, | |
| a3 = 2 * math::PI; | |
| float x = (l + r) / 2, y = (t + b) / 2; | |
| float rx = (r - l) / 2, ry = (b - t) / 2; | |
| builder.moveTo(cosf(a0) * rx + x, sinf(a0) * ry + y); | |
| builder.lineTo(cosf(a1) * rx + x, sinf(a1) * ry + y); | |
| builder.lineTo(cosf(a2) * rx + x, sinf(a2) * ry + y); | |
| builder.lineTo(cosf(a3) * rx + x, sinf(a3) * ry + y); | |
| builder.close(); | |
| fan_polygon(builder, a0, a1, x, y, rx, ry, 5); | |
| fan_polygon(builder, a1, a2, x, y, rx, ry, 5); | |
| fan_polygon(builder, a2, a3, x, y, rx, ry, 5); | |
| break; | |
| } | |
| case kConcavePath_ShapeType: | |
| { | |
| Vec2D points[5] = { | |
| {70.f * cosf(-math::PI / 2), 70.f * sinf(-math::PI / 2)}}; | |
| Mat2D rot = Mat2D::fromRotation(2 * math::PI / 5); | |
| for (int i = 1; i < 5; ++i) | |
| { | |
| rot.mapPoints(points + i, points + i - 1, 1); | |
| } | |
| builder.moveTo(points[0].x + 50, points[0].y + 70); | |
| for (int i = 0; i < 5; ++i) | |
| { | |
| auto [x, y] = points[(2 * i) % 5]; | |
| builder.lineTo(x + 50, y + 70); | |
| } | |
| builder.fillRule(FillRule::evenOdd); | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| renderer->drawPath(builder.detach(), paint); | |
| } | |
| static ColorInt GetColor(Rand* random) | |
| { | |
| return 0x80000000 | (random->u32() & 0xffffff); | |
| } | |
| // static void DrawHairlines(Renderer* renderer) { | |
| // if (canvas->imageInfo().alphaType() == kOpaque_SkAlphaType) { | |
| // draw_clear(renderer, kBackground); | |
| // } else { | |
| // canvas->clear(SK_ColorTRANSPARENT); | |
| // } | |
| // SkPaint hairPaint; | |
| // hairPaint.setStyle(SkPaint::kStroke_Style); | |
| // hairPaint.setStrokeWidth(0); | |
| // hairPaint.setAntiAlias(true); | |
| // static constexpr int kNumHairlines = 12; | |
| // SkPoint pts[] = {{3.f, 7.f}, {29.f, 7.f}}; | |
| // Rand colorRandom; | |
| // SkMatrix rot; | |
| // rot.setRotate(360.f / kNumHairlines, 15.5f, 12.f); | |
| // rot.postTranslate(3.f, 0); | |
| // for (int i = 0; i < 12; ++i) { | |
| // hairPaint.setColor(GetColor(&colorRandom)); | |
| // canvas->drawLine(pts[0], pts[1], hairPaint); | |
| // rot.mapPoints(pts, 2); | |
| // } | |
| // } | |
| void onDraw(Renderer* renderer) override | |
| { | |
| Paint p; | |
| p->color(0x80808080); | |
| draw_rect(renderer, {0, 0, 530, 690}, p); | |
| float y = 5; | |
| for (int i = 0; i < kNumShapeTypes; i++) | |
| { | |
| Rand colorRandom; | |
| ShapeType shapeType = static_cast<ShapeType>(i); | |
| float x = 5; | |
| for (int r = 0; r <= 15; r++) | |
| { | |
| Paint p; | |
| p->color(GetColor(&colorRandom)); | |
| // In order to get some op combining on the GPU backend we do 2 | |
| // src over for each xfer mode which requires a dst read | |
| constexpr static BlendMode blendModes[] = { | |
| BlendMode::srcOver, | |
| BlendMode::screen, | |
| BlendMode::overlay, | |
| BlendMode::darken, | |
| BlendMode::lighten, | |
| BlendMode::colorDodge, | |
| BlendMode::colorBurn, | |
| BlendMode::hardLight, | |
| BlendMode::softLight, | |
| BlendMode::exclusion, | |
| BlendMode::hue, | |
| BlendMode::saturation, | |
| BlendMode::color, | |
| BlendMode::luminosity, | |
| }; | |
| p->blendMode(blendModes[r % std::size(blendModes)]); | |
| renderer->save(); | |
| renderer->translate(x, y); | |
| this->drawShape(renderer, p, shapeType); | |
| renderer->restore(); | |
| x += 15; | |
| } | |
| y += 110; | |
| } | |
| // // Draw hairlines to a surface and then draw that to the main canvas | |
| // with a zoom so that | |
| // // it is easier to see how they blend. | |
| // SkImageInfo info; | |
| // // Recording canvases don't have a color type. | |
| // if (SkColorType::kUnknown_SkColorType == | |
| // canvas->imageInfo().colorType()) { | |
| // info = SkImageInfo::MakeN32Premul(35, 35); | |
| // } else { | |
| // info = SkImageInfo::Make(35, 35, | |
| // canvas->imageInfo().colorType(), | |
| // canvas->imageInfo().alphaType(), | |
| // canvas->imageInfo().refColorSpace()); | |
| // } | |
| // auto surf = canvas->makeSurface(info); | |
| // if (!surf) { | |
| // // Fall back to raster. Raster supports only one of the 8 bit | |
| // per-channel RGBA or BGRA | |
| // // formats. This fall back happens when running with | |
| // --preAbandonGpuContext. if ((info.colorType() == | |
| // kRGBA_8888_SkColorType || | |
| // info.colorType() == kBGRA_8888_SkColorType) && | |
| // info.colorType() != kN32_SkColorType) { | |
| // info = SkImageInfo::Make(35, 35, | |
| // kN32_SkColorType, | |
| // canvas->imageInfo().alphaType(), | |
| // canvas->imageInfo().refColorSpace()); | |
| // } | |
| // surf = SkSurface::MakeRaster(info); | |
| // SkASSERT(surf); | |
| // } | |
| // canvas->scale(5.f, 5.f); | |
| // canvas->translate(67.f, 10.f); | |
| // DrawHairlines(surf->getCanvas()); | |
| // canvas->drawImage(surf->makeImageSnapshot(), 0.f, 0.f); | |
| } | |
| private: | |
| static constexpr ColorInt kBackground = 0xFFCCCCCC; | |
| using INHERITED = GM; | |
| }; | |
| ////////////////////////////////////////////////////////////////////////////// | |
| GMREGISTER(dstreadshuffle, return new DstReadShuffle;) |