1 /*
2  * Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #include "third_party/blink/renderer/platform/graphics/path_traversal_state.h"
21 
22 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
23 #include "third_party/blink/renderer/platform/wtf/vector.h"
24 
25 namespace blink {
26 
MidPoint(const FloatPoint & first,const FloatPoint & second)27 static inline FloatPoint MidPoint(const FloatPoint& first,
28                                   const FloatPoint& second) {
29   return FloatPoint((first.X() + second.X()) / 2.0f,
30                     (first.Y() + second.Y()) / 2.0f);
31 }
32 
DistanceLine(const FloatPoint & start,const FloatPoint & end)33 static inline float DistanceLine(const FloatPoint& start,
34                                  const FloatPoint& end) {
35   return sqrtf((end.X() - start.X()) * (end.X() - start.X()) +
36                (end.Y() - start.Y()) * (end.Y() - start.Y()));
37 }
38 
39 struct QuadraticBezier {
40   DISALLOW_NEW();
41   QuadraticBezier() = default;
QuadraticBezierblink::QuadraticBezier42   QuadraticBezier(const FloatPoint& s, const FloatPoint& c, const FloatPoint& e)
43       : start(s), control(c), end(e), split_depth(0) {}
44 
MagnitudeSquaredblink::QuadraticBezier45   double MagnitudeSquared() const {
46     return ((double)(start.Dot(start)) + (double)(control.Dot(control)) +
47             (double)(end.Dot(end))) /
48            9.0;
49   }
50 
ApproximateDistanceblink::QuadraticBezier51   float ApproximateDistance() const {
52     return DistanceLine(start, control) + DistanceLine(control, end);
53   }
54 
Splitblink::QuadraticBezier55   void Split(QuadraticBezier& left, QuadraticBezier& right) const {
56     left.control = MidPoint(start, control);
57     right.control = MidPoint(control, end);
58 
59     FloatPoint left_control_to_right_control =
60         MidPoint(left.control, right.control);
61     left.end = left_control_to_right_control;
62     right.start = left_control_to_right_control;
63 
64     left.start = start;
65     right.end = end;
66 
67     left.split_depth = right.split_depth = split_depth + 1;
68   }
69 
70   FloatPoint start;
71   FloatPoint control;
72   FloatPoint end;
73   uint16_t split_depth;
74 };
75 
76 struct CubicBezier {
77   DISALLOW_NEW();
78   CubicBezier() = default;
CubicBezierblink::CubicBezier79   CubicBezier(const FloatPoint& s,
80               const FloatPoint& c1,
81               const FloatPoint& c2,
82               const FloatPoint& e)
83       : start(s), control1(c1), control2(c2), end(e), split_depth(0) {}
84 
MagnitudeSquaredblink::CubicBezier85   double MagnitudeSquared() const {
86     return ((double)(start.Dot(start)) + (double)(control1.Dot(control1)) +
87             (double)(control2.Dot(control2)) + (double)(end.Dot(end))) /
88            16.0;
89   }
90 
ApproximateDistanceblink::CubicBezier91   float ApproximateDistance() const {
92     return DistanceLine(start, control1) + DistanceLine(control1, control2) +
93            DistanceLine(control2, end);
94   }
95 
Splitblink::CubicBezier96   void Split(CubicBezier& left, CubicBezier& right) const {
97     FloatPoint start_to_control1 = MidPoint(control1, control2);
98 
99     left.start = start;
100     left.control1 = MidPoint(start, control1);
101     left.control2 = MidPoint(left.control1, start_to_control1);
102 
103     right.control2 = MidPoint(control2, end);
104     right.control1 = MidPoint(right.control2, start_to_control1);
105     right.end = end;
106 
107     FloatPoint left_control2_to_right_control1 =
108         MidPoint(left.control2, right.control1);
109     left.end = left_control2_to_right_control1;
110     right.start = left_control2_to_right_control1;
111 
112     left.split_depth = right.split_depth = split_depth + 1;
113   }
114 
115   FloatPoint start;
116   FloatPoint control1;
117   FloatPoint control2;
118   FloatPoint end;
119   uint16_t split_depth;
120 };
121 
122 template <class CurveType>
CurveLength(PathTraversalState & traversal_state,CurveType curve)123 static float CurveLength(PathTraversalState& traversal_state, CurveType curve) {
124   static const uint16_t kCurveSplitDepthLimit = 20;
125   static const double kPathSegmentLengthToleranceSquared = 1.e-16;
126 
127   double curve_scale_for_tolerance_squared = curve.MagnitudeSquared();
128   if (curve_scale_for_tolerance_squared < kPathSegmentLengthToleranceSquared)
129     return 0;
130 
131   Vector<CurveType> curve_stack;
132   curve_stack.push_back(curve);
133 
134   float total_length = 0;
135   do {
136     float length = curve.ApproximateDistance();
137     double length_discrepancy = length - DistanceLine(curve.start, curve.end);
138     if ((length_discrepancy * length_discrepancy) /
139                 curve_scale_for_tolerance_squared >
140             kPathSegmentLengthToleranceSquared &&
141         curve.split_depth < kCurveSplitDepthLimit) {
142       CurveType left_curve;
143       CurveType right_curve;
144       curve.Split(left_curve, right_curve);
145       curve = left_curve;
146       curve_stack.push_back(right_curve);
147     } else {
148       total_length += length;
149       if (traversal_state.action_ ==
150               PathTraversalState::kTraversalPointAtLength ||
151           traversal_state.action_ ==
152               PathTraversalState::kTraversalNormalAngleAtLength) {
153         traversal_state.previous_ = curve.start;
154         traversal_state.current_ = curve.end;
155         if (traversal_state.total_length_ + total_length >
156             traversal_state.desired_length_)
157           return total_length;
158       }
159       curve = curve_stack.back();
160       curve_stack.pop_back();
161     }
162   } while (!curve_stack.IsEmpty());
163 
164   return total_length;
165 }
166 
PathTraversalState(PathTraversalAction action)167 PathTraversalState::PathTraversalState(PathTraversalAction action)
168     : action_(action),
169       success_(false),
170       total_length_(0),
171       desired_length_(0),
172       normal_angle_(0) {}
173 
CloseSubpath()174 float PathTraversalState::CloseSubpath() {
175   float distance = DistanceLine(current_, start_);
176   current_ = start_;
177   return distance;
178 }
179 
MoveTo(const FloatPoint & point)180 float PathTraversalState::MoveTo(const FloatPoint& point) {
181   current_ = start_ = point;
182   return 0;
183 }
184 
LineTo(const FloatPoint & point)185 float PathTraversalState::LineTo(const FloatPoint& point) {
186   float distance = DistanceLine(current_, point);
187   current_ = point;
188   return distance;
189 }
190 
CubicBezierTo(const FloatPoint & new_control1,const FloatPoint & new_control2,const FloatPoint & new_end)191 float PathTraversalState::CubicBezierTo(const FloatPoint& new_control1,
192                                         const FloatPoint& new_control2,
193                                         const FloatPoint& new_end) {
194   float distance = CurveLength<CubicBezier>(
195       *this, CubicBezier(current_, new_control1, new_control2, new_end));
196 
197   if (action_ != kTraversalPointAtLength &&
198       action_ != kTraversalNormalAngleAtLength)
199     current_ = new_end;
200 
201   return distance;
202 }
203 
ProcessSegment()204 void PathTraversalState::ProcessSegment() {
205   if ((action_ == kTraversalPointAtLength ||
206        action_ == kTraversalNormalAngleAtLength) &&
207       total_length_ >= desired_length_) {
208     float slope = FloatPoint(current_ - previous_).SlopeAngleRadians();
209     if (action_ == kTraversalPointAtLength) {
210       float offset = desired_length_ - total_length_;
211       current_.Move(offset * cosf(slope), offset * sinf(slope));
212     } else {
213       normal_angle_ = rad2deg(slope);
214     }
215     success_ = true;
216   }
217   previous_ = current_;
218 }
219 
220 }  // namespace blink
221