blob: aebdf1d090c21c133c3f755d3a8bfbf214056173 [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
20const GrScalar GrPathUtils::gTolerance = GR_Scalar1;
21
bsalomon@google.comb5b31682011-06-16 18:05:35 +000022static const int MAX_POINTS_PER_CURVE = 1 << 10;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000023
24uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[],
25 GrScalar tol) {
26 GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
27 if (d < tol) {
28 return 1;
29 } else {
30 // Each time we subdivide, d should be cut in 4. So we need to
31 // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
32 // points.
33 // 2^(log4(x)) = sqrt(x);
epoger@google.com2047f002011-05-17 17:36:59 +000034 int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
bsalomon@google.com61f3bde2011-06-17 20:06:49 +000035 int pow2 = GrNextPow2(temp);
36 // Because of NaNs & INFs we can wind up with a degenerate temp
37 // such that pow2 comes out negative. Also, our point generator
38 // will always output at least one pt.
39 if (pow2 < 1) {
40 pow2 = 1;
41 }
42 return GrMin(pow2, MAX_POINTS_PER_CURVE);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000043 }
44}
45
46uint32_t GrPathUtils::generateQuadraticPoints(const GrPoint& p0,
47 const GrPoint& p1,
48 const GrPoint& p2,
49 GrScalar tolSqd,
50 GrPoint** points,
51 uint32_t pointsLeft) {
52 if (pointsLeft < 2 ||
53 (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) {
54 (*points)[0] = p2;
55 *points += 1;
56 return 1;
57 }
58
59 GrPoint q[] = {
reed@google.com7744c202011-05-06 19:26:26 +000060 { GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY) },
61 { GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY) },
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000062 };
reed@google.com7744c202011-05-06 19:26:26 +000063 GrPoint r = { GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY) };
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000064
65 pointsLeft >>= 1;
66 uint32_t a = generateQuadraticPoints(p0, q[0], r, tolSqd, points, pointsLeft);
67 uint32_t b = generateQuadraticPoints(r, q[1], p2, tolSqd, points, pointsLeft);
68 return a + b;
69}
70
71uint32_t GrPathUtils::cubicPointCount(const GrPoint points[],
72 GrScalar tol) {
73 GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
74 points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
epoger@google.com2047f002011-05-17 17:36:59 +000075 d = SkScalarSqrt(d);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000076 if (d < tol) {
77 return 1;
78 } else {
epoger@google.com2047f002011-05-17 17:36:59 +000079 int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
bsalomon@google.com61f3bde2011-06-17 20:06:49 +000080 int pow2 = GrNextPow2(temp);
81 // Because of NaNs & INFs we can wind up with a degenerate temp
82 // such that pow2 comes out negative. Also, our point generator
83 // will always output at least one pt.
84 if (pow2 < 1) {
85 pow2 = 1;
86 }
87 return GrMin(pow2, MAX_POINTS_PER_CURVE);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000088 }
89}
90
91uint32_t GrPathUtils::generateCubicPoints(const GrPoint& p0,
92 const GrPoint& p1,
93 const GrPoint& p2,
94 const GrPoint& p3,
95 GrScalar tolSqd,
96 GrPoint** points,
97 uint32_t pointsLeft) {
98 if (pointsLeft < 2 ||
99 (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd &&
100 p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) {
101 (*points)[0] = p3;
102 *points += 1;
103 return 1;
104 }
105 GrPoint q[] = {
reed@google.com7744c202011-05-06 19:26:26 +0000106 { GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY) },
107 { GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY) },
108 { GrScalarAve(p2.fX, p3.fX), GrScalarAve(p2.fY, p3.fY) }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000109 };
110 GrPoint r[] = {
reed@google.com7744c202011-05-06 19:26:26 +0000111 { GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY) },
112 { GrScalarAve(q[1].fX, q[2].fX), GrScalarAve(q[1].fY, q[2].fY) }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000113 };
reed@google.com7744c202011-05-06 19:26:26 +0000114 GrPoint s = { GrScalarAve(r[0].fX, r[1].fX), GrScalarAve(r[0].fY, r[1].fY) };
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000115 pointsLeft >>= 1;
116 uint32_t a = generateCubicPoints(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
117 uint32_t b = generateCubicPoints(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
118 return a + b;
119}
120
reed@google.com07f3ee12011-05-16 17:21:57 +0000121int GrPathUtils::worstCasePointCount(const GrPath& path, int* subpaths,
122 GrScalar tol) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000123 int pointCount = 0;
124 *subpaths = 1;
125
126 bool first = true;
127
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000128 SkPath::Iter iter(path, false);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000129 GrPathCmd cmd;
130
131 GrPoint pts[4];
reed@google.com07f3ee12011-05-16 17:21:57 +0000132 while ((cmd = (GrPathCmd)iter.next(pts)) != kEnd_PathCmd) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000133
134 switch (cmd) {
135 case kLine_PathCmd:
136 pointCount += 1;
137 break;
138 case kQuadratic_PathCmd:
139 pointCount += quadraticPointCount(pts, tol);
140 break;
141 case kCubic_PathCmd:
142 pointCount += cubicPointCount(pts, tol);
143 break;
144 case kMove_PathCmd:
145 pointCount += 1;
146 if (!first) {
147 ++(*subpaths);
148 }
149 break;
150 default:
151 break;
152 }
153 first = false;
154 }
155 return pointCount;
156}