Flutter Engine
The Flutter Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
hairlines.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2013 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
8#include "gm/gm.h"
14#include "include/core/SkRect.h"
16#include "include/core/SkSize.h"
20
21using namespace skia_private;
22
23namespace skiagm {
24
25class HairlinesGM : public GM {
26protected:
27 SkString getName() const override { return SkString("hairlines"); }
28
29 SkISize getISize() override { return SkISize::Make(1250, 1250); }
30
31 void onOnceBeforeDraw() override {
32 {
33 SkPathBuilder lineAngles;
34 constexpr int kNumAngles = 15;
35 constexpr int kRadius = 40;
36
37 for (int i = 0; i < kNumAngles; ++i) {
38 SkScalar angle = SK_ScalarPI * SkIntToScalar(i) / kNumAngles;
39 SkScalar x = kRadius * SkScalarCos(angle);
40 SkScalar y = kRadius * SkScalarSin(angle);
41 lineAngles.moveTo(x, y).lineTo(-x, -y);
42 }
43 fPaths.push_back(lineAngles.detach());
44 }
45
46 fPaths.push_back(SkPathBuilder().moveTo(0, -10)
47 .quadTo(100, 100, -10, 0)
48 .detach());
49
50 fPaths.push_back(SkPathBuilder().moveTo(0, -5)
51 .quadTo(100, 100, -5, 0)
52 .detach());
53
54 fPaths.push_back(SkPathBuilder().moveTo(0, -2)
55 .quadTo(100, 100, -2, 0)
56 .detach());
57
58 fPaths.push_back(SkPathBuilder().moveTo(0, -1)
59 .quadTo(100, 100, -2 + 306.0f / 4, 75)
60 .detach());
61
62 fPaths.push_back(SkPathBuilder().moveTo(0, -1)
63 .quadTo(100, 100, -1, 0)
64 .detach());
65
66 fPaths.push_back(SkPathBuilder().moveTo(0, -0)
67 .quadTo(100, 100, 0, 0)
68 .detach());
69
70 fPaths.push_back(SkPathBuilder().moveTo(0, -0)
71 .quadTo(100, 100, 75, 75)
72 .detach());
73
74 // Two problem cases for gpu hairline renderer found by shapeops testing. These used
75 // to assert that the computed bounding box didn't contain all the vertices.
76
77 fPaths.push_back(SkPathBuilder().moveTo(4, 6)
78 .cubicTo(5, 6, 5, 4, 4, 0)
79 .close()
80 .detach());
81
82 fPaths.push_back(SkPathBuilder().moveTo(5, 1)
83 .lineTo( 4.32787323f, 1.67212653f)
84 .cubicTo(2.75223875f, 3.24776125f,
85 3.00581908f, 4.51236057f,
86 3.7580452f, 4.37367964f)
87 .cubicTo(4.66472578f, 3.888381f,
88 5.f, 2.875f,
89 5.f, 1.f)
90 .close()
91 .detach());
92
93 // Three paths that show the same bug (missing end caps)
94
95 fPaths.push_back(SkPathBuilder().moveTo(6.5f,5.5f)
96 .lineTo(3.5f,0.5f)
97 .moveTo(0.5f,5.5f)
98 .lineTo(3.5f,0.5f)
99 .detach());
100
101 // An X (crbug.com/137317)
102 fPaths.push_back(SkPathBuilder().moveTo(1, 1)
103 .lineTo(6, 6)
104 .moveTo(1, 6)
105 .lineTo(6, 1)
106 .detach());
107
108 // A right angle (crbug.com/137465 and crbug.com/256776)
109 fPaths.push_back(SkPathBuilder().moveTo(5.5f, 5.5f)
110 .lineTo(5.5f, 0.5f)
111 .lineTo(0.5f, 0.5f)
112 .detach());
113
114 {
115 // Arc example to test imperfect truncation bug (crbug.com/295626)
116 constexpr SkScalar kRad = SkIntToScalar(2000);
117 constexpr SkScalar kStartAngle = 262.59717f;
118 constexpr SkScalar kSweepAngle = SkScalarHalf(17.188717f);
119
120 SkPathBuilder bug;
121
122 // Add a circular arc
123 SkRect circle = SkRect::MakeLTRB(-kRad, -kRad, kRad, kRad);
124 bug.addArc(circle, kStartAngle, kSweepAngle);
125
126 // Now add the chord that should cap the circular arc
127 SkPoint p0 = { kRad * SkScalarCos(SkDegreesToRadians(kStartAngle)),
128 kRad * SkScalarSin(SkDegreesToRadians(kStartAngle)) };
129
130 SkPoint p1 = { kRad * SkScalarCos(SkDegreesToRadians(kStartAngle + kSweepAngle)),
131 kRad * SkScalarSin(SkDegreesToRadians(kStartAngle + kSweepAngle)) };
132
133 bug.moveTo(p0);
134 bug.lineTo(p1);
135 fPaths.push_back(bug.detach());
136 }
137 }
138
139 void onDraw(SkCanvas* canvas) override {
140 constexpr SkAlpha kAlphaValue[] = { 0xFF, 0x40 };
141 constexpr SkScalar kWidths[] = { 0, 0.5f, 1.5f };
142
143 enum {
144 kMargin = 5,
145 };
146 int wrapX = 1250 - kMargin;
147
148 SkScalar maxH = 0;
150 canvas->save();
151
153 for (int p = 0; p < fPaths.size(); ++p) {
154 for (size_t a = 0; a < std::size(kAlphaValue); ++a) {
155 for (int aa = 0; aa < 2; ++aa) {
156 for (size_t w = 0; w < std::size(kWidths); w++) {
157 const SkRect& bounds = fPaths[p].getBounds();
158
159 if (x + bounds.width() > wrapX) {
160 canvas->restore();
161 canvas->translate(0, maxH + SkIntToScalar(kMargin));
162 canvas->save();
163 maxH = 0;
165 }
166
168 paint.setARGB(kAlphaValue[a], 0, 0, 0);
169 paint.setAntiAlias(SkToBool(aa));
171 paint.setStrokeWidth(kWidths[w]);
172
173 canvas->save();
174 canvas->translate(-bounds.fLeft, -bounds.fTop);
175 canvas->drawPath(fPaths[p], paint);
176 canvas->restore();
177
178 maxH = std::max(maxH, bounds.height());
179
181 x += dx;
182 canvas->translate(dx, 0);
183 }
184 }
185 }
186 }
187 canvas->restore();
188 }
189
190private:
191 TArray<SkPath> fPaths;
192 using INHERITED = GM;
193};
194
195static void draw_squarehair_tests(SkCanvas* canvas, SkScalar width, SkPaint::Cap cap, bool aa) {
197 paint.setStrokeCap(cap);
198 paint.setStrokeWidth(width);
199 paint.setAntiAlias(aa);
201 canvas->drawLine(10, 10, 20, 10, paint);
202 canvas->drawLine(30, 10, 30, 20, paint);
203 canvas->drawLine(40, 10, 50, 20, paint);
205 path.moveTo(60, 10);
206 path.quadTo(60, 20, 70, 20);
207 path.conicTo(70, 10, 80, 10, 0.707f);
208 canvas->drawPath(path.detach(), paint);
209
210 path.moveTo(90, 10);
211 path.cubicTo(90, 20, 100, 20, 100, 10);
212 path.lineTo(110, 10);
213 canvas->drawPath(path.detach(), paint);
214 canvas->translate(0, 30);
215}
216
217DEF_SIMPLE_GM(squarehair, canvas, 240, 360) {
218 const bool aliases[] = { false, true };
219 const SkScalar widths[] = { 0, 0.999f, 1, 1.001f };
221 for (auto alias : aliases) {
222 canvas->save();
223 for (auto width : widths) {
224 for (auto cap : caps) {
225 draw_squarehair_tests(canvas, width, cap, alias);
226 }
227 }
228 canvas->restore();
229 canvas->translate(120, 0);
230 }
231}
232
233// GM to test subdivision of hairlines
234static void draw_subdivided_quad(SkCanvas* canvas, int x0, int y0, int x1, int y1, SkColor color) {
236 paint.setStrokeWidth(1);
237 paint.setAntiAlias(true);
239 paint.setColor(color);
240
241 canvas->drawPath(SkPathBuilder().moveTo(0,0)
242 .quadTo(SkIntToScalar(x0), SkIntToScalar(y0),
244 .detach(),
245 paint);
246}
247
248DEF_SIMPLE_GM(hairline_subdiv, canvas, 512, 256) {
249 // no subdivisions
250 canvas->translate(45, -25);
251 draw_subdivided_quad(canvas, 334, 334, 467, 267, SK_ColorBLACK);
252
253 // one subdivision
254 canvas->translate(-185, -150);
255 draw_subdivided_quad(canvas, 472, 472, 660, 378, SK_ColorRED);
256
257 // two subdivisions
258 canvas->translate(-275, -200);
259 draw_subdivided_quad(canvas, 668, 668, 934, 535, SK_ColorGREEN);
260
261 // three subdivisions
262 canvas->translate(-385, -260);
263 draw_subdivided_quad(canvas, 944, 944, 1320, 756, SK_ColorBLUE);
264}
265
266//////////////////////////////////////////////////////////////////////////////
267
268DEF_GM( return new HairlinesGM; )
269
270} // namespace skiagm
uint32_t SkColor
Definition: SkColor.h:37
uint8_t SkAlpha
Definition: SkColor.h:26
constexpr SkColor SK_ColorBLUE
Definition: SkColor.h:135
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
constexpr SkColor SK_ColorGREEN
Definition: SkColor.h:131
#define SkDegreesToRadians(degrees)
Definition: SkScalar.h:77
#define SkScalarSin(radians)
Definition: SkScalar.h:45
#define SkScalarHalf(a)
Definition: SkScalar.h:75
#define SkIntToScalar(x)
Definition: SkScalar.h:57
#define SkScalarCos(radians)
Definition: SkScalar.h:46
#define SK_ScalarPI
Definition: SkScalar.h:21
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
const SkScalar widths[]
Definition: StrokerTest.cpp:39
constexpr int kMargin
Definition: aaxfermodes.cpp:31
void restore()
Definition: SkCanvas.cpp:461
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint &paint)
Definition: SkCanvas.cpp:2700
int save()
Definition: SkCanvas.cpp:447
void drawPath(const SkPath &path, const SkPaint &paint)
Definition: SkCanvas.cpp:1747
@ kRound_Cap
adds circle
Definition: SkPaint.h:335
@ kButt_Cap
no stroke extension
Definition: SkPaint.h:334
@ kSquare_Cap
adds square
Definition: SkPaint.h:336
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
SkPathBuilder & lineTo(SkPoint pt)
SkPathBuilder & addArc(const SkRect &oval, SkScalar startAngleDeg, SkScalar sweepAngleDeg)
SkPathBuilder & moveTo(SkPoint pt)
int size() const
Definition: SkTArray.h:421
Definition: gm.h:110
GM(SkColor backgroundColor=SK_ColorWHITE)
Definition: gm.cpp:81
SkISize getISize() override
Definition: hairlines.cpp:29
void onDraw(SkCanvas *canvas) override
Definition: hairlines.cpp:139
void onOnceBeforeDraw() override
Definition: hairlines.cpp:31
SkString getName() const override
Definition: hairlines.cpp:27
const Paint & paint
Definition: color_source.cc:38
DlColor color
float SkScalar
Definition: extension.cpp:12
struct MyStruct a[10]
static float max(float r, float g, float b)
Definition: hsl.cpp:49
double y
double x
Optional< SkRect > bounds
Definition: SkRecords.h:189
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
DEF_GM(return F(C(clipbox), 0.0f, 0.0f, {})) DEF_GM(return F(C(clipbox)
DEF_SIMPLE_GM(hugebitmapshader, canvas, 100, 100)
static void draw_subdivided_quad(SkCanvas *canvas, int x0, int y0, int x1, int y1, SkColor color)
Definition: hairlines.cpp:234
static void draw_squarehair_tests(SkCanvas *canvas, SkScalar width, SkPaint::Cap cap, bool aa)
Definition: hairlines.cpp:195
static const SkScalar kWidths[]
SkScalar w
int32_t width
constexpr int kRadius
Definition: SkSize.h:16
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646