blob: e7c404c006ef3560767a8847085c4aabbab19cc0 [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;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000021
22uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[],
23 GrScalar tol) {
24 GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
25 if (d < tol) {
26 return 1;
27 } else {
28 // Each time we subdivide, d should be cut in 4. So we need to
29 // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
30 // points.
31 // 2^(log4(x)) = sqrt(x);
epoger@google.com2047f002011-05-17 17:36:59 +000032 int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
bsalomon@google.com61f3bde2011-06-17 20:06:49 +000033 int pow2 = GrNextPow2(temp);
34 // Because of NaNs & INFs we can wind up with a degenerate temp
35 // such that pow2 comes out negative. Also, our point generator
36 // will always output at least one pt.
37 if (pow2 < 1) {
38 pow2 = 1;
39 }
40 return GrMin(pow2, MAX_POINTS_PER_CURVE);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000041 }
42}
43
44uint32_t GrPathUtils::generateQuadraticPoints(const GrPoint& p0,
45 const GrPoint& p1,
46 const GrPoint& p2,
47 GrScalar tolSqd,
48 GrPoint** points,
49 uint32_t pointsLeft) {
50 if (pointsLeft < 2 ||
51 (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) {
52 (*points)[0] = p2;
53 *points += 1;
54 return 1;
55 }
56
57 GrPoint q[] = {
reed@google.com7744c202011-05-06 19:26:26 +000058 { GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY) },
59 { GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY) },
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000060 };
reed@google.com7744c202011-05-06 19:26:26 +000061 GrPoint r = { GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY) };
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000062
63 pointsLeft >>= 1;
64 uint32_t a = generateQuadraticPoints(p0, q[0], r, tolSqd, points, pointsLeft);
65 uint32_t b = generateQuadraticPoints(r, q[1], p2, tolSqd, points, pointsLeft);
66 return a + b;
67}
68
69uint32_t GrPathUtils::cubicPointCount(const GrPoint points[],
70 GrScalar tol) {
71 GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
72 points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
epoger@google.com2047f002011-05-17 17:36:59 +000073 d = SkScalarSqrt(d);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000074 if (d < tol) {
75 return 1;
76 } else {
epoger@google.com2047f002011-05-17 17:36:59 +000077 int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
bsalomon@google.com61f3bde2011-06-17 20:06:49 +000078 int pow2 = GrNextPow2(temp);
79 // Because of NaNs & INFs we can wind up with a degenerate temp
80 // such that pow2 comes out negative. Also, our point generator
81 // will always output at least one pt.
82 if (pow2 < 1) {
83 pow2 = 1;
84 }
85 return GrMin(pow2, MAX_POINTS_PER_CURVE);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000086 }
87}
88
89uint32_t GrPathUtils::generateCubicPoints(const GrPoint& p0,
90 const GrPoint& p1,
91 const GrPoint& p2,
92 const GrPoint& p3,
93 GrScalar tolSqd,
94 GrPoint** points,
95 uint32_t pointsLeft) {
96 if (pointsLeft < 2 ||
97 (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd &&
98 p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) {
99 (*points)[0] = p3;
100 *points += 1;
101 return 1;
102 }
103 GrPoint q[] = {
reed@google.com7744c202011-05-06 19:26:26 +0000104 { GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY) },
105 { GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY) },
106 { GrScalarAve(p2.fX, p3.fX), GrScalarAve(p2.fY, p3.fY) }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000107 };
108 GrPoint r[] = {
reed@google.com7744c202011-05-06 19:26:26 +0000109 { GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY) },
110 { GrScalarAve(q[1].fX, q[2].fX), GrScalarAve(q[1].fY, q[2].fY) }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000111 };
reed@google.com7744c202011-05-06 19:26:26 +0000112 GrPoint s = { GrScalarAve(r[0].fX, r[1].fX), GrScalarAve(r[0].fY, r[1].fY) };
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000113 pointsLeft >>= 1;
114 uint32_t a = generateCubicPoints(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
115 uint32_t b = generateCubicPoints(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
116 return a + b;
117}
118
reed@google.com07f3ee12011-05-16 17:21:57 +0000119int GrPathUtils::worstCasePointCount(const GrPath& path, int* subpaths,
120 GrScalar tol) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000121 int pointCount = 0;
122 *subpaths = 1;
123
124 bool first = true;
125
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000126 SkPath::Iter iter(path, false);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000127 GrPathCmd cmd;
128
129 GrPoint pts[4];
reed@google.com07f3ee12011-05-16 17:21:57 +0000130 while ((cmd = (GrPathCmd)iter.next(pts)) != kEnd_PathCmd) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000131
132 switch (cmd) {
133 case kLine_PathCmd:
134 pointCount += 1;
135 break;
136 case kQuadratic_PathCmd:
137 pointCount += quadraticPointCount(pts, tol);
138 break;
139 case kCubic_PathCmd:
140 pointCount += cubicPointCount(pts, tol);
141 break;
142 case kMove_PathCmd:
143 pointCount += 1;
144 if (!first) {
145 ++(*subpaths);
146 }
147 break;
148 default:
149 break;
150 }
151 first = false;
152 }
153 return pointCount;
154}