Flutter Engine
The Flutter Engine
GrDistanceFieldGenFromVector.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2017 ARM Ltd.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
8
13#include "src/core/SkGeometry.h"
14#include "src/core/SkPathPriv.h"
16#include "src/core/SkRectPriv.h"
18
19using namespace skia_private;
20
21#if !defined(SK_ENABLE_OPTIMIZE_SIZE)
22
23namespace {
24// TODO: should we make this real (i.e. src/core) and distinguish it from
25// pathops SkDPoint?
26struct DPoint {
27 double fX, fY;
28
29 double distanceSquared(DPoint p) const {
30 double dx = fX - p.fX;
31 double dy = fY - p.fY;
32 return dx*dx + dy*dy;
33 }
34
35 double distance(DPoint p) const { return sqrt(this->distanceSquared(p)); }
36};
37}
38
39/**
40 * If a scanline (a row of texel) cross from the kRight_SegSide
41 * of a segment to the kLeft_SegSide, the winding score should
42 * add 1.
43 * And winding score should subtract 1 if the scanline cross
44 * from kLeft_SegSide to kRight_SegSide.
45 * Always return kNA_SegSide if the scanline does not cross over
46 * the segment. Winding score should be zero in this case.
47 * You can get the winding number for each texel of the scanline
48 * by adding the winding score from left to right.
49 * Assuming we always start from outside, so the winding number
50 * should always start from zero.
51 * ________ ________
52 * | | | |
53 * ...R|L......L|R.....L|R......R|L..... <= Scanline & side of segment
54 * |+1 |-1 |-1 |+1 <= Winding score
55 * 0 | 1 ^ 0 ^ -1 |0 <= Winding number
56 * |________| |________|
57 *
58 * .......NA................NA..........
59 * 0 0
60 */
61enum SegSide {
66};
67
68struct DFData {
69 float fDistSq; // distance squared to nearest (so far) edge
70 int fDeltaWindingScore; // +1 or -1 whenever a scanline cross over a segment
71};
72
73///////////////////////////////////////////////////////////////////////////////
74
75/*
76 * Type definition for double precision DAffineMatrix
77 */
78
79// Matrix with double precision for affine transformation.
80// We don't store row 3 because its always (0, 0, 1).
82public:
83 double operator[](int index) const {
84 SkASSERT((unsigned)index < 6);
85 return fMat[index];
86 }
87
88 double& operator[](int index) {
89 SkASSERT((unsigned)index < 6);
90 return fMat[index];
91 }
92
93 void setAffine(double m11, double m12, double m13,
94 double m21, double m22, double m23) {
95 fMat[0] = m11;
96 fMat[1] = m12;
97 fMat[2] = m13;
98 fMat[3] = m21;
99 fMat[4] = m22;
100 fMat[5] = m23;
101 }
102
103 /** Set the matrix to identity
104 */
105 void reset() {
106 fMat[0] = fMat[4] = 1.0;
107 fMat[1] = fMat[3] =
108 fMat[2] = fMat[5] = 0.0;
109 }
110
111 // alias for reset()
112 void setIdentity() { this->reset(); }
113
114 DPoint mapPoint(const SkPoint& src) const {
115 DPoint pt = {src.fX, src.fY};
116 return this->mapPoint(pt);
117 }
118
119 DPoint mapPoint(const DPoint& src) const {
120 return { fMat[0] * src.fX + fMat[1] * src.fY + fMat[2],
121 fMat[3] * src.fX + fMat[4] * src.fY + fMat[5] };
122 }
123private:
124 double fMat[6];
125};
126
127///////////////////////////////////////////////////////////////////////////////
128
129static const double kClose = (SK_Scalar1 / 16.0);
130static const double kCloseSqd = kClose * kClose;
131static const double kNearlyZero = (SK_Scalar1 / (1 << 18));
132static const double kTangentTolerance = (SK_Scalar1 / (1 << 11));
133static const float kConicTolerance = 0.25f;
134
135// returns true if a >= min(b,c) && a < max(b,c)
136static inline bool between_closed_open(double a, double b, double c,
137 double tolerance = 0.0,
138 bool xformToleranceToX = false) {
139 SkASSERT(tolerance >= 0.0);
140 double tolB = tolerance;
141 double tolC = tolerance;
142
143 if (xformToleranceToX) {
144 // Canonical space is y = x^2 and the derivative of x^2 is 2x.
145 // So the slope of the tangent line at point (x, x^2) is 2x.
146 //
147 // /|
148 // sqrt(2x * 2x + 1 * 1) / | 2x
149 // /__|
150 // 1
151 tolB = tolerance / sqrt(4.0 * b * b + 1.0);
152 tolC = tolerance / sqrt(4.0 * c * c + 1.0);
153 }
154 return b < c ? (a >= b - tolB && a < c - tolC) :
155 (a >= c - tolC && a < b - tolB);
156}
157
158// returns true if a >= min(b,c) && a <= max(b,c)
159static inline bool between_closed(double a, double b, double c,
160 double tolerance = 0.0,
161 bool xformToleranceToX = false) {
162 SkASSERT(tolerance >= 0.0);
163 double tolB = tolerance;
164 double tolC = tolerance;
165
166 if (xformToleranceToX) {
167 tolB = tolerance / sqrt(4.0 * b * b + 1.0);
168 tolC = tolerance / sqrt(4.0 * c * c + 1.0);
169 }
170 return b < c ? (a >= b - tolB && a <= c + tolC) :
171 (a >= c - tolC && a <= b + tolB);
172}
173
174static inline bool nearly_zero(double x, double tolerance = kNearlyZero) {
175 SkASSERT(tolerance >= 0.0);
176 return fabs(x) <= tolerance;
177}
178
179static inline bool nearly_equal(double x, double y,
180 double tolerance = kNearlyZero,
181 bool xformToleranceToX = false) {
182 SkASSERT(tolerance >= 0.0);
183 if (xformToleranceToX) {
184 tolerance = tolerance / sqrt(4.0 * y * y + 1.0);
185 }
186 return fabs(x - y) <= tolerance;
187}
188
189static inline double sign_of(const double &val) {
190 return std::copysign(1, val);
191}
192
193static bool is_colinear(const SkPoint pts[3]) {
194 return nearly_zero((pts[1].fY - pts[0].fY) * (pts[1].fX - pts[2].fX) -
195 (pts[1].fY - pts[2].fY) * (pts[1].fX - pts[0].fX), kCloseSqd);
196}
197
199public:
200 enum {
201 // These enum values are assumed in member functions below.
202 kLine = 0,
203 kQuad = 1,
205
206 // line uses 2 pts, quad uses 3 pts
208
209 DPoint fP0T, fP2T;
210 DAffineMatrix fXformMatrix; // transforms the segment into canonical space
216
217 void init();
218
220 static_assert(0 == kLine && 1 == kQuad);
221 return fType + 2;
222 }
223
224 const SkPoint& endPt() const {
225 static_assert(0 == kLine && 1 == kQuad);
226 return fPts[fType + 1];
227 }
228};
229
231
233 const DPoint p0 = { fPts[0].fX, fPts[0].fY };
234 const DPoint p2 = { this->endPt().fX, this->endPt().fY };
235 const double p0x = p0.fX;
236 const double p0y = p0.fY;
237 const double p2x = p2.fX;
238 const double p2y = p2.fY;
239
240 fBoundingBox.set(fPts[0], this->endPt());
241
242 if (fType == PathSegment::kLine) {
244 double hypotenuse = p0.distance(p2);
245 if (SkTAbs(hypotenuse) < 1.0e-100) {
247 } else {
248 const double cosTheta = (p2x - p0x) / hypotenuse;
249 const double sinTheta = (p2y - p0y) / hypotenuse;
250
251 // rotates the segment to the x-axis, with p0 at the origin
253 cosTheta, sinTheta, -(cosTheta * p0x) - (sinTheta * p0y),
254 -sinTheta, cosTheta, (sinTheta * p0x) - (cosTheta * p0y)
255 );
256 }
257 } else {
259
260 // Calculate bounding box
261 const SkPoint m = fPts[0]*0.25f + fPts[1]*0.5f + fPts[2]*0.25f; // midpoint of curve
263
264 const double p1x = fPts[1].fX;
265 const double p1y = fPts[1].fY;
266
267 const double p0xSqd = p0x * p0x;
268 const double p0ySqd = p0y * p0y;
269 const double p2xSqd = p2x * p2x;
270 const double p2ySqd = p2y * p2y;
271 const double p1xSqd = p1x * p1x;
272 const double p1ySqd = p1y * p1y;
273
274 const double p01xProd = p0x * p1x;
275 const double p02xProd = p0x * p2x;
276 const double b12xProd = p1x * p2x;
277 const double p01yProd = p0y * p1y;
278 const double p02yProd = p0y * p2y;
279 const double b12yProd = p1y * p2y;
280
281 // calculate quadratic params
282 const double sqrtA = p0y - (2.0 * p1y) + p2y;
283 const double a = sqrtA * sqrtA;
284 const double h = -1.0 * (p0y - (2.0 * p1y) + p2y) * (p0x - (2.0 * p1x) + p2x);
285 const double sqrtB = p0x - (2.0 * p1x) + p2x;
286 const double b = sqrtB * sqrtB;
287 const double c = (p0xSqd * p2ySqd) - (4.0 * p01xProd * b12yProd)
288 - (2.0 * p02xProd * p02yProd) + (4.0 * p02xProd * p1ySqd)
289 + (4.0 * p1xSqd * p02yProd) - (4.0 * b12xProd * p01yProd)
290 + (p2xSqd * p0ySqd);
291 const double g = (p0x * p02yProd) - (2.0 * p0x * p1ySqd)
292 + (2.0 * p0x * b12yProd) - (p0x * p2ySqd)
293 + (2.0 * p1x * p01yProd) - (4.0 * p1x * p02yProd)
294 + (2.0 * p1x * b12yProd) - (p2x * p0ySqd)
295 + (2.0 * p2x * p01yProd) + (p2x * p02yProd)
296 - (2.0 * p2x * p1ySqd);
297 const double f = -((p0xSqd * p2y) - (2.0 * p01xProd * p1y)
298 - (2.0 * p01xProd * p2y) - (p02xProd * p0y)
299 + (4.0 * p02xProd * p1y) - (p02xProd * p2y)
300 + (2.0 * p1xSqd * p0y) + (2.0 * p1xSqd * p2y)
301 - (2.0 * b12xProd * p0y) - (2.0 * b12xProd * p1y)
302 + (p2xSqd * p0y));
303
304 const double cosTheta = sqrt(a / (a + b));
305 const double sinTheta = -1.0 * sign_of((a + b) * h) * sqrt(b / (a + b));
306
307 const double gDef = cosTheta * g - sinTheta * f;
308 const double fDef = sinTheta * g + cosTheta * f;
309
310
311 const double x0 = gDef / (a + b);
312 const double y0 = (1.0 / (2.0 * fDef)) * (c - (gDef * gDef / (a + b)));
313
314
315 const double lambda = -1.0 * ((a + b) / (2.0 * fDef));
316 fScalingFactor = fabs(1.0 / lambda);
318
319 const double lambda_cosTheta = lambda * cosTheta;
320 const double lambda_sinTheta = lambda * sinTheta;
321
322 // transforms to lie on a canonical y = x^2 parabola
324 lambda_cosTheta, -lambda_sinTheta, lambda * x0,
325 lambda_sinTheta, lambda_cosTheta, lambda * y0
326 );
327 }
328
331
334}
335
336static void init_distances(DFData* data, int size) {
337 DFData* currData = data;
338
339 for (int i = 0; i < size; ++i) {
340 // init distance to "far away"
342 currData->fDeltaWindingScore = 0;
343 ++currData;
344 }
345}
346
347static inline void add_line(const SkPoint pts[2], PathSegmentArray* segments) {
348 segments->push_back();
349 segments->back().fType = PathSegment::kLine;
350 segments->back().fPts[0] = pts[0];
351 segments->back().fPts[1] = pts[1];
352
353 segments->back().init();
354}
355
356static inline void add_quad(const SkPoint pts[3], PathSegmentArray* segments) {
357 if (SkPointPriv::DistanceToSqd(pts[0], pts[1]) < kCloseSqd ||
358 SkPointPriv::DistanceToSqd(pts[1], pts[2]) < kCloseSqd ||
359 is_colinear(pts)) {
360 if (pts[0] != pts[2]) {
361 SkPoint line_pts[2];
362 line_pts[0] = pts[0];
363 line_pts[1] = pts[2];
364 add_line(line_pts, segments);
365 }
366 } else {
367 segments->push_back();
368 segments->back().fType = PathSegment::kQuad;
369 segments->back().fPts[0] = pts[0];
370 segments->back().fPts[1] = pts[1];
371 segments->back().fPts[2] = pts[2];
372
373 segments->back().init();
374 }
375}
376
377static inline void add_cubic(const SkPoint pts[4],
378 PathSegmentArray* segments) {
381 int count = quads.size();
382 for (int q = 0; q < count; q += 3) {
383 add_quad(&quads[q], segments);
384 }
385}
386
388 const PathSegment& segment,
389 const DPoint &xFormPt) {
390 static const float kThird = 0.33333333333f;
391 static const float kTwentySeventh = 0.037037037f;
392
393 const float a = 0.5f - (float)xFormPt.fY;
394 const float b = -0.5f * (float)xFormPt.fX;
395
396 const float a3 = a * a * a;
397 const float b2 = b * b;
398
399 const float c = (b2 * 0.25f) + (a3 * kTwentySeventh);
400
401 if (c >= 0.f) {
402 const float sqrtC = sqrt(c);
403 const float result = (float)cbrt((-b * 0.5f) + sqrtC) + (float)cbrt((-b * 0.5f) - sqrtC);
404 return result;
405 } else {
406 const float cosPhi = (float)sqrt((b2 * 0.25f) * (-27.f / a3)) * ((b > 0) ? -1.f : 1.f);
407 const float phi = (float)acos(cosPhi);
408 float result;
409 if (xFormPt.fX > 0.f) {
410 result = 2.f * (float)sqrt(-a * kThird) * (float)cos(phi * kThird);
411 if (!between_closed(result, segment.fP0T.fX, segment.fP2T.fX)) {
412 result = 2.f * (float)sqrt(-a * kThird) * (float)cos((phi * kThird) + (SK_ScalarPI * 2.f * kThird));
413 }
414 } else {
415 result = 2.f * (float)sqrt(-a * kThird) * (float)cos((phi * kThird) + (SK_ScalarPI * 2.f * kThird));
416 if (!between_closed(result, segment.fP0T.fX, segment.fP2T.fX)) {
417 result = 2.f * (float)sqrt(-a * kThird) * (float)cos(phi * kThird);
418 }
419 }
420 return result;
421 }
422}
423
424// This structure contains some intermediate values shared by the same row.
425// It is used to calculate segment side of a quadratic bezier.
426struct RowData {
427 // The intersection type of a scanline and y = x * x parabola in canonical space.
434
435 // The direction of the quadratic segment/scanline in the canonical space.
436 // 1: The quadratic segment/scanline going from negative x-axis to positive x-axis.
437 // 0: The scanline is a vertical line in the canonical space.
438 // -1: The quadratic segment/scanline going from positive x-axis to negative x-axis.
441
442 // The y-value(equal to x*x) of intersection point for the kVerticalLine intersection type.
444
445 // The x-value for two intersection points.
448};
449
450void precomputation_for_row(RowData *rowData, const PathSegment& segment,
451 const SkPoint& pointLeft, const SkPoint& pointRight) {
452 if (segment.fType != PathSegment::kQuad) {
453 return;
454 }
455
456 const DPoint& xFormPtLeft = segment.fXformMatrix.mapPoint(pointLeft);
457 const DPoint& xFormPtRight = segment.fXformMatrix.mapPoint(pointRight);
458
459 rowData->fQuadXDirection = (int)sign_of(segment.fP2T.fX - segment.fP0T.fX);
460 rowData->fScanlineXDirection = (int)sign_of(xFormPtRight.fX - xFormPtLeft.fX);
461
462 const double x1 = xFormPtLeft.fX;
463 const double y1 = xFormPtLeft.fY;
464 const double x2 = xFormPtRight.fX;
465 const double y2 = xFormPtRight.fY;
466
467 if (nearly_equal(x1, x2, segment.fNearlyZeroScaled, true)) {
469 rowData->fYAtIntersection = x1 * x1;
470 rowData->fScanlineXDirection = 0;
471 return;
472 }
473
474 // Line y = mx + b
475 const double m = (y2 - y1) / (x2 - x1);
476 const double b = -m * x1 + y1;
477
478 const double m2 = m * m;
479 const double c = m2 + 4.0 * b;
480
481 const double tol = 4.0 * segment.fTangentTolScaledSqd / (m2 + 1.0);
482
483 // Check if the scanline is the tangent line of the curve,
484 // and the curve start or end at the same y-coordinate of the scanline
485 if ((rowData->fScanlineXDirection == 1 &&
486 (segment.fPts[0].fY == pointLeft.fY ||
487 segment.fPts[2].fY == pointLeft.fY)) &&
488 nearly_zero(c, tol)) {
490 rowData->fXAtIntersection1 = m / 2.0;
491 rowData->fXAtIntersection2 = m / 2.0;
492 } else if (c <= 0.0) {
494 return;
495 } else {
497 const double d = sqrt(c);
498 rowData->fXAtIntersection1 = (m + d) / 2.0;
499 rowData->fXAtIntersection2 = (m - d) / 2.0;
500 }
501}
502
504 const PathSegment& segment,
505 const SkPoint& point,
506 const DPoint& xFormPt,
507 const RowData& rowData) {
509
511 side = (SegSide)(int)(sign_of(xFormPt.fY - rowData.fYAtIntersection) * rowData.fQuadXDirection);
512 }
514 const double p1 = rowData.fXAtIntersection1;
515 const double p2 = rowData.fXAtIntersection2;
516
517 int signP1 = (int)sign_of(p1 - xFormPt.fX);
518 bool includeP1 = true;
519 bool includeP2 = true;
520
521 if (rowData.fScanlineXDirection == 1) {
522 if ((rowData.fQuadXDirection == -1 && segment.fPts[0].fY <= point.fY &&
523 nearly_equal(segment.fP0T.fX, p1, segment.fNearlyZeroScaled, true)) ||
524 (rowData.fQuadXDirection == 1 && segment.fPts[2].fY <= point.fY &&
525 nearly_equal(segment.fP2T.fX, p1, segment.fNearlyZeroScaled, true))) {
526 includeP1 = false;
527 }
528 if ((rowData.fQuadXDirection == -1 && segment.fPts[2].fY <= point.fY &&
529 nearly_equal(segment.fP2T.fX, p2, segment.fNearlyZeroScaled, true)) ||
530 (rowData.fQuadXDirection == 1 && segment.fPts[0].fY <= point.fY &&
531 nearly_equal(segment.fP0T.fX, p2, segment.fNearlyZeroScaled, true))) {
532 includeP2 = false;
533 }
534 }
535
536 if (includeP1 && between_closed(p1, segment.fP0T.fX, segment.fP2T.fX,
537 segment.fNearlyZeroScaled, true)) {
538 side = (SegSide)(signP1 * rowData.fQuadXDirection);
539 }
540 if (includeP2 && between_closed(p2, segment.fP0T.fX, segment.fP2T.fX,
541 segment.fNearlyZeroScaled, true)) {
542 int signP2 = (int)sign_of(p2 - xFormPt.fX);
543 if (side == kNA_SegSide || signP2 == 1) {
544 side = (SegSide)(-signP2 * rowData.fQuadXDirection);
545 }
546 }
547 } else if (RowData::kTangentLine == rowData.fIntersectionType) {
548 // The scanline is the tangent line of current quadratic segment.
549
550 const double p = rowData.fXAtIntersection1;
551 int signP = (int)sign_of(p - xFormPt.fX);
552 if (rowData.fScanlineXDirection == 1) {
553 // The path start or end at the tangent point.
554 if (segment.fPts[0].fY == point.fY) {
555 side = (SegSide)(signP);
556 } else if (segment.fPts[2].fY == point.fY) {
557 side = (SegSide)(-signP);
558 }
559 }
560 }
561
562 return side;
563}
564
565static float distance_to_segment(const SkPoint& point,
566 const PathSegment& segment,
567 const RowData& rowData,
568 SegSide* side) {
569 SkASSERT(side);
570
571 const DPoint xformPt = segment.fXformMatrix.mapPoint(point);
572
573 if (segment.fType == PathSegment::kLine) {
575
576 if (between_closed(xformPt.fX, segment.fP0T.fX, segment.fP2T.fX)) {
577 result = (float)(xformPt.fY * xformPt.fY);
578 } else if (xformPt.fX < segment.fP0T.fX) {
579 result = (float)(xformPt.fX * xformPt.fX + xformPt.fY * xformPt.fY);
580 } else {
581 result = (float)((xformPt.fX - segment.fP2T.fX) * (xformPt.fX - segment.fP2T.fX)
582 + xformPt.fY * xformPt.fY);
583 }
584
585 if (between_closed_open(point.fY, segment.fBoundingBox.fTop,
586 segment.fBoundingBox.fBottom)) {
587 *side = (SegSide)(int)sign_of(xformPt.fY);
588 } else {
589 *side = kNA_SegSide;
590 }
591 return result;
592 } else {
594
595 const float nearestPoint = calculate_nearest_point_for_quad(segment, xformPt);
596
597 float dist;
598
599 if (between_closed(nearestPoint, segment.fP0T.fX, segment.fP2T.fX)) {
600 DPoint x = { nearestPoint, nearestPoint * nearestPoint };
601 dist = (float)xformPt.distanceSquared(x);
602 } else {
603 const float distToB0T = (float)xformPt.distanceSquared(segment.fP0T);
604 const float distToB2T = (float)xformPt.distanceSquared(segment.fP2T);
605
606 if (distToB0T < distToB2T) {
607 dist = distToB0T;
608 } else {
609 dist = distToB2T;
610 }
611 }
612
613 if (between_closed_open(point.fY, segment.fBoundingBox.fTop,
614 segment.fBoundingBox.fBottom)) {
615 *side = calculate_side_of_quad(segment, point, xformPt, rowData);
616 } else {
617 *side = kNA_SegSide;
618 }
619
620 return (float)(dist * segment.fScalingFactorSqd);
621 }
622}
623
625 DFData* dataPtr,
626 int width, int height) {
627 int count = segments->size();
628 // for each segment
629 for (int a = 0; a < count; ++a) {
630 PathSegment& segment = (*segments)[a];
631 const SkRect& segBB = segment.fBoundingBox;
632 // get the bounding box, outset by distance field pad, and clip to total bounds
634 int startColumn = (int)paddedBB.fLeft;
635 int endColumn = SkScalarCeilToInt(paddedBB.fRight);
636
637 int startRow = (int)paddedBB.fTop;
638 int endRow = SkScalarCeilToInt(paddedBB.fBottom);
639
640 SkASSERT((startColumn >= 0) && "StartColumn < 0!");
641 SkASSERT((endColumn <= width) && "endColumn > width!");
642 SkASSERT((startRow >= 0) && "StartRow < 0!");
643 SkASSERT((endRow <= height) && "EndRow > height!");
644
645 // Clip inside the distance field to avoid overflow
646 startColumn = std::max(startColumn, 0);
647 endColumn = std::min(endColumn, width);
648 startRow = std::max(startRow, 0);
649 endRow = std::min(endRow, height);
650
651 // for each row in the padded bounding box
652 for (int row = startRow; row < endRow; ++row) {
653 SegSide prevSide = kNA_SegSide; // track side for winding count
654 const float pY = row + 0.5f; // offset by 1/2? why?
655 RowData rowData;
656
657 const SkPoint pointLeft = SkPoint::Make((SkScalar)startColumn, pY);
658 const SkPoint pointRight = SkPoint::Make((SkScalar)endColumn, pY);
659
660 // if this is a row inside the original segment bounding box
661 if (between_closed_open(pY, segBB.fTop, segBB.fBottom)) {
662 // compute intersections with the row
663 precomputation_for_row(&rowData, segment, pointLeft, pointRight);
664 }
665
666 // adjust distances and windings in each column based on the row calculation
667 for (int col = startColumn; col < endColumn; ++col) {
668 int idx = (row * width) + col;
669
670 const float pX = col + 0.5f;
671 const SkPoint point = SkPoint::Make(pX, pY);
672
673 const float distSq = dataPtr[idx].fDistSq;
674
675 // Optimization for not calculating some points.
676 int dilation = distSq < 1.5f * 1.5f ? 1 :
677 distSq < 2.5f * 2.5f ? 2 :
678 distSq < 3.5f * 3.5f ? 3 : SK_DistanceFieldPad;
679 if (dilation < SK_DistanceFieldPad &&
680 !segBB.roundOut().makeOutset(dilation, dilation).contains(col, row)) {
681 continue;
682 }
683
685 int deltaWindingScore = 0;
686 float currDistSq = distance_to_segment(point, segment, rowData, &side);
687 if (prevSide == kLeft_SegSide && side == kRight_SegSide) {
688 deltaWindingScore = -1;
689 } else if (prevSide == kRight_SegSide && side == kLeft_SegSide) {
690 deltaWindingScore = 1;
691 }
692
693 prevSide = side;
694
695 if (currDistSq < distSq) {
696 dataPtr[idx].fDistSq = currDistSq;
697 }
698
699 dataPtr[idx].fDeltaWindingScore += deltaWindingScore;
700 }
701 }
702 }
703}
704
705template <int distanceMagnitude>
706static unsigned char pack_distance_field_val(float dist) {
707 // The distance field is constructed as unsigned char values, so that the zero value is at 128,
708 // Beside 128, we have 128 values in range [0, 128), but only 127 values in range (128, 255].
709 // So we multiply distanceMagnitude by 127/128 at the latter range to avoid overflow.
710 dist = SkTPin<float>(-dist, -distanceMagnitude, distanceMagnitude * 127.0f / 128.0f);
711
712 // Scale into the positive range for unsigned distance.
713 dist += distanceMagnitude;
714
715 // Scale into unsigned char range.
716 // Round to place negative and positive values as equally as possible around 128
717 // (which represents zero).
718 return (unsigned char)SkScalarRoundToInt(dist / (2 * distanceMagnitude) * 256.0f);
719}
720
721bool GrGenerateDistanceFieldFromPath(unsigned char* distanceField,
722 const SkPath& path, const SkMatrix& drawMatrix,
723 int width, int height, size_t rowBytes) {
724 SkASSERT(distanceField);
725
726 // transform to device space, then:
727 // translate path to offset (SK_DistanceFieldPad, SK_DistanceFieldPad)
728 SkMatrix dfMatrix(drawMatrix);
730
731#ifdef SK_DEBUG
732 SkPath xformPath;
733 path.transform(dfMatrix, &xformPath);
734 SkIRect pathBounds = xformPath.getBounds().roundOut();
735 SkIRect expectPathBounds = SkIRect::MakeWH(width, height);
736#endif
737
738 SkASSERT(expectPathBounds.isEmpty() ||
739 expectPathBounds.contains(pathBounds.fLeft, pathBounds.fTop));
740 SkASSERT(expectPathBounds.isEmpty() || pathBounds.isEmpty() ||
741 expectPathBounds.contains(pathBounds));
742
743// TODO: restore when Simplify() is working correctly
744// see https://bugs.chromium.org/p/skia/issues/detail?id=9732
745// SkPath simplifiedPath;
746 SkPath workingPath;
747// if (Simplify(path, &simplifiedPath)) {
748// workingPath = simplifiedPath;
749// } else {
750 workingPath = path;
751// }
752 // only even-odd and inverse even-odd supported
753 if (!IsDistanceFieldSupportedFillType(workingPath.getFillType())) {
754 return false;
755 }
756
757 // transform to device space + SDF offset
758 // TODO: remove degenerate segments while doing this?
759 workingPath.transform(dfMatrix);
760
761 SkDEBUGCODE(pathBounds = workingPath.getBounds().roundOut());
762 SkASSERT(expectPathBounds.isEmpty() ||
763 expectPathBounds.contains(pathBounds.fLeft, pathBounds.fTop));
764 SkASSERT(expectPathBounds.isEmpty() || pathBounds.isEmpty() ||
765 expectPathBounds.contains(pathBounds));
766
767 // create temp data
768 size_t dataSize = width * height * sizeof(DFData);
769 SkAutoSMalloc<1024> dfStorage(dataSize);
770 DFData* dataPtr = (DFData*) dfStorage.get();
771
772 // create initial distance data (init to "far away")
773 init_distances(dataPtr, width * height);
774
775 // polygonize path into line and quad segments
776 SkPathEdgeIter iter(workingPath);
778 while (auto e = iter.next()) {
779 switch (e.fEdge) {
781 add_line(e.fPts, &segments);
782 break;
783 }
785 add_quad(e.fPts, &segments);
786 break;
788 SkScalar weight = iter.conicWeight();
790 const SkPoint* quadPts = converter.computeQuads(e.fPts, weight, kConicTolerance);
791 for (int i = 0; i < converter.countQuads(); ++i) {
792 add_quad(quadPts + 2*i, &segments);
793 }
794 break;
795 }
797 add_cubic(e.fPts, &segments);
798 break;
799 }
800 }
801 }
802
803 // do all the work
804 calculate_distance_field_data(&segments, dataPtr, width, height);
805
806 // adjust distance based on winding
807 for (int row = 0; row < height; ++row) {
808 using DFSign = int;
809 constexpr DFSign kInside = -1;
810 constexpr DFSign kOutside = 1;
811
812 int windingNumber = 0; // Winding number start from zero for each scanline
813 for (int col = 0; col < width; ++col) {
814 int idx = (row * width) + col;
815 windingNumber += dataPtr[idx].fDeltaWindingScore;
816
817 DFSign dfSign;
818 switch (workingPath.getFillType()) {
820 dfSign = windingNumber ? kInside : kOutside;
821 break;
823 dfSign = windingNumber ? kOutside : kInside;
824 break;
826 dfSign = (windingNumber % 2) ? kInside : kOutside;
827 break;
829 dfSign = (windingNumber % 2) ? kOutside : kInside;
830 break;
831 }
832
833 const float miniDist = sqrt(dataPtr[idx].fDistSq);
834 const float dist = dfSign * miniDist;
835
836 unsigned char pixelVal = pack_distance_field_val<SK_DistanceFieldMagnitude>(dist);
837
838 distanceField[(row * rowBytes) + col] = pixelVal;
839 }
840
841 // The winding number at the end of a scanline should be zero.
842 if (windingNumber != 0) {
843 SkDEBUGFAIL("Winding number should be zero at the end of a scan line.");
844 // Fallback to use SkPath::contains to determine the sign of pixel in release build.
845 for (int col = 0; col < width; ++col) {
846 int idx = (row * width) + col;
847 DFSign dfSign = workingPath.contains(col + 0.5, row + 0.5) ? kInside : kOutside;
848 const float miniDist = sqrt(dataPtr[idx].fDistSq);
849 const float dist = dfSign * miniDist;
850
851 unsigned char pixelVal = pack_distance_field_val<SK_DistanceFieldMagnitude>(dist);
852
853 distanceField[(row * rowBytes) + col] = pixelVal;
854 }
855 continue;
856 }
857 }
858 return true;
859}
860
861#endif // SK_ENABLE_OPTIMIZE_SIZE
int count
Definition: FontMgrTest.cpp:50
void precomputation_for_row(RowData *rowData, const PathSegment &segment, const SkPoint &pointLeft, const SkPoint &pointRight)
static const double kClose
static bool is_colinear(const SkPoint pts[3])
static void init_distances(DFData *data, int size)
static unsigned char pack_distance_field_val(float dist)
SegSide calculate_side_of_quad(const PathSegment &segment, const SkPoint &point, const DPoint &xFormPt, const RowData &rowData)
static void calculate_distance_field_data(PathSegmentArray *segments, DFData *dataPtr, int width, int height)
static void add_quad(const SkPoint pts[3], PathSegmentArray *segments)
static const float kConicTolerance
static double sign_of(const double &val)
static float distance_to_segment(const SkPoint &point, const PathSegment &segment, const RowData &rowData, SegSide *side)
static float calculate_nearest_point_for_quad(const PathSegment &segment, const DPoint &xFormPt)
static const double kNearlyZero
static const double kTangentTolerance
static bool nearly_zero(double x, double tolerance=kNearlyZero)
static bool between_closed_open(double a, double b, double c, double tolerance=0.0, bool xformToleranceToX=false)
static void add_line(const SkPoint pts[2], PathSegmentArray *segments)
static void add_cubic(const SkPoint pts[4], PathSegmentArray *segments)
static bool between_closed(double a, double b, double c, double tolerance=0.0, bool xformToleranceToX=false)
TArray< PathSegment, true > PathSegmentArray
static bool nearly_equal(double x, double y, double tolerance=kNearlyZero, bool xformToleranceToX=false)
static const double kCloseSqd
bool GrGenerateDistanceFieldFromPath(unsigned char *distanceField, const SkPath &path, const SkMatrix &drawMatrix, int width, int height, size_t rowBytes)
bool IsDistanceFieldSupportedFillType(SkPathFillType fFillType)
#define SkDEBUGFAIL(message)
Definition: SkAssert.h:118
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define SK_DistanceFieldPad
#define SK_DistanceFieldMagnitude
static int side(double x)
#define SK_Scalar1
Definition: SkScalar.h:18
#define SkScalarRoundToInt(x)
Definition: SkScalar.h:37
#define SkScalarCeilToInt(x)
Definition: SkScalar.h:36
#define SK_ScalarPI
Definition: SkScalar.h:21
static T SkTAbs(T value)
Definition: SkTemplates.h:43
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
DPoint mapPoint(const DPoint &src) const
DPoint mapPoint(const SkPoint &src) const
void setAffine(double m11, double m12, double m13, double m21, double m22, double m23)
double & operator[](int index)
double operator[](int index) const
const SkPoint & endPt() const
enum PathSegment::@370 fType
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:281
Result next()
Definition: SkPathPriv.h:485
SkScalar conicWeight() const
Definition: SkPathPriv.h:460
Definition: SkPath.h:59
SkPathFillType getFillType() const
Definition: SkPath.h:230
const SkRect & getBounds() const
Definition: SkPath.cpp:430
void transform(const SkMatrix &matrix, SkPath *dst, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkPath.cpp:1711
bool contains(SkScalar x, SkScalar y) const
Definition: SkPath.cpp:3118
static SkScalar DistanceToSqd(const SkPoint &pt, const SkPoint &a)
Definition: SkPointPriv.h:48
static void GrowToInclude(SkRect *r, const SkPoint &pt)
Definition: SkRectPriv.h:47
int size() const
Definition: SkTArray.h:421
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]
GAsyncResult * result
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
double y
double x
void convertCubicToQuads(const SkPoint p[4], SkScalar tolScale, skia_private::TArray< SkPoint, true > *quads)
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
string converter
Definition: cacheimages.py:19
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
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
SkScalar h
int32_t height
int32_t width
enum RowData::IntersectionType fIntersectionType
Definition: SkRect.h:32
int32_t fTop
smaller y-axis bounds
Definition: SkRect.h:34
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
bool isEmpty() const
Definition: SkRect.h:202
int32_t fLeft
smaller x-axis bounds
Definition: SkRect.h:33
bool contains(int32_t x, int32_t y) const
Definition: SkRect.h:463
float fX
x-axis value
Definition: SkPoint_impl.h:164
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
float fY
y-axis value
Definition: SkPoint_impl.h:165
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
SkRect makeOutset(float dx, float dy) const
Definition: SkRect.h:1002
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
void roundOut(SkIRect *dst) const
Definition: SkRect.h:1241
void set(const SkIRect &src)
Definition: SkRect.h:849
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63