blob: 84b9097501ab4115b7b7b18f65e2d0259cb48914 [file] [log] [blame]
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +00001/*
2 Copyright 2011 Google Inc.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17#include "GrPathUtils.h"
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000018#include "GrPoint.h"
19
bsalomon@google.comb5b31682011-06-16 18:05:35 +000020static const int MAX_POINTS_PER_CURVE = 1 << 10;
tomhudson@google.comc10a8882011-06-28 15:19:32 +000021const GrScalar GrPathUtils::gMinCurveTol (GrFloatToScalar(0.0001f));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000022
23uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[],
tomhudson@google.comc10a8882011-06-28 15:19:32 +000024 GrScalar tol) {
25 if (tol < gMinCurveTol) {
tomhudson@google.comafec7ba2011-06-30 14:47:55 +000026 tol = gMinCurveTol;
tomhudson@google.comc10a8882011-06-28 15:19:32 +000027 }
28 GrAssert(tol > 0);
29
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000030 GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
tomhudson@google.comc10a8882011-06-28 15:19:32 +000031 if (d <= tol) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000032 return 1;
33 } else {
34 // Each time we subdivide, d should be cut in 4. So we need to
35 // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
36 // points.
37 // 2^(log4(x)) = sqrt(x);
epoger@google.com2047f002011-05-17 17:36:59 +000038 int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
bsalomon@google.com61f3bde2011-06-17 20:06:49 +000039 int pow2 = GrNextPow2(temp);
40 // Because of NaNs & INFs we can wind up with a degenerate temp
41 // such that pow2 comes out negative. Also, our point generator
42 // will always output at least one pt.
43 if (pow2 < 1) {
44 pow2 = 1;
45 }
46 return GrMin(pow2, MAX_POINTS_PER_CURVE);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000047 }
48}
49
50uint32_t GrPathUtils::generateQuadraticPoints(const GrPoint& p0,
tomhudson@google.comc10a8882011-06-28 15:19:32 +000051 const GrPoint& p1,
52 const GrPoint& p2,
53 GrScalar tolSqd,
54 GrPoint** points,
55 uint32_t pointsLeft) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000056 if (pointsLeft < 2 ||
57 (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) {
58 (*points)[0] = p2;
59 *points += 1;
60 return 1;
61 }
62
63 GrPoint q[] = {
reed@google.com7744c202011-05-06 19:26:26 +000064 { GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY) },
65 { GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY) },
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000066 };
reed@google.com7744c202011-05-06 19:26:26 +000067 GrPoint r = { GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY) };
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000068
69 pointsLeft >>= 1;
70 uint32_t a = generateQuadraticPoints(p0, q[0], r, tolSqd, points, pointsLeft);
71 uint32_t b = generateQuadraticPoints(r, q[1], p2, tolSqd, points, pointsLeft);
72 return a + b;
73}
74
75uint32_t GrPathUtils::cubicPointCount(const GrPoint points[],
76 GrScalar tol) {
tomhudson@google.comc10a8882011-06-28 15:19:32 +000077 if (tol < gMinCurveTol) {
tomhudson@google.comafec7ba2011-06-30 14:47:55 +000078 tol = gMinCurveTol;
tomhudson@google.comc10a8882011-06-28 15:19:32 +000079 }
80 GrAssert(tol > 0);
81
82 GrScalar d = GrMax(
83 points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
84 points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
epoger@google.com2047f002011-05-17 17:36:59 +000085 d = SkScalarSqrt(d);
tomhudson@google.comc10a8882011-06-28 15:19:32 +000086 if (d <= tol) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000087 return 1;
88 } else {
epoger@google.com2047f002011-05-17 17:36:59 +000089 int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
bsalomon@google.com61f3bde2011-06-17 20:06:49 +000090 int pow2 = GrNextPow2(temp);
91 // Because of NaNs & INFs we can wind up with a degenerate temp
92 // such that pow2 comes out negative. Also, our point generator
93 // will always output at least one pt.
94 if (pow2 < 1) {
95 pow2 = 1;
96 }
97 return GrMin(pow2, MAX_POINTS_PER_CURVE);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000098 }
99}
100
101uint32_t GrPathUtils::generateCubicPoints(const GrPoint& p0,
tomhudson@google.comc10a8882011-06-28 15:19:32 +0000102 const GrPoint& p1,
103 const GrPoint& p2,
104 const GrPoint& p3,
105 GrScalar tolSqd,
106 GrPoint** points,
107 uint32_t pointsLeft) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000108 if (pointsLeft < 2 ||
109 (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd &&
110 p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) {
111 (*points)[0] = p3;
112 *points += 1;
113 return 1;
114 }
115 GrPoint q[] = {
reed@google.com7744c202011-05-06 19:26:26 +0000116 { GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY) },
117 { GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY) },
118 { GrScalarAve(p2.fX, p3.fX), GrScalarAve(p2.fY, p3.fY) }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000119 };
120 GrPoint r[] = {
reed@google.com7744c202011-05-06 19:26:26 +0000121 { GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY) },
122 { GrScalarAve(q[1].fX, q[2].fX), GrScalarAve(q[1].fY, q[2].fY) }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000123 };
reed@google.com7744c202011-05-06 19:26:26 +0000124 GrPoint s = { GrScalarAve(r[0].fX, r[1].fX), GrScalarAve(r[0].fY, r[1].fY) };
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000125 pointsLeft >>= 1;
126 uint32_t a = generateCubicPoints(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
127 uint32_t b = generateCubicPoints(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
128 return a + b;
129}
130
reed@google.com07f3ee12011-05-16 17:21:57 +0000131int GrPathUtils::worstCasePointCount(const GrPath& path, int* subpaths,
132 GrScalar tol) {
tomhudson@google.comc10a8882011-06-28 15:19:32 +0000133 if (tol < gMinCurveTol) {
tomhudson@google.comafec7ba2011-06-30 14:47:55 +0000134 tol = gMinCurveTol;
tomhudson@google.comc10a8882011-06-28 15:19:32 +0000135 }
136 GrAssert(tol > 0);
137
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000138 int pointCount = 0;
139 *subpaths = 1;
140
141 bool first = true;
142
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000143 SkPath::Iter iter(path, false);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000144 GrPathCmd cmd;
145
146 GrPoint pts[4];
reed@google.com07f3ee12011-05-16 17:21:57 +0000147 while ((cmd = (GrPathCmd)iter.next(pts)) != kEnd_PathCmd) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000148
149 switch (cmd) {
150 case kLine_PathCmd:
151 pointCount += 1;
152 break;
153 case kQuadratic_PathCmd:
154 pointCount += quadraticPointCount(pts, tol);
155 break;
156 case kCubic_PathCmd:
157 pointCount += cubicPointCount(pts, tol);
158 break;
159 case kMove_PathCmd:
160 pointCount += 1;
161 if (!first) {
162 ++(*subpaths);
163 }
164 break;
165 default:
166 break;
167 }
168 first = false;
169 }
170 return pointCount;
171}