/src/skia/src/pathops/SkPathOpsConic.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2015 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 | | #include "src/pathops/SkPathOpsConic.h" |
8 | | |
9 | | #include "include/core/SkTypes.h" |
10 | | #include "include/private/base/SkFloatingPoint.h" |
11 | | #include "src/pathops/SkIntersections.h" |
12 | | #include "src/pathops/SkPathOpsCubic.h" |
13 | | #include "src/pathops/SkPathOpsQuad.h" |
14 | | #include "src/pathops/SkPathOpsRect.h" |
15 | | #include "src/pathops/SkPathOpsTypes.h" |
16 | | |
17 | | #include <cmath> |
18 | | |
19 | | struct SkDLine; |
20 | | |
21 | | // cribbed from the float version in SkGeometry.cpp |
22 | | static void conic_deriv_coeff(const double src[], |
23 | | SkScalar w, |
24 | 49.8M | double coeff[3]) { |
25 | 49.8M | const double P20 = src[4] - src[0]; |
26 | 49.8M | const double P10 = src[2] - src[0]; |
27 | 49.8M | const double wP10 = w * P10; |
28 | 49.8M | coeff[0] = w * P20 - P20; |
29 | 49.8M | coeff[1] = P20 - 2 * wP10; |
30 | 49.8M | coeff[2] = wP10; |
31 | 49.8M | } |
32 | | |
33 | 41.0M | static double conic_eval_tan(const double coord[], SkScalar w, double t) { |
34 | 41.0M | double coeff[3]; |
35 | 41.0M | conic_deriv_coeff(coord, w, coeff); |
36 | 41.0M | return t * (t * coeff[0] + coeff[1]) + coeff[2]; |
37 | 41.0M | } |
38 | | |
39 | 8.82M | int SkDConic::FindExtrema(const double src[], SkScalar w, double t[1]) { |
40 | 8.82M | double coeff[3]; |
41 | 8.82M | conic_deriv_coeff(src, w, coeff); |
42 | | |
43 | 8.82M | double tValues[2]; |
44 | 8.82M | int roots = SkDQuad::RootsValidT(coeff[0], coeff[1], coeff[2], tValues); |
45 | | // In extreme cases, the number of roots returned can be 2. Pathops |
46 | | // will fail later on, so there's no advantage to plumbing in an error |
47 | | // return here. |
48 | | // SkASSERT(0 == roots || 1 == roots); |
49 | | |
50 | 8.82M | if (1 == roots) { |
51 | 8.45M | t[0] = tValues[0]; |
52 | 8.45M | return 1; |
53 | 8.45M | } |
54 | 368k | return 0; |
55 | 8.82M | } |
56 | | |
57 | 20.5M | SkDVector SkDConic::dxdyAtT(double t) const { |
58 | 20.5M | SkDVector result = { |
59 | 20.5M | conic_eval_tan(&fPts[0].fX, fWeight, t), |
60 | 20.5M | conic_eval_tan(&fPts[0].fY, fWeight, t) |
61 | 20.5M | }; |
62 | 20.5M | if (result.fX == 0 && result.fY == 0) { |
63 | 2.38k | if (zero_or_one(t)) { |
64 | 1.44k | result = fPts[2] - fPts[0]; |
65 | 1.44k | } else { |
66 | | // incomplete |
67 | 942 | SkDebugf("!k"); |
68 | 942 | } |
69 | 2.38k | } |
70 | 20.5M | return result; |
71 | 20.5M | } |
72 | | |
73 | 835M | static double conic_eval_numerator(const double src[], SkScalar w, double t) { |
74 | 835M | SkASSERT(src); |
75 | 835M | SkASSERT(t >= 0 && t <= 1); |
76 | 835M | double src2w = src[2] * w; |
77 | 835M | double C = src[0]; |
78 | 835M | double A = src[4] - 2 * src2w + C; |
79 | 835M | double B = 2 * (src2w - C); |
80 | 835M | return (A * t + B) * t + C; |
81 | 835M | } |
82 | | |
83 | | |
84 | 417M | static double conic_eval_denominator(SkScalar w, double t) { |
85 | 417M | double B = 2 * (w - 1); |
86 | 417M | double C = 1; |
87 | 417M | double A = -B; |
88 | 417M | return (A * t + B) * t + C; |
89 | 417M | } |
90 | | |
91 | 2.86M | bool SkDConic::hullIntersects(const SkDCubic& cubic, bool* isLinear) const { |
92 | 2.86M | return cubic.hullIntersects(*this, isLinear); |
93 | 2.86M | } |
94 | | |
95 | 130M | SkDPoint SkDConic::ptAtT(double t) const { |
96 | 130M | if (t == 0) { |
97 | 16.7M | return fPts[0]; |
98 | 16.7M | } |
99 | 113M | if (t == 1) { |
100 | 15.6M | return fPts[2]; |
101 | 15.6M | } |
102 | 97.8M | double denominator = conic_eval_denominator(fWeight, t); |
103 | 97.8M | SkDPoint result = { |
104 | 97.8M | sk_ieee_double_divide(conic_eval_numerator(&fPts[0].fX, fWeight, t), denominator), |
105 | 97.8M | sk_ieee_double_divide(conic_eval_numerator(&fPts[0].fY, fWeight, t), denominator) |
106 | 97.8M | }; |
107 | 97.8M | return result; |
108 | 113M | } |
109 | | |
110 | | /* see quad subdivide for point rationale */ |
111 | | /* w rationale : the mid point between t1 and t2 could be determined from the computed a/b/c |
112 | | values if the computed w was known. Since we know the mid point at (t1+t2)/2, we'll assume |
113 | | that it is the same as the point on the new curve t==(0+1)/2. |
114 | | |
115 | | d / dz == conic_poly(dst, unknownW, .5) / conic_weight(unknownW, .5); |
116 | | |
117 | | conic_poly(dst, unknownW, .5) |
118 | | = a / 4 + (b * unknownW) / 2 + c / 4 |
119 | | = (a + c) / 4 + (bx * unknownW) / 2 |
120 | | |
121 | | conic_weight(unknownW, .5) |
122 | | = unknownW / 2 + 1 / 2 |
123 | | |
124 | | d / dz == ((a + c) / 2 + b * unknownW) / (unknownW + 1) |
125 | | d / dz * (unknownW + 1) == (a + c) / 2 + b * unknownW |
126 | | unknownW = ((a + c) / 2 - d / dz) / (d / dz - b) |
127 | | |
128 | | Thus, w is the ratio of the distance from the mid of end points to the on-curve point, and the |
129 | | distance of the on-curve point to the control point. |
130 | | */ |
131 | 262M | SkDConic SkDConic::subDivide(double t1, double t2) const { |
132 | 262M | double ax, ay, az; |
133 | 262M | if (t1 == 0) { |
134 | 73.6M | ax = fPts[0].fX; |
135 | 73.6M | ay = fPts[0].fY; |
136 | 73.6M | az = 1; |
137 | 188M | } else if (t1 != 1) { |
138 | 187M | ax = conic_eval_numerator(&fPts[0].fX, fWeight, t1); |
139 | 187M | ay = conic_eval_numerator(&fPts[0].fY, fWeight, t1); |
140 | 187M | az = conic_eval_denominator(fWeight, t1); |
141 | 187M | } else { |
142 | 1.22M | ax = fPts[2].fX; |
143 | 1.22M | ay = fPts[2].fY; |
144 | 1.22M | az = 1; |
145 | 1.22M | } |
146 | 262M | double midT = (t1 + t2) / 2; |
147 | 262M | double dx = conic_eval_numerator(&fPts[0].fX, fWeight, midT); |
148 | 262M | double dy = conic_eval_numerator(&fPts[0].fY, fWeight, midT); |
149 | 262M | double dz = conic_eval_denominator(fWeight, midT); |
150 | 262M | double cx, cy, cz; |
151 | 262M | if (t2 == 1) { |
152 | 71.7M | cx = fPts[2].fX; |
153 | 71.7M | cy = fPts[2].fY; |
154 | 71.7M | cz = 1; |
155 | 190M | } else if (t2 != 0) { |
156 | 189M | cx = conic_eval_numerator(&fPts[0].fX, fWeight, t2); |
157 | 189M | cy = conic_eval_numerator(&fPts[0].fY, fWeight, t2); |
158 | 189M | cz = conic_eval_denominator(fWeight, t2); |
159 | 189M | } else { |
160 | 1.68M | cx = fPts[0].fX; |
161 | 1.68M | cy = fPts[0].fY; |
162 | 1.68M | cz = 1; |
163 | 1.68M | } |
164 | 262M | double bx = 2 * dx - (ax + cx) / 2; |
165 | 262M | double by = 2 * dy - (ay + cy) / 2; |
166 | 262M | double bz = 2 * dz - (az + cz) / 2; |
167 | 262M | if (!bz) { |
168 | 47.1k | bz = 1; // if bz is 0, weight is 0, control point has no effect: any value will do |
169 | 47.1k | } |
170 | 262M | SkDConic dst = {{{{ax / az, ay / az}, {bx / bz, by / bz}, {cx / cz, cy / cz}} |
171 | 262M | SkDEBUGPARAMS(fPts.fDebugGlobalState) }, |
172 | 262M | SkDoubleToScalar(bz / sqrt(az * cz)) }; |
173 | 262M | return dst; |
174 | 262M | } SkDConic::subDivide(double, double) const Line | Count | Source | 131 | 131M | SkDConic SkDConic::subDivide(double t1, double t2) const { | 132 | 131M | double ax, ay, az; | 133 | 131M | if (t1 == 0) { | 134 | 36.8M | ax = fPts[0].fX; | 135 | 36.8M | ay = fPts[0].fY; | 136 | 36.8M | az = 1; | 137 | 94.4M | } else if (t1 != 1) { | 138 | 93.8M | ax = conic_eval_numerator(&fPts[0].fX, fWeight, t1); | 139 | 93.8M | ay = conic_eval_numerator(&fPts[0].fY, fWeight, t1); | 140 | 93.8M | az = conic_eval_denominator(fWeight, t1); | 141 | 93.8M | } else { | 142 | 610k | ax = fPts[2].fX; | 143 | 610k | ay = fPts[2].fY; | 144 | 610k | az = 1; | 145 | 610k | } | 146 | 131M | double midT = (t1 + t2) / 2; | 147 | 131M | double dx = conic_eval_numerator(&fPts[0].fX, fWeight, midT); | 148 | 131M | double dy = conic_eval_numerator(&fPts[0].fY, fWeight, midT); | 149 | 131M | double dz = conic_eval_denominator(fWeight, midT); | 150 | 131M | double cx, cy, cz; | 151 | 131M | if (t2 == 1) { | 152 | 35.8M | cx = fPts[2].fX; | 153 | 35.8M | cy = fPts[2].fY; | 154 | 35.8M | cz = 1; | 155 | 95.4M | } else if (t2 != 0) { | 156 | 94.6M | cx = conic_eval_numerator(&fPts[0].fX, fWeight, t2); | 157 | 94.6M | cy = conic_eval_numerator(&fPts[0].fY, fWeight, t2); | 158 | 94.6M | cz = conic_eval_denominator(fWeight, t2); | 159 | 94.6M | } else { | 160 | 841k | cx = fPts[0].fX; | 161 | 841k | cy = fPts[0].fY; | 162 | 841k | cz = 1; | 163 | 841k | } | 164 | 131M | double bx = 2 * dx - (ax + cx) / 2; | 165 | 131M | double by = 2 * dy - (ay + cy) / 2; | 166 | 131M | double bz = 2 * dz - (az + cz) / 2; | 167 | 131M | if (!bz) { | 168 | 23.5k | bz = 1; // if bz is 0, weight is 0, control point has no effect: any value will do | 169 | 23.5k | } | 170 | 131M | SkDConic dst = {{{{ax / az, ay / az}, {bx / bz, by / bz}, {cx / cz, cy / cz}} | 171 | 131M | SkDEBUGPARAMS(fPts.fDebugGlobalState) }, | 172 | 131M | SkDoubleToScalar(bz / sqrt(az * cz)) }; | 173 | 131M | return dst; | 174 | 131M | } |
SkDConic::subDivide(double, double) const Line | Count | Source | 131 | 131M | SkDConic SkDConic::subDivide(double t1, double t2) const { | 132 | 131M | double ax, ay, az; | 133 | 131M | if (t1 == 0) { | 134 | 36.8M | ax = fPts[0].fX; | 135 | 36.8M | ay = fPts[0].fY; | 136 | 36.8M | az = 1; | 137 | 94.4M | } else if (t1 != 1) { | 138 | 93.8M | ax = conic_eval_numerator(&fPts[0].fX, fWeight, t1); | 139 | 93.8M | ay = conic_eval_numerator(&fPts[0].fY, fWeight, t1); | 140 | 93.8M | az = conic_eval_denominator(fWeight, t1); | 141 | 93.8M | } else { | 142 | 610k | ax = fPts[2].fX; | 143 | 610k | ay = fPts[2].fY; | 144 | 610k | az = 1; | 145 | 610k | } | 146 | 131M | double midT = (t1 + t2) / 2; | 147 | 131M | double dx = conic_eval_numerator(&fPts[0].fX, fWeight, midT); | 148 | 131M | double dy = conic_eval_numerator(&fPts[0].fY, fWeight, midT); | 149 | 131M | double dz = conic_eval_denominator(fWeight, midT); | 150 | 131M | double cx, cy, cz; | 151 | 131M | if (t2 == 1) { | 152 | 35.8M | cx = fPts[2].fX; | 153 | 35.8M | cy = fPts[2].fY; | 154 | 35.8M | cz = 1; | 155 | 95.4M | } else if (t2 != 0) { | 156 | 94.6M | cx = conic_eval_numerator(&fPts[0].fX, fWeight, t2); | 157 | 94.6M | cy = conic_eval_numerator(&fPts[0].fY, fWeight, t2); | 158 | 94.6M | cz = conic_eval_denominator(fWeight, t2); | 159 | 94.6M | } else { | 160 | 841k | cx = fPts[0].fX; | 161 | 841k | cy = fPts[0].fY; | 162 | 841k | cz = 1; | 163 | 841k | } | 164 | 131M | double bx = 2 * dx - (ax + cx) / 2; | 165 | 131M | double by = 2 * dy - (ay + cy) / 2; | 166 | 131M | double bz = 2 * dz - (az + cz) / 2; | 167 | 131M | if (!bz) { | 168 | 23.5k | bz = 1; // if bz is 0, weight is 0, control point has no effect: any value will do | 169 | 23.5k | } | 170 | 131M | SkDConic dst = {{{{ax / az, ay / az}, {bx / bz, by / bz}, {cx / cz, cy / cz}} | 171 | 131M | SkDEBUGPARAMS(fPts.fDebugGlobalState) }, | 172 | 131M | SkDoubleToScalar(bz / sqrt(az * cz)) }; | 173 | 131M | return dst; | 174 | 131M | } |
|
175 | | |
176 | | SkDPoint SkDConic::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2, |
177 | 3.80M | SkScalar* weight) const { |
178 | 3.80M | SkDConic chopped = this->subDivide(t1, t2); |
179 | 3.80M | *weight = chopped.fWeight; |
180 | 3.80M | return chopped[1]; |
181 | 3.80M | } |
182 | | |
183 | 69.3M | int SkTConic::intersectRay(SkIntersections* i, const SkDLine& line) const { |
184 | 69.3M | return i->intersectRay(fConic, line); |
185 | 69.3M | } |
186 | | |
187 | 5.44M | bool SkTConic::hullIntersects(const SkDQuad& quad, bool* isLinear) const { |
188 | 5.44M | return quad.hullIntersects(fConic, isLinear); |
189 | 5.44M | } |
190 | | |
191 | 4.67M | bool SkTConic::hullIntersects(const SkDCubic& cubic, bool* isLinear) const { |
192 | 4.67M | return cubic.hullIntersects(fConic, isLinear); |
193 | 4.67M | } |
194 | | |
195 | 127M | void SkTConic::setBounds(SkDRect* rect) const { |
196 | 127M | rect->setBounds(fConic); |
197 | 127M | } |