Flutter Engine
The Flutter Engine
arcto.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "gm/gm.h"
15#include "include/core/SkRect.h"
21#include "src/base/SkRandom.h"
22#include "src/core/SkOSFile.h"
23
24#include <stdio.h>
25
26/* The test below generates a reference image using SVG. To compare the result for correctness,
27 enable the define below and then view the generated SVG in a browser.
28 */
29static constexpr bool GENERATE_SVG_REFERENCE = false;
30
31/*
32The arcto test below should draw the same as this SVG:
33(Note that Skia's arcTo Direction parameter value is opposite SVG's sweep value, e.g. 0 / 1)
34
35<svg width="500" height="600">
36<path d="M 50,100 A50,50, 0,0,1, 150,200" style="stroke:#660000; fill:none; stroke-width:2" />
37<path d="M100,100 A50,100, 0,0,1, 200,200" style="stroke:#660000; fill:none; stroke-width:2" />
38<path d="M150,100 A50,50, 45,0,1, 250,200" style="stroke:#660000; fill:none; stroke-width:2" />
39<path d="M200,100 A50,100, 45,0,1, 300,200" style="stroke:#660000; fill:none; stroke-width:2" />
40
41<path d="M150,200 A50,50, 0,1,0, 150,300" style="stroke:#660000; fill:none; stroke-width:2" />
42<path d="M200,200 A50,100, 0,1,0, 200,300" style="stroke:#660000; fill:none; stroke-width:2" />
43<path d="M250,200 A50,50, 45,1,0, 250,300" style="stroke:#660000; fill:none; stroke-width:2" />
44<path d="M300,200 A50,100, 45,1,0, 300,300" style="stroke:#660000; fill:none; stroke-width:2" />
45
46<path d="M250,400 A120,80 0 0,0 250,500"
47 fill="none" stroke="red" stroke-width="5" />
48
49<path d="M250,400 A120,80 0 1,1 250,500"
50 fill="none" stroke="green" stroke-width="5"/>
51
52<path d="M250,400 A120,80 0 1,0 250,500"
53 fill="none" stroke="purple" stroke-width="5"/>
54
55<path d="M250,400 A120,80 0 0,1 250,500"
56 fill="none" stroke="blue" stroke-width="5"/>
57
58<path d="M100,100 A 0, 0 0 0,1 200,200"
59 fill="none" stroke="blue" stroke-width="5" stroke-linecap="round"/>
60
61<path d="M200,100 A 80,80 0 0,1 200,100"
62 fill="none" stroke="blue" stroke-width="5" stroke-linecap="round"/>
63</svg>
64 */
65
66DEF_SIMPLE_GM(arcto, canvas, 500, 600) {
68 paint.setAntiAlias(true);
70 paint.setStrokeWidth(2);
71 paint.setColor(0xFF660000);
72// canvas->scale(2, 2); // for testing on retina
73 SkRect oval = SkRect::MakeXYWH(100, 100, 100, 100);
74
75 for (SkScalar angle = 0; angle <= 45; angle += 45) {
76 for (int oHeight = 2; oHeight >= 1; --oHeight) {
77 SkPathBuilder svgArc;
78 SkScalar ovalHeight = oval.height() / oHeight;
79 svgArc.moveTo(oval.fLeft, oval.fTop);
80 svgArc.arcTo({oval.width() / 2, ovalHeight}, angle, SkPathBuilder::kSmall_ArcSize,
82 canvas->drawPath(svgArc.detach(), paint);
83
84 svgArc.moveTo(oval.fLeft + 100, oval.fTop + 100);
85 svgArc.arcTo({oval.width() / 2, ovalHeight}, angle, SkPathBuilder::kLarge_ArcSize,
87 canvas->drawPath(svgArc.detach(), paint);
88 oval.offset(50, 0);
89
90 }
91 }
92
93 paint.setStrokeWidth(5);
94 const SkColor purple = 0xFF800080;
95 const SkColor darkgreen = 0xFF008000;
96 const SkColor colors[] = { SK_ColorRED, darkgreen, purple, SK_ColorBLUE };
97 const char* arcstrs[] = {
98 "M250,400 A120,80 0 0,0 250,500",
99 "M250,400 A120,80 0 1,1 250,500",
100 "M250,400 A120,80 0 1,0 250,500",
101 "M250,400 A120,80 0 0,1 250,500"
102 };
103 int cIndex = 0;
104 for (const char* arcstr : arcstrs) {
105 SkPath path;
107 paint.setColor(colors[cIndex++]);
108 canvas->drawPath(path, paint);
109 }
110
111 // test that zero length arcs still draw round cap
112 paint.setStrokeCap(SkPaint::kRound_Cap);
114 path.moveTo(100, 100)
115 .arcTo({0, 0}, 0, SkPathBuilder::kLarge_ArcSize, SkPathDirection::kCW, {200, 200});
116 canvas->drawPath(path.detach(), paint);
117
118 path.moveTo(200, 100)
119 .arcTo({80, 80}, 0, SkPathBuilder::kLarge_ArcSize, SkPathDirection::kCW, {200, 100});
120 canvas->drawPath(path.detach(), paint);
121}
122
123enum {
126
127const struct Legal {
128 char fSymbol;
129 int fScalars;
130} gLegal[] = {
131 { 'M', 2 },
132 { 'H', 1 },
133 { 'V', 1 },
134 { 'L', 2 },
135 { 'Q', 4 },
136 { 'T', 2 },
137 { 'C', 6 },
138 { 'S', 4 },
139 { 'A', 4 },
140 { 'Z', 0 },
142
143bool gEasy = false; // set to true while debugging to suppress unusual whitespace
144
145// mostly do nothing, then bias towards spaces
146const char gWhiteSpace[] = { 0, 0, 0, 0, 0, 0, 0, 0, ' ', ' ', ' ', ' ', 0x09, 0x0D, 0x0A };
147
148static void add_white(SkRandom* rand, SkString* atom) {
149 if (gEasy) {
150 atom->append(" ");
151 return;
152 }
153 int reps = rand->nextRangeU(0, 2);
154 for (int rep = 0; rep < reps; ++rep) {
155 int index = rand->nextRangeU(0, (int) std::size(gWhiteSpace) - 1);
156 if (gWhiteSpace[index]) {
157 atom->append(&gWhiteSpace[index], 1);
158 }
159 }
160}
161
162static void add_comma(SkRandom* rand, SkString* atom) {
163 if (gEasy) {
164 atom->append(",");
165 return;
166 }
167 size_t count = atom->size();
168 add_white(rand, atom);
169 if (rand->nextBool()) {
170 atom->append(",");
171 }
172 do {
173 add_white(rand, atom);
174 } while (count == atom->size());
175}
176
177static void add_some_white(SkRandom* rand, SkString* atom) {
178 size_t count = atom->size();
179 do {
180 add_white(rand, atom);
181 } while (count == atom->size());
182}
183
185 SkString atom;
186 int legalIndex = rand->nextRangeU(0, (int) std::size(gLegal) - 1);
187 const Legal& legal = gLegal[legalIndex];
188 gEasy ? atom.append("\n") : add_white(rand, &atom);
189 char symbol = legal.fSymbol | (rand->nextBool() ? 0x20 : 0);
190 atom.append(&symbol, 1);
191 int reps = rand->nextRangeU(1, 3);
192 for (int rep = 0; rep < reps; ++rep) {
193 for (int index = 0; index < legal.fScalars; ++index) {
194 SkScalar coord = rand->nextRangeF(0, 100);
195 add_white(rand, &atom);
196 atom.appendScalar(coord);
197 if (rep < reps - 1 && index < legal.fScalars - 1) {
198 add_comma(rand, &atom);
199 } else {
200 add_some_white(rand, &atom);
201 }
202 if ('A' == legal.fSymbol && 1 == index) {
203 atom.appendScalar(rand->nextRangeF(-720, 720));
204 add_comma(rand, &atom);
205 atom.appendU32(rand->nextRangeU(0, 1));
206 add_comma(rand, &atom);
207 atom.appendU32(rand->nextRangeU(0, 1));
208 add_comma(rand, &atom);
209 }
210 }
211 }
212 return atom;
213}
214
216 SkString str;
217 FILE* file;
219 file = sk_fopen("svgout.htm", kWrite_SkFILE_Flag);
220 str.printf("<svg width=\"%d\" height=\"%d\">\n", kParsePathTestDimension,
222 sk_fwrite(str.c_str(), str.size(), file);
223 }
224 SkRandom rand;
226 paint.setAntiAlias(true);
227 for (int xStart = 0; xStart < kParsePathTestDimension; xStart += 100) {
228 canvas->save();
229 for (int yStart = 0; yStart < kParsePathTestDimension; yStart += 100) {
231 str.printf("<g transform='translate(%d,%d) scale(%d,%d)'>\n", xStart, yStart,
232 1, 1);
233 sk_fwrite(str.c_str(), str.size(), file);
234 str.printf("<clipPath id='clip_%d_%d'>\n", xStart, yStart);
235 sk_fwrite(str.c_str(), str.size(), file);
236 str.printf("<rect width='100' height='100' x='0' y='0'></rect>\n");
237 sk_fwrite(str.c_str(), str.size(), file);
238 str.printf("</clipPath>\n");
239 sk_fwrite(str.c_str(), str.size(), file);
240 }
241 int count = 3;
242 do {
243 SkPath path;
244 SkString spec;
245 uint32_t y = rand.nextRangeU(30, 70);
246 uint32_t x = rand.nextRangeU(30, 70);
247 spec.printf("M %u,%u\n", x, y);
248 for (uint32_t i = rand.nextRangeU(0, 10); i--; ) {
249 spec.append(make_random_svg_path(&rand));
250 }
252 paint.setColor(rand.nextU());
253 canvas->save();
254 canvas->clipRect(SkRect::MakeIWH(100, 100));
255 canvas->drawPath(path, paint);
256 canvas->restore();
258 str.printf("<path d='\n");
259 sk_fwrite(str.c_str(), str.size(), file);
260 sk_fwrite(spec.c_str(), spec.size(), file);
261 str.printf("\n' fill='#%06x' fill-opacity='%g'", paint.getColor() & 0xFFFFFF,
262 paint.getAlpha() / 255.f);
263 sk_fwrite(str.c_str(), str.size(), file);
264 str.printf(" clip-path='url(#clip_%d_%d)'/>\n", xStart, yStart);
265 sk_fwrite(str.c_str(), str.size(), file);
266 }
267 } while (--count > 0);
269 str.printf("</g>\n");
270 sk_fwrite(str.c_str(), str.size(), file);
271 }
272 canvas->translate(0, 100);
273 }
274 canvas->restore();
275 canvas->translate(100, 0);
276 }
278 const char trailer[] = "</svg>\n";
279 sk_fwrite(trailer, sizeof(trailer) - 1, file);
281 }
282}
283
284DEF_SIMPLE_GM(bug593049, canvas, 300, 300) {
285 canvas->translate(111, 0);
286
288 p.moveTo(-43.44464063610148f, 79.43535936389853f);
289 const SkScalar yOffset = 122.88f;
290 const SkScalar radius = 61.44f;
291 SkRect oval = SkRect::MakeXYWH(-radius, yOffset - radius, 2 * radius, 2 * radius);
292 p.arcTo(oval, 1.25f * 180, .5f * 180, false);
293
296 paint.setStrokeCap(SkPaint::kRound_Cap);
297 paint.setStrokeWidth(15.36f);
298
299 canvas->drawPath(p.detach(), paint);
300}
301
302DEF_SIMPLE_GM(bug583299, canvas, 300, 300) {
303 const char* d="M60,60 A50,50 0 0 0 160,60 A50,50 0 0 0 60,60z";
304 SkPaint p;
305 p.setStyle(SkPaint::kStroke_Style);
306 p.setStrokeWidth(100);
307 p.setAntiAlias(true);
308 p.setColor(0xFF008200);
309 p.setStrokeCap(SkPaint::kSquare_Cap);
310 SkPath path;
312 SkPathMeasure meas(path, false);
313 SkScalar length = meas.getLength();
314 SkScalar intervals[] = {0, length };
315 int intervalCount = (int) std::size(intervals);
316 p.setPathEffect(SkDashPathEffect::Make(intervals, intervalCount, 0));
317 canvas->drawPath(path, p);
318}
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
int count
Definition: FontMgrTest.cpp:50
uint32_t SkColor
Definition: SkColor.h:37
constexpr SkColor SK_ColorBLUE
Definition: SkColor.h:135
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
FILE * sk_fopen(const char path[], SkFILE_Flags)
void sk_fclose(FILE *)
size_t sk_fwrite(const void *buffer, size_t byteCount, FILE *)
@ kWrite_SkFILE_Flag
Definition: SkOSFile.h:21
@ kParsePathTestDimension
Definition: arcto.cpp:124
static void add_some_white(SkRandom *rand, SkString *atom)
Definition: arcto.cpp:177
static void add_comma(SkRandom *rand, SkString *atom)
Definition: arcto.cpp:162
static constexpr bool GENERATE_SVG_REFERENCE
Definition: arcto.cpp:29
const char gWhiteSpace[]
Definition: arcto.cpp:146
DEF_SIMPLE_GM(arcto, canvas, 500, 600)
Definition: arcto.cpp:66
const struct Legal gLegal[]
static SkString make_random_svg_path(SkRandom *rand)
Definition: arcto.cpp:184
static void add_white(SkRandom *rand, SkString *atom)
Definition: arcto.cpp:148
bool gEasy
Definition: arcto.cpp:143
static sk_sp< SkPathEffect > Make(const SkScalar intervals[], int count, SkScalar phase)
@ kRound_Cap
adds circle
Definition: SkPaint.h:335
@ kSquare_Cap
adds square
Definition: SkPaint.h:336
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
static bool FromSVGString(const char str[], SkPath *)
SkPathBuilder & arcTo(const SkRect &oval, SkScalar startAngleDeg, SkScalar sweepAngleDeg, bool forceMoveTo)
SkPathBuilder & moveTo(SkPoint pt)
@ kSmall_ArcSize
smaller of arc pair
@ kLarge_ArcSize
larger of arc pair
SkScalar getLength()
Definition: SkPath.h:59
uint32_t nextU()
Definition: SkRandom.h:42
bool nextBool()
Definition: SkRandom.h:117
float nextRangeF(float min, float max)
Definition: SkRandom.h:64
uint32_t nextRangeU(uint32_t min, uint32_t max)
Definition: SkRandom.h:80
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition: SkString.cpp:534
size_t size() const
Definition: SkString.h:131
void append(const char text[])
Definition: SkString.h:203
void appendScalar(SkScalar value)
Definition: SkString.h:213
void appendU32(uint32_t value)
Definition: SkString.h:210
const char * c_str() const
Definition: SkString.h:133
const Paint & paint
Definition: color_source.cc:38
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
float SkScalar
Definition: extension.cpp:12
size_t length
double y
double x
SkRect oval
Definition: SkRecords.h:249
PODArray< SkColor > colors
Definition: SkRecords.h:276
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
char fSymbol
int fScalars
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
static SkRect MakeIWH(int w, int h)
Definition: SkRect.h:623
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
void offset(float dx, float dy)
Definition: SkRect.h:1016
constexpr float height() const
Definition: SkRect.h:769
constexpr float right() const
Definition: SkRect.h:748
constexpr float width() const
Definition: SkRect.h:762
constexpr float bottom() const
Definition: SkRect.h:755
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15