blob: b93c8a9ed9db2ca7e0cabf6db09a408bfe13ab1a [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)));
35 return GrMin(GrNextPow2(temp), MAX_POINTS_PER_CURVE);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000036 }
37}
38
39uint32_t GrPathUtils::generateQuadraticPoints(const GrPoint& p0,
40 const GrPoint& p1,
41 const GrPoint& p2,
42 GrScalar tolSqd,
43 GrPoint** points,
44 uint32_t pointsLeft) {
45 if (pointsLeft < 2 ||
46 (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) {
47 (*points)[0] = p2;
48 *points += 1;
49 return 1;
50 }
51
52 GrPoint q[] = {
reed@google.com7744c202011-05-06 19:26:26 +000053 { GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY) },
54 { GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY) },
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000055 };
reed@google.com7744c202011-05-06 19:26:26 +000056 GrPoint r = { GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY) };
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000057
58 pointsLeft >>= 1;
59 uint32_t a = generateQuadraticPoints(p0, q[0], r, tolSqd, points, pointsLeft);
60 uint32_t b = generateQuadraticPoints(r, q[1], p2, tolSqd, points, pointsLeft);
61 return a + b;
62}
63
64uint32_t GrPathUtils::cubicPointCount(const GrPoint points[],
65 GrScalar tol) {
66 GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
67 points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
epoger@google.com2047f002011-05-17 17:36:59 +000068 d = SkScalarSqrt(d);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000069 if (d < tol) {
70 return 1;
71 } else {
epoger@google.com2047f002011-05-17 17:36:59 +000072 int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
73 return GrMin(GrNextPow2(temp), MAX_POINTS_PER_CURVE);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000074 }
75}
76
77uint32_t GrPathUtils::generateCubicPoints(const GrPoint& p0,
78 const GrPoint& p1,
79 const GrPoint& p2,
80 const GrPoint& p3,
81 GrScalar tolSqd,
82 GrPoint** points,
83 uint32_t pointsLeft) {
84 if (pointsLeft < 2 ||
85 (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd &&
86 p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) {
87 (*points)[0] = p3;
88 *points += 1;
89 return 1;
90 }
91 GrPoint q[] = {
reed@google.com7744c202011-05-06 19:26:26 +000092 { GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY) },
93 { GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY) },
94 { GrScalarAve(p2.fX, p3.fX), GrScalarAve(p2.fY, p3.fY) }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000095 };
96 GrPoint r[] = {
reed@google.com7744c202011-05-06 19:26:26 +000097 { GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY) },
98 { GrScalarAve(q[1].fX, q[2].fX), GrScalarAve(q[1].fY, q[2].fY) }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000099 };
reed@google.com7744c202011-05-06 19:26:26 +0000100 GrPoint s = { GrScalarAve(r[0].fX, r[1].fX), GrScalarAve(r[0].fY, r[1].fY) };
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000101 pointsLeft >>= 1;
102 uint32_t a = generateCubicPoints(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
103 uint32_t b = generateCubicPoints(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
104 return a + b;
105}
106
reed@google.com07f3ee12011-05-16 17:21:57 +0000107int GrPathUtils::worstCasePointCount(const GrPath& path, int* subpaths,
108 GrScalar tol) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000109 int pointCount = 0;
110 *subpaths = 1;
111
112 bool first = true;
113
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000114 SkPath::Iter iter(path, false);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000115 GrPathCmd cmd;
116
117 GrPoint pts[4];
reed@google.com07f3ee12011-05-16 17:21:57 +0000118 while ((cmd = (GrPathCmd)iter.next(pts)) != kEnd_PathCmd) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000119
120 switch (cmd) {
121 case kLine_PathCmd:
122 pointCount += 1;
123 break;
124 case kQuadratic_PathCmd:
125 pointCount += quadraticPointCount(pts, tol);
126 break;
127 case kCubic_PathCmd:
128 pointCount += cubicPointCount(pts, tol);
129 break;
130 case kMove_PathCmd:
131 pointCount += 1;
132 if (!first) {
133 ++(*subpaths);
134 }
135 break;
136 default:
137 break;
138 }
139 first = false;
140 }
141 return pointCount;
142}