blob: 623bdbb2081bfb188c73f91224701a3df59dd1ea [file] [log] [blame]
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -04009#include "include/core/SkBlendMode.h"
10#include "include/core/SkCanvas.h"
11#include "include/core/SkMatrix.h"
12#include "include/core/SkPaint.h"
13#include "include/core/SkPoint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "include/core/SkRRect.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040015#include "include/core/SkRect.h"
16#include "include/core/SkScalar.h"
17#include "include/core/SkSize.h"
18#include "include/core/SkString.h"
19#include "include/core/SkTypes.h"
Chris Daltonfa5a8b82021-07-09 12:17:04 -060020#include "include/effects/SkGradientShader.h"
Robert Phillips59ba27b2022-04-12 09:59:38 -040021#include "include/private/gpu/ganesh/GrTypesPriv.h"
Brian Salomon8f7d9532020-12-23 09:16:59 -050022#include "src/core/SkCanvasPriv.h"
Kevin Lubickacdc1082023-06-09 11:05:24 -040023#include "src/gpu/ganesh/GrCanvas.h"
Greg Daniel719239c2022-04-07 11:20:24 -040024#include "src/gpu/ganesh/GrCaps.h"
25#include "src/gpu/ganesh/GrFragmentProcessor.h"
26#include "src/gpu/ganesh/GrPaint.h"
Robert Phillipsc1b94082022-08-09 17:16:19 -040027#include "src/gpu/ganesh/SurfaceDrawContext.h"
Greg Daniel719239c2022-04-07 11:20:24 -040028#include "src/gpu/ganesh/effects/GrPorterDuffXferProcessor.h"
29#include "src/gpu/ganesh/effects/GrRRectEffect.h"
30#include "src/gpu/ganesh/ops/FillRectOp.h"
31#include "src/gpu/ganesh/ops/GrDrawOp.h"
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +000032
Ben Wagner7fde8e12019-05-01 17:28:53 -040033#include <memory>
34#include <utility>
35
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +000036namespace skiagm {
37
38///////////////////////////////////////////////////////////////////////////////
39
40class RRectGM : public GM {
41public:
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000042 enum Type {
43 kBW_Draw_Type,
44 kAA_Draw_Type,
45 kBW_Clip_Type,
46 kAA_Clip_Type,
47 kEffect_Type,
48 };
herbb10fe492016-01-08 13:48:43 -080049 RRectGM(Type type) : fType(type) { }
50
51protected:
52
53 void onOnceBeforeDraw() override {
Mike Kleind46dce32018-08-16 10:17:03 -040054 this->setBGColor(0xFFDDDDDD);
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +000055 this->setUpRRects();
56 }
57
Leandro Lovisolo24fa2112023-08-15 19:05:17 +000058 SkString getName() const override {
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +000059 SkString name("rrect");
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000060 switch (fType) {
61 case kBW_Draw_Type:
62 name.append("_draw_bw");
63 break;
64 case kAA_Draw_Type:
65 name.append("_draw_aa");
66 break;
67 case kBW_Clip_Type:
68 name.append("_clip_bw");
69 break;
70 case kAA_Clip_Type:
71 name.append("_clip_aa");
72 break;
73 case kEffect_Type:
74 name.append("_effect");
75 break;
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +000076 }
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +000077 return name;
78 }
79
Leandro Lovisolo8f023882023-08-15 21:13:52 +000080 SkISize getISize() override { return SkISize::Make(kImageWidth, kImageHeight); }
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +000081
Chris Dalton50e24d72019-02-07 16:20:09 -070082 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
Kevin Lubickacdc1082023-06-09 11:05:24 -040083 auto sdc = skgpu::ganesh::TopDeviceSurfaceDrawContext(canvas);
Brian Salomon8f7d9532020-12-23 09:16:59 -050084
Robert Phillips7a0d3c32021-07-21 15:39:51 -040085 auto rContext = canvas->recordingContext();
86 if (kEffect_Type == fType && (!sdc || !rContext)) {
Chris Dalton50e24d72019-02-07 16:20:09 -070087 *errorMsg = kErrorMsg_DrawSkippedGpuOnly;
88 return DrawResult::kSkip;
Robert Phillips7c525e62018-06-12 10:11:12 -040089 }
90
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +000091 SkPaint paint;
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000092 if (kAA_Draw_Type == fType) {
93 paint.setAntiAlias(true);
94 }
skia.committer@gmail.comf1f66c02014-03-05 03:02:06 +000095
Chris Daltonfa5a8b82021-07-09 12:17:04 -060096 if (fType == kBW_Clip_Type || fType == kAA_Clip_Type) {
97 // Add a gradient to the paint to ensure local coords are respected.
98 SkPoint pts[3] = {{0, 0}, {1.5f, 1}};
99 SkColor colors[3] = {SK_ColorBLACK, SK_ColorYELLOW};
100 paint.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2,
101 SkTileMode::kClamp));
102 }
103
commit-bot@chromium.orgbfce48e2014-03-10 19:33:16 +0000104#ifdef SK_DEBUG
mtkleindbfd7ab2016-09-01 11:24:54 -0700105 const SkRect kMaxImageBound = SkRect::MakeWH(SkIntToScalar(kImageWidth),
106 SkIntToScalar(kImageHeight));
commit-bot@chromium.orgbfce48e2014-03-10 19:33:16 +0000107#endif
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000108
Ethan Nicholas1706f842017-11-10 11:58:19 -0500109 int lastEdgeType = (kEffect_Type == fType) ? (int) GrClipEdgeType::kLast: 0;
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000110
commit-bot@chromium.orgbfce48e2014-03-10 19:33:16 +0000111 int y = 1;
bsalomon@google.comb0ba39d2014-03-10 19:51:46 +0000112 for (int et = 0; et <= lastEdgeType; ++et) {
commit-bot@chromium.orgbfce48e2014-03-10 19:33:16 +0000113 int x = 1;
114 for (int curRRect = 0; curRRect < kNumRRects; ++curRRect) {
115 bool drew = true;
116#ifdef SK_DEBUG
Michael Ludwig5a4709d2022-03-16 16:38:59 -0400117 if (curRRect != kNumRRects - 1) { // skip last rrect, which is large but clipped
118 SkRect imageSpaceBounds = fRRects[curRRect].getBounds();
119 imageSpaceBounds.offset(SkIntToScalar(x), SkIntToScalar(y));
120 SkASSERT(kMaxImageBound.contains(imageSpaceBounds));
121 }
commit-bot@chromium.orgbfce48e2014-03-10 19:33:16 +0000122#endif
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000123 canvas->save();
124 canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
Michael Ludwig5a4709d2022-03-16 16:38:59 -0400125
126 SkRRect rrect = fRRects[curRRect];
127 if (curRRect == kNumRRects - 1) {
128 canvas->clipRect({0, 0, kTileX - 2, kTileY - 2});
129 canvas->translate(-0.14f * rrect.rect().width(),
130 -0.14f * rrect.rect().height());
131 }
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000132 if (kEffect_Type == fType) {
Michael Ludwig5a4709d2022-03-16 16:38:59 -0400133 fRRects[curRRect].transform(canvas->getLocalToDeviceAs3x3(), &rrect);
134
Ethan Nicholas0f3c7322017-11-09 14:51:17 -0500135 GrClipEdgeType edgeType = (GrClipEdgeType) et;
Robert Phillips7a0d3c32021-07-21 15:39:51 -0400136 const auto& caps = *rContext->priv().caps()->shaderCaps();
John Stiles851b90e2020-06-17 13:53:37 -0400137 auto [success, fp] = GrRRectEffect::Make(/*inputFP=*/nullptr,
138 edgeType, rrect, caps);
139 if (success) {
Brian Salomon82f44312017-01-11 13:42:54 -0500140 GrPaint grPaint;
141 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
John Stiles41d91b62020-07-21 14:39:40 -0400142 grPaint.setCoverageFragmentProcessor(std::move(fp));
Brian Osmancb3d0872018-10-16 15:19:28 -0400143 grPaint.setColor4f({ 0, 0, 0, 1.f });
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000144
145 SkRect bounds = rrect.getBounds();
Michael Ludwig5a4709d2022-03-16 16:38:59 -0400146 bounds.intersect(SkRect::MakeXYWH(x, y, kTileX - 2, kTileY - 2));
Michael Ludwig10940042022-03-18 15:23:06 -0400147 if (et >= (int) GrClipEdgeType::kInverseFillBW) {
Michael Ludwig5a4709d2022-03-16 16:38:59 -0400148 bounds.outset(2.f, 2.f);
149 }
skia.committer@gmail.comf1f66c02014-03-05 03:02:06 +0000150
Kevin Lubickf24283f2023-03-17 16:10:23 -0400151 sdc->addDrawOp(skgpu::ganesh::FillRectOp::MakeNonAARect(
Robert Phillips7a0d3c32021-07-21 15:39:51 -0400152 rContext, std::move(grPaint), SkMatrix::I(), bounds));
commit-bot@chromium.orgbfce48e2014-03-10 19:33:16 +0000153 } else {
154 drew = false;
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000155 }
Chris Daltonfa5a8b82021-07-09 12:17:04 -0600156 } else if (fType == kBW_Clip_Type || fType == kAA_Clip_Type) {
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000157 bool aaClip = (kAA_Clip_Type == fType);
Michael Ludwig5a4709d2022-03-16 16:38:59 -0400158 canvas->clipRRect(rrect, aaClip);
Chris Daltonfa5a8b82021-07-09 12:17:04 -0600159 canvas->setMatrix(SkMatrix::Scale(kImageWidth, kImageHeight));
160 canvas->drawRect(SkRect::MakeWH(1, 1), paint);
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000161 } else {
Michael Ludwig5a4709d2022-03-16 16:38:59 -0400162 canvas->drawRRect(rrect, paint);
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000163 }
Michael Ludwig5a4709d2022-03-16 16:38:59 -0400164
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000165 canvas->restore();
commit-bot@chromium.orgbfce48e2014-03-10 19:33:16 +0000166 if (drew) {
167 x = x + kTileX;
168 if (x > kImageWidth) {
169 x = 1;
170 y += kTileY;
171 }
172 }
173 }
174 if (x != 1) {
175 y += kTileY;
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000176 }
177 }
Chris Dalton50e24d72019-02-07 16:20:09 -0700178 return DrawResult::kOk;
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000179 }
180
181 void setUpRRects() {
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +0000182 // each RRect must fit in a 0x0 -> (kTileX-2)x(kTileY-2) block. These will be tiled across
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000183 // the screen in kTileX x kTileY tiles. The extra empty pixels on each side are for AA.
184
185 // simple cases
186 fRRects[0].setRect(SkRect::MakeWH(kTileX-2, kTileY-2));
187 fRRects[1].setOval(SkRect::MakeWH(kTileX-2, kTileY-2));
188 fRRects[2].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 10, 10);
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000189 fRRects[3].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 10, 5);
190 // small circular corners are an interesting test case for gpu clipping
191 fRRects[4].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 1, 1);
192 fRRects[5].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 0.5f, 0.5f);
193 fRRects[6].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 0.2f, 0.2f);
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000194
195 // The first complex case needs special handling since it is a square
196 fRRects[kNumSimpleCases].setRectRadii(SkRect::MakeWH(kTileY-2, kTileY-2), gRadii[0]);
Herb Derbyc37b3862022-06-21 09:49:17 -0400197 for (size_t i = 1; i < std::size(gRadii); ++i) {
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000198 fRRects[kNumSimpleCases+i].setRectRadii(SkRect::MakeWH(kTileX-2, kTileY-2), gRadii[i]);
199 }
Michael Ludwig5a4709d2022-03-16 16:38:59 -0400200 // The last case is larger than kTileX-2 x kTileY-2 but will be drawn at an offset
201 // into a clip rect that respects the tile size and highlights the rrect's corner curve.
202 fRRects[kNumRRects - 1].setRectXY({9.f, 9.f, 1699.f, 1699.f}, 843.749f, 843.75f);
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000203 }
204
205private:
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000206 Type fType;
skia.committer@gmail.comf1f66c02014-03-05 03:02:06 +0000207
Brian Salomon9fa47cc2021-10-08 18:48:26 -0400208 inline static constexpr int kImageWidth = 640;
209 inline static constexpr int kImageHeight = 480;
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000210
Brian Salomon9fa47cc2021-10-08 18:48:26 -0400211 inline static constexpr int kTileX = 80;
212 inline static constexpr int kTileY = 40;
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000213
Brian Salomon9fa47cc2021-10-08 18:48:26 -0400214 inline static constexpr int kNumSimpleCases = 7;
215 inline static constexpr int kNumComplexCases = 35;
216
robertphillips@google.com5683d422012-12-17 21:58:02 +0000217 static const SkVector gRadii[kNumComplexCases][4];
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000218
Michael Ludwig5a4709d2022-03-16 16:38:59 -0400219 inline static constexpr int kNumRRects = kNumSimpleCases + kNumComplexCases + 1 /* extra big */;
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000220 SkRRect fRRects[kNumRRects];
221
John Stiles7571f9e2020-09-02 22:42:33 -0400222 using INHERITED = GM;
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000223};
224
225// Radii for the various test cases. Order is UL, UR, LR, LL
226const SkVector RRectGM::gRadii[kNumComplexCases][4] = {
227 // a circle
228 { { kTileY, kTileY }, { kTileY, kTileY }, { kTileY, kTileY }, { kTileY, kTileY } },
229
230 // odd ball cases
231 { { 8, 8 }, { 32, 32 }, { 8, 8 }, { 32, 32 } },
232 { { 16, 8 }, { 8, 16 }, { 16, 8 }, { 8, 16 } },
233 { { 0, 0 }, { 16, 16 }, { 8, 8 }, { 32, 32 } },
234
235 // UL
236 { { 30, 30 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
237 { { 30, 15 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
238 { { 15, 30 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
239
240 // UR
241 { { 0, 0 }, { 30, 30 }, { 0, 0 }, { 0, 0 } },
242 { { 0, 0 }, { 30, 15 }, { 0, 0 }, { 0, 0 } },
243 { { 0, 0 }, { 15, 30 }, { 0, 0 }, { 0, 0 } },
244
245 // LR
246 { { 0, 0 }, { 0, 0 }, { 30, 30 }, { 0, 0 } },
247 { { 0, 0 }, { 0, 0 }, { 30, 15 }, { 0, 0 } },
248 { { 0, 0 }, { 0, 0 }, { 15, 30 }, { 0, 0 } },
249
250 // LL
251 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 30, 30 } },
252 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 30, 15 } },
253 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 15, 30 } },
254
255 // over-sized radii
256 { { 0, 0 }, { 100, 400 }, { 0, 0 }, { 0, 0 } },
257 { { 0, 0 }, { 400, 400 }, { 0, 0 }, { 0, 0 } },
258 { { 400, 400 }, { 400, 400 }, { 400, 400 }, { 400, 400 } },
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000259
260 // circular corner tabs
261 { { 0, 0 }, { 20, 20 }, { 20, 20 }, { 0, 0 } },
262 { { 20, 20 }, { 20, 20 }, { 0, 0 }, { 0, 0 } },
263 { { 0, 0 }, { 0, 0 }, { 20, 20 }, { 20, 20 } },
264 { { 20, 20 }, { 0, 0 }, { 0, 0 }, { 20, 20 } },
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000265
commit-bot@chromium.org04eff722014-03-24 14:53:09 +0000266 // small radius circular corner tabs
267 { { 0, 0 }, { 0.2f, 0.2f }, { 0.2f, 0.2f }, { 0, 0 } },
268 { { 0.3f, 0.3f }, { 0.3f, .3f }, { 0, 0 }, { 0, 0 } },
269
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000270 // single circular corner cases
271 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 15, 15 } },
272 { { 0, 0 }, { 0, 0 }, { 15, 15 }, { 0, 0 } },
273 { { 0, 0 }, { 15, 15 }, { 0, 0 }, { 0, 0 } },
274 { { 15, 15 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
commit-bot@chromium.orgfa5edbe2014-03-13 18:01:05 +0000275
276 // nine patch elliptical
277 { { 5, 7 }, { 8, 7 }, { 8, 12 }, { 5, 12 } },
278 { { 0, 7 }, { 8, 7 }, { 8, 12 }, { 0, 12 } },
commit-bot@chromium.org04eff722014-03-24 14:53:09 +0000279
280 // nine patch elliptical, small radii
281 { { 0.4f, 7 }, { 8, 7 }, { 8, 12 }, { 0.4f, 12 } },
282 { { 0.4f, 0.4f }, { 8, 0.4f }, { 8, 12 }, { 0.4f, 12 } },
283 { { 20, 0.4f }, { 18, 0.4f }, { 18, 0.4f }, { 20, 0.4f } },
284 { { 0.3f, 0.4f }, { 0.3f, 0.4f }, { 0.3f, 0.4f }, { 0.3f, 0.4f } },
285
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000286};
287
288///////////////////////////////////////////////////////////////////////////////
289
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000290DEF_GM( return new RRectGM(RRectGM::kAA_Draw_Type); )
291DEF_GM( return new RRectGM(RRectGM::kBW_Draw_Type); )
292DEF_GM( return new RRectGM(RRectGM::kAA_Clip_Type); )
293DEF_GM( return new RRectGM(RRectGM::kBW_Clip_Type); )
294DEF_GM( return new RRectGM(RRectGM::kEffect_Type); )
robertphillips@google.com4e18c7a2012-12-17 21:48:19 +0000295
Michael Ludwiga82a7b62022-11-28 22:38:51 -0500296// This GM is designed to test a variety of fill and stroked rectangles and round rectangles, with
297// different stroke width and join type scenarios. The geometry parameters are chosen so that
298// Graphite should be able to use its AnalyticRoundRectRenderStep and batch into a single draw.
Michael Ludwig182d5da2023-02-09 16:16:27 -0500299DEF_SIMPLE_GM(stroke_rect_rrects, canvas, 1350, 700) {
Michael Ludwiga82a7b62022-11-28 22:38:51 -0500300 canvas->scale(0.5f, 0.5f);
301 canvas->translate(50.f, 50.f);
302
303 auto draw = [&](int cx, int cy, bool rrect, float width, SkPaint::Join join) {
304 SkPaint p;
305 p.setAntiAlias(true);
306 p.setStrokeWidth(width);
307 p.setStyle(width >= 0.f ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
308 p.setStrokeJoin(join);
309
310 canvas->save();
311 canvas->translate(cx * 110.f, cy * 110.f);
312 float dx = cx % 2 ? 0.5f : 0.f;
313 float dy = cy % 2 ? 0.5f : 0.f;
314 SkRect rect = SkRect::MakeWH(50.f, 40.f);
315 rect.offset(dx, dy);
316
317 if (width < 0.0) {
318 rect.outset(25.f, 25.f); // make it the same size as the largest stroke
319 }
320
321 // Filled rounded rects can have arbitrary corners
322 float cornerScale = std::min(rect.width(), rect.height());
323 SkVector outerRadii[4] = { { 0.25f * cornerScale, 0.75f * cornerScale },
324 { 0.f, 0.f},
325 { 0.50f * cornerScale, 0.50f * cornerScale },
326 { 0.75f * cornerScale, 0.25f * cornerScale } };
327 // Stroked rounded rects will only have circular corners so that they remain compatible with
328 // Graphite's AnalyticRoundRectRenderStep's requirements.
329 SkVector strokeRadii[4] = { { 0.25f * cornerScale, 0.25f * cornerScale },
330 { 0.f, 0.f }, // this corner matches join type
331 { 0.50f * cornerScale, 0.50f * cornerScale },
332 { 0.75f * cornerScale, 0.75f * cornerScale } };
333
334 if (rrect) {
335 SkRRect r;
336 if (width >= 0.0) {
337 r.setRectRadii(rect, strokeRadii);
338 } else {
339 r.setRectRadii(rect, outerRadii);
340 }
341 canvas->drawRRect(r, p);
342 } else {
343 canvas->drawRect(rect, p);
344 }
345 canvas->restore();
346 };
347
348 // The stroke widths are chosen to test when the inner stroke edges have completely crossed
349 // over (50); when the inner corner circles intersect each other (30); a typical "nice"
350 // stroke (10); a skinny stroke (1); and a hairline (0).
351 int i = 0;
352 for (float width : {-1.f, 50.f, 30.f, 10.f, 1.f, 0.f}) {
353 int j = 0;
354 for (SkPaint::Join join : { SkPaint::kMiter_Join,
355 SkPaint::kBevel_Join,
356 SkPaint::kRound_Join }) {
357 if (width < 0 && join != SkPaint::kMiter_Join) {
358 continue; // Don't repeat fills, since join type is ignored
359 }
360 draw(2*i, 2*j, false, width, join);
361 draw(2*i+1, 2*j, false, width, join);
362 draw(2*i, 2*j+1, false, width, join);
363 draw(2*i+1, 2*j+1, false, width, join);
364 j++;
365 }
366 i++;
367 }
368
369 canvas->translate(0.f, 50.f);
370
371 i = 0;
372 for (float width : {-1.f, 50.f, 30.f, 10.f, 1.f, 0.f}) {
373 int j = 3;
374 for (SkPaint::Join join : { SkPaint::kMiter_Join,
375 SkPaint::kBevel_Join,
376 SkPaint::kRound_Join }) {
377 if (width < 0 && join != SkPaint::kMiter_Join) {
378 continue;
379 }
380 draw(2*i, 2*j, true, width, join);
381 draw(2*i+1, 2*j, true, width, join);
382 draw(2*i, 2*j+1, true, width, join);
383 draw(2*i+1, 2*j+1, true, width, join);
384 j++;
385 }
386 i++;
387 }
Michael Ludwig182d5da2023-02-09 16:16:27 -0500388
389 // Rotated "footballs"
390 auto drawComplex = [&](int cx, int cy, float width, float stretch) {
391 SkPaint p;
392 p.setAntiAlias(true);
393 p.setStrokeWidth(width);
394 p.setStyle(SkPaint::kStroke_Style);
395 p.setStrokeJoin(SkPaint::kBevel_Join);
396
397 canvas->save();
398 canvas->translate(cx * 110.f, cy * 110.f);
399
400 SkRect rect = SkRect::MakeWH(cx % 2 ? 50.f : (40.f + stretch),
401 cx % 2 ? (40.f + stretch) : 50.f);
402 const SkVector kBigCorner{30.f, 30.f};
403 const SkVector kRectCorner{0.f, 0.f};
404
405 SkVector strokeRadii[4] = { cy % 2 ? kRectCorner : kBigCorner,
406 cy % 2 ? kBigCorner : kRectCorner,
407 cy % 2 ? kRectCorner : kBigCorner,
408 cy % 2 ? kBigCorner : kRectCorner };
409
410 SkRRect r;
411 r.setRectRadii(rect, strokeRadii);
412 canvas->drawRRect(r, p);
413
414 canvas->restore();
415 };
416
417 canvas->translate(0.f, -50.f);
418 i = 6;
419 for (float width : {50.f, 30.f, 20.f, 10.f, 1.f, 0.f}) {
420 int j = 0;
421 for (float stretch: {0.f, 5.f, 10.f}) {
422 drawComplex(2*i, 2*j, width, stretch);
423 drawComplex(2*i+1, 2*j, width, stretch);
424 drawComplex(2*i, 2*j+1, width, stretch);
425 drawComplex(2*i+1, 2*j+1, width, stretch);
426 j++;
427 }
428 i++;
429 }
430
431 // Rotated "D"s
432 auto drawComplex2 = [&](int cx, int cy, float width, float stretch) {
433 SkPaint p;
434 p.setAntiAlias(true);
435 p.setStrokeWidth(width);
436 p.setStyle(SkPaint::kStroke_Style);
437 p.setStrokeJoin(SkPaint::kMiter_Join);
438
439 canvas->save();
440 canvas->translate(cx * 110.f, cy * 110.f);
441
442 SkRect rect = SkRect::MakeWH(cx % 2 ? 50.f : (40.f + stretch),
443 cx % 2 ? (40.f + stretch) : 50.f);
444 const SkVector kBigCorner{30.f, 30.f};
445 const SkVector kRectCorner{0.f, 0.f};
446
447 SkVector strokeRadii[4] = { cx % 2 ? kRectCorner : kBigCorner,
448 (cx % 2) ^ (cy % 2) ? kBigCorner : kRectCorner,
449 cx % 2 ? kBigCorner : kRectCorner,
450 (cx % 2) ^ (cy % 2) ? kRectCorner : kBigCorner };
451
452 SkRRect r;
453 r.setRectRadii(rect, strokeRadii);
454 canvas->drawRRect(r, p);
455
456 canvas->restore();
457 };
458
459 canvas->translate(0.f, 50.f);
460 i = 6;
461 for (float width : {50.f, 30.f, 20.f, 10.f, 1.f, 0.f}) {
462 int j = 3;
463 for (float stretch: {0.f, 5.f, 10.f}) {
464 drawComplex2(2*i, 2*j, width, stretch);
465 drawComplex2(2*i+1, 2*j, width, stretch);
466 drawComplex2(2*i, 2*j+1, width, stretch);
467 drawComplex2(2*i+1, 2*j+1, width, stretch);
468 j++;
469 }
470 i++;
471 }
Michael Ludwiga82a7b62022-11-28 22:38:51 -0500472}
473
John Stilesa6841be2020-08-06 14:11:56 -0400474} // namespace skiagm