Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
DegenerateQuadsSlide.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2019 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
17
22
23// Draw a line through the two points, outset by a fixed length in screen space
24static void draw_extended_line(SkCanvas* canvas, const SkPaint& paint,
25 const SkPoint& p0, const SkPoint& p1) {
26 SkVector v = p1 - p0;
27 v.setLength(v.length() + 3.f);
28 canvas->drawLine(p1 - v, p0 + v, paint);
29
30 // Draw normal vector too
31 SkPaint normalPaint = paint;
32 normalPaint.setPathEffect(nullptr);
33 normalPaint.setStrokeWidth(paint.getStrokeWidth() / 4.f);
34
35 SkVector n = {v.fY, -v.fX};
36 n.setLength(.25f);
37 SkPoint m = (p0 + p1) * 0.5f;
38 canvas->drawLine(m, m + n, normalPaint);
39}
40
41static void make_aa_line(const SkPoint& p0, const SkPoint& p1, bool aaOn,
42 bool outset, SkPoint line[2]) {
43 SkVector n = {0.f, 0.f};
44 if (aaOn) {
45 SkVector v = p1 - p0;
46 n = outset ? SkVector::Make(v.fY, -v.fX) : SkVector::Make(-v.fY, v.fX);
47 n.setLength(0.5f);
48 }
49
50 line[0] = p0 + n;
51 line[1] = p1 + n;
52}
53
54// To the line through l0-l1, not capped at the end points of the segment
55static SkScalar signed_distance(const SkPoint& p, const SkPoint& l0, const SkPoint& l1) {
56 SkVector v = l1 - l0;
57 v.normalize();
58 SkVector n = {v.fY, -v.fX};
59 SkScalar c = -n.dot(l0);
60 return n.dot(p) + c;
61}
62
63static SkScalar get_area_coverage(const bool edgeAA[4], const SkPoint corners[4],
64 const SkPoint& point) {
65 SkPath shape;
66 shape.addPoly(corners, 4, true);
67 SkPath pixel;
68 pixel.addRect(SkRect::MakeXYWH(point.fX - 0.5f, point.fY - 0.5f, 1.f, 1.f));
69
70 SkPath intersection;
71 if (!Op(shape, pixel, kIntersect_SkPathOp, &intersection) || intersection.isEmpty()) {
72 return 0.f;
73 }
74
75 // Calculate area of the convex polygon
76 SkScalar area = 0.f;
77 for (int i = 0; i < intersection.countPoints(); ++i) {
78 SkPoint p0 = intersection.getPoint(i);
79 SkPoint p1 = intersection.getPoint((i + 1) % intersection.countPoints());
80 SkScalar det = p0.fX * p1.fY - p1.fX * p0.fY;
81 area += det;
82 }
83
84 // Scale by 1/2, then take abs value (this area formula is signed based on point winding, but
85 // since it's convex, just make it positive).
86 area = SkScalarAbs(0.5f * area);
87
88 // Now account for the edge AA. If the pixel center is outside of a non-AA edge, turn of its
89 // coverage. If the pixel only intersects non-AA edges, then set coverage to 1.
90 bool needsNonAA = false;
91 SkScalar edgeD[4];
92 for (int i = 0; i < 4; ++i) {
93 SkPoint e0 = corners[i];
94 SkPoint e1 = corners[(i + 1) % 4];
95 edgeD[i] = -signed_distance(point, e0, e1);
96 if (!edgeAA[i]) {
97 if (edgeD[i] < -1e-4f) {
98 return 0.f; // Outside of non-AA line
99 }
100 needsNonAA = true;
101 }
102 }
103 // Otherwise inside the shape, so check if any AA edge exerts influence over nonAA
104 if (needsNonAA) {
105 for (int i = 0; i < 4; i++) {
106 if (edgeAA[i] && edgeD[i] < 0.5f) {
107 needsNonAA = false;
108 break;
109 }
110 }
111 }
112 return needsNonAA ? 1.f : area;
113}
114
115// FIXME take into account max coverage properly,
116static SkScalar get_edge_dist_coverage(const bool edgeAA[4], const SkPoint corners[4],
117 const SkPoint outsetLines[8], const SkPoint insetLines[8],
118 const SkPoint& point) {
119 bool flip = false;
120 // If the quad has been inverted, the original corners will not all be on the negative side of
121 // every outset line. When that happens, calculate coverage using the "inset" lines and flip
122 // the signed distance
123 for (int i = 0; i < 4; ++i) {
124 for (int j = 0; j < 4; ++j) {
125 SkScalar d = signed_distance(corners[i], outsetLines[j * 2], outsetLines[j * 2 + 1]);
126 if (d > 1e-4f) {
127 flip = true;
128 break;
129 }
130 }
131 if (flip) {
132 break;
133 }
134 }
135
136 const SkPoint* lines = flip ? insetLines : outsetLines;
137
138 SkScalar minCoverage = 1.f;
139 for (int i = 0; i < 4; ++i) {
140 // Multiply by negative 1 so that outside points have negative distances
141 SkScalar d = (flip ? 1 : -1) * signed_distance(point, lines[i * 2], lines[i * 2 + 1]);
142 if (!edgeAA[i] && d >= -1e-4f) {
143 d = 1.f;
144 }
145 if (d < minCoverage) {
146 minCoverage = d;
147 if (minCoverage < 0.f) {
148 break; // Outside the shape
149 }
150 }
151 }
152 return minCoverage < 0.f ? 0.f : minCoverage;
153}
154
155static bool inside_triangle(const SkPoint& point, const SkPoint& t0, const SkPoint& t1,
156 const SkPoint& t2, SkScalar bary[3]) {
157 // Check sign of t0 to (t1,t2). If it is positive, that means the normals point into the
158 // triangle otherwise the normals point outside the triangle so update edge distances as
159 // necessary
160 bool flip = signed_distance(t0, t1, t2) < 0.f;
161
162 SkScalar d0 = (flip ? -1 : 1) * signed_distance(point, t0, t1);
163 SkScalar d1 = (flip ? -1 : 1) * signed_distance(point, t1, t2);
164 SkScalar d2 = (flip ? -1 : 1) * signed_distance(point, t2, t0);
165 // Be a little forgiving
166 if (d0 < -1e-4f || d1 < -1e-4f || d2 < -1e-4f) {
167 return false;
168 }
169
170 // Inside, so calculate barycentric coords from the sideline distances
171 SkScalar d01 = (t0 - t1).length();
172 SkScalar d12 = (t1 - t2).length();
173 SkScalar d20 = (t2 - t0).length();
174
176 // Empty degenerate triangle
177 return false;
178 }
179
180 // Coordinates for a vertex use distances to the opposite edge
181 bary[0] = d1 * d12;
182 bary[1] = d2 * d20;
183 bary[2] = d0 * d01;
184 // And normalize
185 SkScalar sum = bary[0] + bary[1] + bary[2];
186 bary[0] /= sum;
187 bary[1] /= sum;
188 bary[2] /= sum;
189
190 return true;
191}
192
193static SkScalar get_framed_coverage(const SkPoint outer[4], const SkScalar outerCoverages[4],
194 const SkPoint inner[4], const SkScalar innerCoverages[4],
195 const SkRect& geomDomain, const SkPoint& point) {
196 // Triangles are ordered clock wise. Indices >= 4 refer to inner[i - 4]. Otherwise its outer[i].
197 static const int kFrameTris[] = {
198 0, 1, 4, 4, 1, 5,
199 1, 2, 5, 5, 2, 6,
200 2, 3, 6, 6, 3, 7,
201 3, 0, 7, 7, 0, 4,
202 4, 5, 7, 7, 5, 6
203 };
204 static const int kNumTris = 10;
205
206 SkScalar bary[3];
207 for (int i = 0; i < kNumTris; ++i) {
208 int i0 = kFrameTris[i * 3];
209 int i1 = kFrameTris[i * 3 + 1];
210 int i2 = kFrameTris[i * 3 + 2];
211
212 SkPoint t0 = i0 >= 4 ? inner[i0 - 4] : outer[i0];
213 SkPoint t1 = i1 >= 4 ? inner[i1 - 4] : outer[i1];
214 SkPoint t2 = i2 >= 4 ? inner[i2 - 4] : outer[i2];
215 if (inside_triangle(point, t0, t1, t2, bary)) {
216 // Calculate coverage by barycentric interpolation of coverages
217 SkScalar c0 = i0 >= 4 ? innerCoverages[i0 - 4] : outerCoverages[i0];
218 SkScalar c1 = i1 >= 4 ? innerCoverages[i1 - 4] : outerCoverages[i1];
219 SkScalar c2 = i2 >= 4 ? innerCoverages[i2 - 4] : outerCoverages[i2];
220
221 SkScalar coverage = bary[0] * c0 + bary[1] * c1 + bary[2] * c2;
222 if (coverage < 0.5f) {
223 // Check distances to domain
224 SkScalar l = SkTPin(point.fX - geomDomain.fLeft, 0.f, 1.f);
225 SkScalar t = SkTPin(point.fY - geomDomain.fTop, 0.f, 1.f);
226 SkScalar r = SkTPin(geomDomain.fRight - point.fX, 0.f, 1.f);
227 SkScalar b = SkTPin(geomDomain.fBottom - point.fY, 0.f, 1.f);
228 coverage = std::min(coverage, l * t * r * b);
229 }
230 return coverage;
231 }
232 }
233 // Not inside any triangle
234 return 0.f;
235}
236
237static constexpr SkScalar kViewScale = 100.f;
238static constexpr SkScalar kViewOffset = 200.f;
239
241public:
243 : fOuterRect(rect)
244 , fCoverageMode(CoverageMode::kArea) {
245 fOuterRect.toQuad(fCorners);
246 for (int i = 0; i < 4; ++i) {
247 fEdgeAA[i] = true;
248 }
249 fName = "DegenerateQuad";
250 }
251
252 void draw(SkCanvas* canvas) override {
253 static const SkScalar kDotParams[2] = {1.f / kViewScale, 12.f / kViewScale};
254 sk_sp<SkPathEffect> dots = SkDashPathEffect::Make(kDotParams, 2, 0.f);
255 static const SkScalar kDashParams[2] = {8.f / kViewScale, 12.f / kViewScale};
256 sk_sp<SkPathEffect> dashes = SkDashPathEffect::Make(kDashParams, 2, 0.f);
257
258 SkPaint circlePaint;
259 circlePaint.setAntiAlias(true);
260
261 SkPaint linePaint;
262 linePaint.setAntiAlias(true);
264 linePaint.setStrokeWidth(4.f / kViewScale);
267
269 canvas->scale(kViewScale, kViewScale);
270
271 // Draw the outer rectangle as a dotted line
272 linePaint.setPathEffect(dots);
273 canvas->drawRect(fOuterRect, linePaint);
274
275 bool valid = this->isValid();
276
277 if (valid) {
278 SkPoint outsets[8];
279 SkPoint insets[8];
280 // Calculate inset and outset lines for edge-distance visualization
281 for (int i = 0; i < 4; ++i) {
282 make_aa_line(fCorners[i], fCorners[(i + 1) % 4], fEdgeAA[i], true, outsets + i * 2);
283 make_aa_line(fCorners[i], fCorners[(i + 1) % 4], fEdgeAA[i], false, insets + i * 2);
284 }
285
286 // Calculate inner and outer meshes for GPU visualization
287 SkPoint gpuOutset[4];
288 SkScalar gpuOutsetCoverage[4];
289 SkPoint gpuInset[4];
290 SkScalar gpuInsetCoverage[4];
291 SkRect gpuDomain;
292 this->getTessellatedPoints(gpuInset, gpuInsetCoverage, gpuOutset, gpuOutsetCoverage,
293 &gpuDomain);
294
295 // Visualize the coverage values across the clamping rectangle, but test pixels outside
296 // of the "outer" rect since some quad edges can be outset extra far.
297 SkPaint pixelPaint;
298 pixelPaint.setAntiAlias(true);
299 SkRect covRect = fOuterRect.makeOutset(2.f, 2.f);
300 for (SkScalar py = covRect.fTop; py < covRect.fBottom; py += 1.f) {
301 for (SkScalar px = covRect.fLeft; px < covRect.fRight; px += 1.f) {
302 // px and py are the top-left corner of the current pixel, so get center's
303 // coordinate
304 SkPoint pixelCenter = {px + 0.5f, py + 0.5f};
305 SkScalar coverage;
306 if (fCoverageMode == CoverageMode::kArea) {
307 coverage = get_area_coverage(fEdgeAA, fCorners, pixelCenter);
308 } else if (fCoverageMode == CoverageMode::kEdgeDistance) {
309 coverage = get_edge_dist_coverage(fEdgeAA, fCorners, outsets, insets,
310 pixelCenter);
311 } else {
312 SkASSERT(fCoverageMode == CoverageMode::kGPUMesh);
313 coverage = get_framed_coverage(gpuOutset, gpuOutsetCoverage,
314 gpuInset, gpuInsetCoverage, gpuDomain,
315 pixelCenter);
316 }
317
318 SkRect pixelRect = SkRect::MakeXYWH(px, py, 1.f, 1.f);
319 pixelRect.inset(0.1f, 0.1f);
320
321 SkScalar a = 1.f - 0.5f * coverage;
322 pixelPaint.setColor4f({a, a, a, 1.f}, nullptr);
323 canvas->drawRect(pixelRect, pixelPaint);
324
325 pixelPaint.setColor(coverage > 0.f ? SK_ColorGREEN : SK_ColorRED);
326 pixelRect.inset(0.38f, 0.38f);
327 canvas->drawRect(pixelRect, pixelPaint);
328 }
329 }
330
331 linePaint.setPathEffect(dashes);
332 // Draw the inset/outset "infinite" lines
333 if (fCoverageMode == CoverageMode::kEdgeDistance) {
334 for (int i = 0; i < 4; ++i) {
335 if (fEdgeAA[i]) {
336 linePaint.setColor(SK_ColorBLUE);
337 draw_extended_line(canvas, linePaint, outsets[i * 2], outsets[i * 2 + 1]);
338 linePaint.setColor(SK_ColorGREEN);
339 draw_extended_line(canvas, linePaint, insets[i * 2], insets[i * 2 + 1]);
340 } else {
341 // Both outset and inset are the same line, so only draw one in cyan
342 linePaint.setColor(SK_ColorCYAN);
343 draw_extended_line(canvas, linePaint, outsets[i * 2], outsets[i * 2 + 1]);
344 }
345 }
346 }
347
348 linePaint.setPathEffect(nullptr);
349 // What is tessellated using GrQuadPerEdgeAA
350 if (fCoverageMode == CoverageMode::kGPUMesh) {
351 SkPath outsetPath;
352 outsetPath.addPoly(gpuOutset, 4, true);
353 linePaint.setColor(SK_ColorBLUE);
354 canvas->drawPath(outsetPath, linePaint);
355
356 SkPath insetPath;
357 insetPath.addPoly(gpuInset, 4, true);
358 linePaint.setColor(SK_ColorGREEN);
359 canvas->drawPath(insetPath, linePaint);
360
361 SkPaint domainPaint = linePaint;
362 domainPaint.setStrokeWidth(2.f / kViewScale);
363 domainPaint.setPathEffect(dashes);
364 domainPaint.setColor(SK_ColorMAGENTA);
365 canvas->drawRect(gpuDomain, domainPaint);
366 }
367
368 // Draw the edges of the true quad as a solid line
369 SkPath path;
370 path.addPoly(fCorners, 4, true);
371 linePaint.setColor(SK_ColorBLACK);
372 canvas->drawPath(path, linePaint);
373 } else {
374 // Draw the edges of the true quad as a solid *red* line
375 SkPath path;
376 path.addPoly(fCorners, 4, true);
377 linePaint.setColor(SK_ColorRED);
378 linePaint.setPathEffect(nullptr);
379 canvas->drawPath(path, linePaint);
380 }
381
382 // Draw the four clickable corners as circles
383 circlePaint.setColor(valid ? SK_ColorBLACK : SK_ColorRED);
384 for (int i = 0; i < 4; ++i) {
385 canvas->drawCircle(fCorners[i], 5.f / kViewScale, circlePaint);
386 }
387 }
388
389 bool onChar(SkUnichar) override;
390
391
392protected:
394 bool onClick(Click*) override;
395
396private:
397 class Click;
398
399 enum class CoverageMode {
400 kArea, kEdgeDistance, kGPUMesh
401 };
402
403 const SkRect fOuterRect;
404 SkPoint fCorners[4]; // TL, TR, BR, BL
405 bool fEdgeAA[4]; // T, R, B, L
406 CoverageMode fCoverageMode;
407
408 bool isValid() const {
409 SkPath path;
410 path.addPoly(fCorners, 4, true);
411 return path.isConvex();
412 }
413
414 void getTessellatedPoints(SkPoint inset[4], SkScalar insetCoverage[4], SkPoint outset[4],
415 SkScalar outsetCoverage[4], SkRect* domain) const {
416 // Fixed vertex spec for extracting the picture frame geometry
417 static const VertexSpec kSpec =
418 {GrQuad::Type::kGeneral, ColorType::kNone,
419 GrQuad::Type::kAxisAligned, false, Subset::kNo,
420 GrAAType::kCoverage, false, IndexBufferOption::kPictureFramed};
421 static const GrQuad kIgnored(SkRect::MakeEmpty());
422
428
429 GrQuad quad = GrQuad::MakeFromSkQuad(fCorners, SkMatrix::I());
430
431 float vertices[56]; // 2 quads, with x, y, coverage, and geometry domain (7 floats x 8 vert)
432 skgpu::ganesh::QuadPerEdgeAA::Tessellator tessellator(kSpec, (char*)vertices);
433 tessellator.append(&quad, nullptr, {1.f, 1.f, 1.f, 1.f},
435
436 // The first quad in vertices is the inset, then the outset, but they
437 // are ordered TL, BL, TR, BR so un-interleave coverage and re-arrange
438 inset[0] = {vertices[0], vertices[1]}; // TL
439 insetCoverage[0] = vertices[2];
440 inset[3] = {vertices[7], vertices[8]}; // BL
441 insetCoverage[3] = vertices[9];
442 inset[1] = {vertices[14], vertices[15]}; // TR
443 insetCoverage[1] = vertices[16];
444 inset[2] = {vertices[21], vertices[22]}; // BR
445 insetCoverage[2] = vertices[23];
446
447 outset[0] = {vertices[28], vertices[29]}; // TL
448 outsetCoverage[0] = vertices[30];
449 outset[3] = {vertices[35], vertices[36]}; // BL
450 outsetCoverage[3] = vertices[37];
451 outset[1] = {vertices[42], vertices[43]}; // TR
452 outsetCoverage[1] = vertices[44];
453 outset[2] = {vertices[49], vertices[50]}; // BR
454 outsetCoverage[2] = vertices[51];
455
456 *domain = {vertices[52], vertices[53], vertices[54], vertices[55]};
457 }
458};
459
461public:
462 Click(const SkRect& clamp, int index)
463 : fOuterRect(clamp)
464 , fIndex(index) {}
465
467 if (fIndex >= 0) {
468 this->drag(&points[fIndex]);
469 } else {
470 for (int i = 0; i < 4; ++i) {
471 this->drag(&points[i]);
472 }
473 }
474 }
475
476private:
477 SkRect fOuterRect;
478 int fIndex;
479
480 void drag(SkPoint* point) {
481 SkPoint delta = fCurr - fPrev;
482 *point += SkPoint::Make(delta.x() / kViewScale, delta.y() / kViewScale);
483 point->fX = std::min(fOuterRect.fRight, std::max(point->fX, fOuterRect.fLeft));
484 point->fY = std::min(fOuterRect.fBottom, std::max(point->fY, fOuterRect.fTop));
485 }
486};
487
491 for (int i = 0; i < 4; ++i) {
492 if ((fCorners[i] - inCTM).length() < 10.f / kViewScale) {
493 return new Click(fOuterRect, i);
494 }
495 }
496 return new Click(fOuterRect, -1);
497}
498
500 Click* myClick = (Click*) click;
501 myClick->doClick(fCorners);
502 return true;
503}
504
506 switch(code) {
507 case '1':
508 fEdgeAA[0] = !fEdgeAA[0];
509 return true;
510 case '2':
511 fEdgeAA[1] = !fEdgeAA[1];
512 return true;
513 case '3':
514 fEdgeAA[2] = !fEdgeAA[2];
515 return true;
516 case '4':
517 fEdgeAA[3] = !fEdgeAA[3];
518 return true;
519 case 'q':
520 fCoverageMode = CoverageMode::kArea;
521 return true;
522 case 'w':
523 fCoverageMode = CoverageMode::kEdgeDistance;
524 return true;
525 case 'e':
526 fCoverageMode = CoverageMode::kGPUMesh;
527 return true;
528 }
529 return false;
530}
531
532DEF_SLIDE(return new DegenerateQuadSlide(SkRect::MakeWH(4.f, 4.f));)
static const int outset
Definition BlurTest.cpp:58
static constexpr SkScalar kViewScale
static SkScalar get_area_coverage(const bool edgeAA[4], const SkPoint corners[4], const SkPoint &point)
static SkScalar get_edge_dist_coverage(const bool edgeAA[4], const SkPoint corners[4], const SkPoint outsetLines[8], const SkPoint insetLines[8], const SkPoint &point)
static void make_aa_line(const SkPoint &p0, const SkPoint &p1, bool aaOn, bool outset, SkPoint line[2])
static void draw_extended_line(SkCanvas *canvas, const SkPaint &paint, const SkPoint &p0, const SkPoint &p1)
static bool inside_triangle(const SkPoint &point, const SkPoint &t0, const SkPoint &t1, const SkPoint &t2, SkScalar bary[3])
static constexpr SkScalar kViewOffset
static SkScalar get_framed_coverage(const SkPoint outer[4], const SkScalar outerCoverages[4], const SkPoint inner[4], const SkScalar innerCoverages[4], const SkRect &geomDomain, const SkPoint &point)
static SkScalar signed_distance(const SkPoint &p, const SkPoint &l0, const SkPoint &l1)
GrQuadAAFlags
static const int points[]
float e1
float e0
#define SkASSERT(cond)
Definition SkAssert.h:116
static unsigned clamp(SkFixed fx, int max)
constexpr SkColor SK_ColorMAGENTA
Definition SkColor.h:147
constexpr SkColor SK_ColorCYAN
Definition SkColor.h:143
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
@ kIntersect_SkPathOp
intersect the two paths
Definition SkPathOps.h:24
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:101
#define SkScalarAbs(x)
Definition SkScalar.h:39
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition SkTPin.h:19
int32_t SkUnichar
Definition SkTypes.h:175
#define DEF_SLIDE(code)
Definition Slide.h:25
void doClick(SkPoint points[4])
Click(const SkRect &clamp, int index)
bool onChar(SkUnichar) override
void draw(SkCanvas *canvas) override
DegenerateQuadSlide(const SkRect &rect)
Click * onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override
bool onClick(Click *) override
static GrQuad MakeFromSkQuad(const SkPoint pts[4], const SkMatrix &)
Definition GrQuad.cpp:122
void drawRect(const SkRect &rect, const SkPaint &paint)
void translate(SkScalar dx, SkScalar dy)
void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint &paint)
void drawPath(const SkPath &path, const SkPaint &paint)
void scale(SkScalar sx, SkScalar sy)
void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint &paint)
static sk_sp< SkPathEffect > Make(const SkScalar intervals[], int count, SkScalar phase)
static const SkMatrix & I()
@ kRound_Cap
adds circle
Definition SkPaint.h:335
void setStyle(Style style)
Definition SkPaint.cpp:105
void setColor(SkColor color)
Definition SkPaint.cpp:119
void setAntiAlias(bool aa)
Definition SkPaint.h:170
void setStrokeCap(Cap cap)
Definition SkPaint.cpp:179
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
void setStrokeJoin(Join join)
Definition SkPaint.cpp:189
void setColor4f(const SkColor4f &color, SkColorSpace *colorSpace=nullptr)
Definition SkPaint.h:253
void setPathEffect(sk_sp< SkPathEffect > pathEffect)
@ kRound_Join
adds circle
Definition SkPaint.h:360
void setStrokeWidth(SkScalar width)
Definition SkPaint.cpp:159
bool isEmpty() const
Definition SkPath.cpp:406
int countPoints() const
Definition SkPath.cpp:525
SkPoint getPoint(int index) const
Definition SkPath.cpp:539
SkPath & addPoly(const SkPoint pts[], int count, bool close)
Definition SkPath.cpp:880
SkPath & addRect(const SkRect &rect, SkPathDirection dir, unsigned start)
Definition SkPath.cpp:854
SkString fName
Definition Slide.h:54
const Paint & paint
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
float SkScalar
Definition extension.cpp:12
static bool b
struct MyStruct a[10]
FlutterSemanticsFlag flags
size_t length
double y
double x
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
ModifierKey
Definition ModifierKey.h:9
static SkRect inset(const SkRect &r)
bool setLength(float length)
Definition SkPoint.cpp:30
float fX
x-axis value
float dot(const SkVector &vec) const
static constexpr SkPoint Make(float x, float y)
float length() const
float fY
y-axis value
bool normalize()
Definition SkPoint.cpp:22
static constexpr SkRect MakeEmpty()
Definition SkRect.h:595
SkScalar fBottom
larger y-axis bounds
Definition extension.cpp:17
void toQuad(SkPoint quad[4]) const
Definition SkRect.cpp:50
void inset(float dx, float dy)
Definition SkRect.h:1060
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
SkRect makeOutset(float dx, float dy) const
Definition SkRect.h:1002
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
SkScalar fRight
larger x-axis bounds
Definition extension.cpp:16
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609
SkScalar fTop
smaller y-axis bounds
Definition extension.cpp:15