Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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 enum {
35 kNumAngles = 15,
36 kRadius = 40,
37 };
38 for (int i = 0; i < kNumAngles; ++i) {
39 SkScalar angle = SK_ScalarPI * SkIntToScalar(i) / kNumAngles;
40 SkScalar x = kRadius * SkScalarCos(angle);
41 SkScalar y = kRadius * SkScalarSin(angle);
42 lineAngles.moveTo(x, y).lineTo(-x, -y);
43 }
44 fPaths.push_back(lineAngles.detach());
45 }
46
47 fPaths.push_back(SkPathBuilder().moveTo(0, -10)
48 .quadTo(100, 100, -10, 0)
49 .detach());
50
51 fPaths.push_back(SkPathBuilder().moveTo(0, -5)
52 .quadTo(100, 100, -5, 0)
53 .detach());
54
55 fPaths.push_back(SkPathBuilder().moveTo(0, -2)
56 .quadTo(100, 100, -2, 0)
57 .detach());
58
59 fPaths.push_back(SkPathBuilder().moveTo(0, -1)
60 .quadTo(100, 100, -2 + 306.0f / 4, 75)
61 .detach());
62
63 fPaths.push_back(SkPathBuilder().moveTo(0, -1)
64 .quadTo(100, 100, -1, 0)
65 .detach());
66
67 fPaths.push_back(SkPathBuilder().moveTo(0, -0)
68 .quadTo(100, 100, 0, 0)
69 .detach());
70
71 fPaths.push_back(SkPathBuilder().moveTo(0, -0)
72 .quadTo(100, 100, 75, 75)
73 .detach());
74
75 // Two problem cases for gpu hairline renderer found by shapeops testing. These used
76 // to assert that the computed bounding box didn't contain all the vertices.
77
78 fPaths.push_back(SkPathBuilder().moveTo(4, 6)
79 .cubicTo(5, 6, 5, 4, 4, 0)
80 .close()
81 .detach());
82
83 fPaths.push_back(SkPathBuilder().moveTo(5, 1)
84 .lineTo( 4.32787323f, 1.67212653f)
85 .cubicTo(2.75223875f, 3.24776125f,
86 3.00581908f, 4.51236057f,
87 3.7580452f, 4.37367964f)
88 .cubicTo(4.66472578f, 3.888381f,
89 5.f, 2.875f,
90 5.f, 1.f)
91 .close()
92 .detach());
93
94 // Three paths that show the same bug (missing end caps)
95
96 fPaths.push_back(SkPathBuilder().moveTo(6.5f,5.5f)
97 .lineTo(3.5f,0.5f)
98 .moveTo(0.5f,5.5f)
99 .lineTo(3.5f,0.5f)
100 .detach());
101
102 // An X (crbug.com/137317)
103 fPaths.push_back(SkPathBuilder().moveTo(1, 1)
104 .lineTo(6, 6)
105 .moveTo(1, 6)
106 .lineTo(6, 1)
107 .detach());
108
109 // A right angle (crbug.com/137465 and crbug.com/256776)
110 fPaths.push_back(SkPathBuilder().moveTo(5.5f, 5.5f)
111 .lineTo(5.5f, 0.5f)
112 .lineTo(0.5f, 0.5f)
113 .detach());
114
115 {
116 // Arc example to test imperfect truncation bug (crbug.com/295626)
117 constexpr SkScalar kRad = SkIntToScalar(2000);
118 constexpr SkScalar kStartAngle = 262.59717f;
119 constexpr SkScalar kSweepAngle = SkScalarHalf(17.188717f);
120
121 SkPathBuilder bug;
122
123 // Add a circular arc
124 SkRect circle = SkRect::MakeLTRB(-kRad, -kRad, kRad, kRad);
125 bug.addArc(circle, kStartAngle, kSweepAngle);
126
127 // Now add the chord that should cap the circular arc
128 SkPoint p0 = { kRad * SkScalarCos(SkDegreesToRadians(kStartAngle)),
129 kRad * SkScalarSin(SkDegreesToRadians(kStartAngle)) };
130
131 SkPoint p1 = { kRad * SkScalarCos(SkDegreesToRadians(kStartAngle + kSweepAngle)),
132 kRad * SkScalarSin(SkDegreesToRadians(kStartAngle + kSweepAngle)) };
133
134 bug.moveTo(p0);
135 bug.lineTo(p1);
136 fPaths.push_back(bug.detach());
137 }
138 }
139
140 void onDraw(SkCanvas* canvas) override {
141 constexpr SkAlpha kAlphaValue[] = { 0xFF, 0x40 };
142 constexpr SkScalar kWidths[] = { 0, 0.5f, 1.5f };
143
144 enum {
145 kMargin = 5,
146 };
147 int wrapX = 1250 - kMargin;
148
149 SkScalar maxH = 0;
151 canvas->save();
152
154 for (int p = 0; p < fPaths.size(); ++p) {
155 for (size_t a = 0; a < std::size(kAlphaValue); ++a) {
156 for (int aa = 0; aa < 2; ++aa) {
157 for (size_t w = 0; w < std::size(kWidths); w++) {
158 const SkRect& bounds = fPaths[p].getBounds();
159
160 if (x + bounds.width() > wrapX) {
161 canvas->restore();
162 canvas->translate(0, maxH + SkIntToScalar(kMargin));
163 canvas->save();
164 maxH = 0;
166 }
167
169 paint.setARGB(kAlphaValue[a], 0, 0, 0);
170 paint.setAntiAlias(SkToBool(aa));
172 paint.setStrokeWidth(kWidths[w]);
173
174 canvas->save();
175 canvas->translate(-bounds.fLeft, -bounds.fTop);
176 canvas->drawPath(fPaths[p], paint);
177 canvas->restore();
178
179 maxH = std::max(maxH, bounds.height());
180
181 SkScalar dx = bounds.width() + SkIntToScalar(kMargin);
182 x += dx;
183 canvas->translate(dx, 0);
184 }
185 }
186 }
187 }
188 canvas->restore();
189 }
190
191private:
192 TArray<SkPath> fPaths;
193 using INHERITED = GM;
194};
195
196static void draw_squarehair_tests(SkCanvas* canvas, SkScalar width, SkPaint::Cap cap, bool aa) {
198 paint.setStrokeCap(cap);
199 paint.setStrokeWidth(width);
200 paint.setAntiAlias(aa);
202 canvas->drawLine(10, 10, 20, 10, paint);
203 canvas->drawLine(30, 10, 30, 20, paint);
204 canvas->drawLine(40, 10, 50, 20, paint);
205 SkPathBuilder path;
206 path.moveTo(60, 10);
207 path.quadTo(60, 20, 70, 20);
208 path.conicTo(70, 10, 80, 10, 0.707f);
209 canvas->drawPath(path.detach(), paint);
210
211 path.moveTo(90, 10);
212 path.cubicTo(90, 20, 100, 20, 100, 10);
213 path.lineTo(110, 10);
214 canvas->drawPath(path.detach(), paint);
215 canvas->translate(0, 30);
216}
217
218DEF_SIMPLE_GM(squarehair, canvas, 240, 360) {
219 const bool aliases[] = { false, true };
220 const SkScalar widths[] = { 0, 0.999f, 1, 1.001f };
222 for (auto alias : aliases) {
223 canvas->save();
224 for (auto width : widths) {
225 for (auto cap : caps) {
226 draw_squarehair_tests(canvas, width, cap, alias);
227 }
228 }
229 canvas->restore();
230 canvas->translate(120, 0);
231 }
232}
233
234// GM to test subdivision of hairlines
235static void draw_subdivided_quad(SkCanvas* canvas, int x0, int y0, int x1, int y1, SkColor color) {
237 paint.setStrokeWidth(1);
238 paint.setAntiAlias(true);
240 paint.setColor(color);
241
242 canvas->drawPath(SkPathBuilder().moveTo(0,0)
243 .quadTo(SkIntToScalar(x0), SkIntToScalar(y0),
245 .detach(),
246 paint);
247}
248
249DEF_SIMPLE_GM(hairline_subdiv, canvas, 512, 256) {
250 // no subdivisions
251 canvas->translate(45, -25);
252 draw_subdivided_quad(canvas, 334, 334, 467, 267, SK_ColorBLACK);
253
254 // one subdivision
255 canvas->translate(-185, -150);
256 draw_subdivided_quad(canvas, 472, 472, 660, 378, SK_ColorRED);
257
258 // two subdivisions
259 canvas->translate(-275, -200);
260 draw_subdivided_quad(canvas, 668, 668, 934, 535, SK_ColorGREEN);
261
262 // three subdivisions
263 canvas->translate(-385, -260);
264 draw_subdivided_quad(canvas, 944, 944, 1320, 756, SK_ColorBLUE);
265}
266
267//////////////////////////////////////////////////////////////////////////////
268
269DEF_GM( return new HairlinesGM; )
270
271} // namespace skiagm
SkColor4f color
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[]
@ kMargin
void restore()
Definition SkCanvas.cpp:465
void translate(SkScalar dx, SkScalar dy)
void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint &paint)
int save()
Definition SkCanvas.cpp:451
void drawPath(const SkPath &path, const SkPaint &paint)
@ 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:416
SkISize getISize() override
Definition hairlines.cpp:29
void onDraw(SkCanvas *canvas) override
void onOnceBeforeDraw() override
Definition hairlines.cpp:31
SkString getName() const override
Definition hairlines.cpp:27
const Paint & paint
float SkScalar
Definition extension.cpp:12
struct MyStruct a[10]
#define DEF_GM(CODE)
Definition gm.h:40
#define DEF_SIMPLE_GM(NAME, CANVAS, W, H)
Definition gm.h:50
double y
double x
static void draw_subdivided_quad(SkCanvas *canvas, int x0, int y0, int x1, int y1, SkColor color)
static void draw_squarehair_tests(SkCanvas *canvas, SkScalar width, SkPaint::Cap cap, bool aa)
static const SkScalar kWidths[]
SkScalar w
int32_t width
constexpr int kRadius
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