KHtml

Path.cpp
1 /*
2  * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
3  * 2006 Rob Buis <[email protected]>
4  * Copyright (C) 2007 Eric Seidel <[email protected]>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "wtf/Platform.h"
29 #include "Path.h"
30 
31 #include "FloatPoint.h"
32 #include "FloatRect.h"
33 #include "PathTraversalState.h"
34 #include <math.h>
35 #include <wtf/MathExtras.h>
36 
37 const float QUARTER = 0.552f; // approximation of control point positions on a bezier
38 // to simulate a quarter of a circle.
39 
40 using namespace WebCore;
41 
42 namespace khtml
43 {
44 
45 static void pathLengthApplierFunction(void *info, const PathElement *element)
46 {
47  PathTraversalState &traversalState = *static_cast<PathTraversalState *>(info);
48  if (traversalState.m_success) {
49  return;
50  }
51  traversalState.m_previous = traversalState.m_current;
52  FloatPoint *points = element->points;
53  float segmentLength = 0.0f;
54  switch (element->type) {
55  case PathElementMoveToPoint:
56  segmentLength = traversalState.moveTo(points[0]);
57  break;
58  case PathElementAddLineToPoint:
59  segmentLength = traversalState.lineTo(points[0]);
60  break;
61  case PathElementAddQuadCurveToPoint:
62  segmentLength = traversalState.quadraticBezierTo(points[0], points[1]);
63  break;
64  case PathElementAddCurveToPoint:
65  segmentLength = traversalState.cubicBezierTo(points[0], points[1], points[2]);
66  break;
67  case PathElementCloseSubpath:
68  segmentLength = traversalState.closeSubpath();
69  break;
70  }
71  traversalState.m_totalLength += segmentLength;
72  if ((traversalState.m_action == PathTraversalState::TraversalPointAtLength ||
73  traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLength) &&
74  (traversalState.m_totalLength >= traversalState.m_desiredLength)) {
75  FloatSize change = traversalState.m_current - traversalState.m_previous;
76  float slope = atan2f(change.height(), change.width());
77 
78  if (traversalState.m_action == PathTraversalState::TraversalPointAtLength) {
79  float offset = traversalState.m_desiredLength - traversalState.m_totalLength;
80  traversalState.m_current.move(offset * cosf(slope), offset * sinf(slope));
81  } else {
82  static const float rad2deg = 180.0f / piFloat;
83  traversalState.m_normalAngle = slope * rad2deg;
84  }
85 
86  traversalState.m_success = true;
87  }
88 }
89 
90 float Path::length()
91 {
92  PathTraversalState traversalState(PathTraversalState::TraversalTotalLength);
93  apply(&traversalState, pathLengthApplierFunction);
94  return traversalState.m_totalLength;
95 }
96 
97 FloatPoint Path::pointAtLength(float length, bool &ok)
98 {
99  PathTraversalState traversalState(PathTraversalState::TraversalPointAtLength);
100  traversalState.m_desiredLength = length;
101  apply(&traversalState, pathLengthApplierFunction);
102  ok = traversalState.m_success;
103  return traversalState.m_current;
104 }
105 
106 float Path::normalAngleAtLength(float length, bool &ok)
107 {
108  PathTraversalState traversalState(PathTraversalState::TraversalNormalAngleAtLength);
109  traversalState.m_desiredLength = length;
110  apply(&traversalState, pathLengthApplierFunction);
111  ok = traversalState.m_success;
112  return traversalState.m_normalAngle;
113 }
114 
115 Path Path::createRoundedRectangle(const FloatRect &rectangle, const FloatSize &roundingRadii)
116 {
117  Path path;
118  float x = rectangle.x();
119  float y = rectangle.y();
120  float width = rectangle.width();
121  float height = rectangle.height();
122  float rx = roundingRadii.width();
123  float ry = roundingRadii.height();
124  if (width <= 0.0f || height <= 0.0f) {
125  return path;
126  }
127 
128  float dx = rx, dy = ry;
129  // If rx is greater than half of the width of the rectangle
130  // then set rx to half of the width (required in SVG spec)
131  if (dx > width * 0.5f) {
132  dx = width * 0.5f;
133  }
134 
135  // If ry is greater than half of the height of the rectangle
136  // then set ry to half of the height (required in SVG spec)
137  if (dy > height * 0.5f) {
138  dy = height * 0.5f;
139  }
140 
141  path.moveTo(FloatPoint(x + dx, y));
142 
143  if (dx < width * 0.5f) {
144  path.addLineTo(FloatPoint(x + width - rx, y));
145  }
146 
147  path.addBezierCurveTo(FloatPoint(x + width - dx * (1 - QUARTER), y), FloatPoint(x + width, y + dy * (1 - QUARTER)), FloatPoint(x + width, y + dy));
148 
149  if (dy < height * 0.5) {
150  path.addLineTo(FloatPoint(x + width, y + height - dy));
151  }
152 
153  path.addBezierCurveTo(FloatPoint(x + width, y + height - dy * (1 - QUARTER)), FloatPoint(x + width - dx * (1 - QUARTER), y + height), FloatPoint(x + width - dx, y + height));
154 
155  if (dx < width * 0.5) {
156  path.addLineTo(FloatPoint(x + dx, y + height));
157  }
158 
159  path.addBezierCurveTo(FloatPoint(x + dx * (1 - QUARTER), y + height), FloatPoint(x, y + height - dy * (1 - QUARTER)), FloatPoint(x, y + height - dy));
160 
161  if (dy < height * 0.5) {
162  path.addLineTo(FloatPoint(x, y + dy));
163  }
164 
165  path.addBezierCurveTo(FloatPoint(x, y + dy * (1 - QUARTER)), FloatPoint(x + dx * (1 - QUARTER), y), FloatPoint(x + dx, y));
166 
167  path.closeSubpath();
168 
169  return path;
170 }
171 
172 Path Path::createRoundedRectangle(const FloatRect &rectangle, const FloatSize &topLeftRadius, const FloatSize &topRightRadius, const FloatSize &bottomLeftRadius, const FloatSize &bottomRightRadius)
173 {
174  Path path;
175 
176  float width = rectangle.width();
177  float height = rectangle.height();
178  if (width <= 0.0 || height <= 0.0) {
179  return path;
180  }
181 
182  if (width < topLeftRadius.width() + topRightRadius.width()
183  || width < bottomLeftRadius.width() + bottomRightRadius.width()
184  || height < topLeftRadius.height() + bottomLeftRadius.height()
185  || height < topRightRadius.height() + bottomRightRadius.height())
186  // If all the radii cannot be accommodated, return a rect.
187  {
188  return createRectangle(rectangle);
189  }
190 
191  float x = rectangle.x();
192  float y = rectangle.y();
193 
194  path.moveTo(FloatPoint(x + topLeftRadius.width(), y));
195 
196  path.addLineTo(FloatPoint(x + width - topRightRadius.width(), y));
197 
198  path.addBezierCurveTo(FloatPoint(x + width - topRightRadius.width() * (1 - QUARTER), y), FloatPoint(x + width, y + topRightRadius.height() * (1 - QUARTER)), FloatPoint(x + width, y + topRightRadius.height()));
199 
200  path.addLineTo(FloatPoint(x + width, y + height - bottomRightRadius.height()));
201 
202  path.addBezierCurveTo(FloatPoint(x + width, y + height - bottomRightRadius.height() * (1 - QUARTER)), FloatPoint(x + width - bottomRightRadius.width() * (1 - QUARTER), y + height), FloatPoint(x + width - bottomRightRadius.width(), y + height));
203 
204  path.addLineTo(FloatPoint(x + bottomLeftRadius.width(), y + height));
205 
206  path.addBezierCurveTo(FloatPoint(x + bottomLeftRadius.width() * (1 - QUARTER), y + height), FloatPoint(x, y + height - bottomLeftRadius.height() * (1 - QUARTER)), FloatPoint(x, y + height - bottomLeftRadius.height()));
207 
208  path.addLineTo(FloatPoint(x, y + topLeftRadius.height()));
209 
210  path.addBezierCurveTo(FloatPoint(x, y + topLeftRadius.height() * (1 - QUARTER)), FloatPoint(x + topLeftRadius.width() * (1 - QUARTER), y), FloatPoint(x + topLeftRadius.width(), y));
211 
212  path.closeSubpath();
213 
214  return path;
215 }
216 
217 Path Path::createRectangle(const FloatRect &rectangle)
218 {
219  Path path;
220  float x = rectangle.x();
221  float y = rectangle.y();
222  float width = rectangle.width();
223  float height = rectangle.height();
224  if (width <= 0.0f || height <= 0.0f) {
225  return path;
226  }
227 
228  path.moveTo(FloatPoint(x, y));
229  path.addLineTo(FloatPoint(x + width, y));
230  path.addLineTo(FloatPoint(x + width, y + height));
231  path.addLineTo(FloatPoint(x, y + height));
232  path.closeSubpath();
233 
234  return path;
235 }
236 
237 Path Path::createEllipse(const FloatPoint &center, float rx, float ry)
238 {
239  float cx = center.x();
240  float cy = center.y();
241  Path path;
242  if (rx <= 0.0f || ry <= 0.0f) {
243  return path;
244  }
245 
246  float x = cx;
247  float y = cy;
248 
249  unsigned step = 0, num = 100;
250  bool running = true;
251  while (running) {
252  if (step == num) {
253  running = false;
254  break;
255  }
256 
257  float angle = static_cast<float>(step) / static_cast<float>(num) * 2.0f * piFloat;
258  x = cx + cosf(angle) * rx;
259  y = cy + sinf(angle) * ry;
260 
261  step++;
262  if (step == 1) {
263  path.moveTo(FloatPoint(x, y));
264  } else {
265  path.addLineTo(FloatPoint(x, y));
266  }
267  }
268 
269  path.closeSubpath();
270 
271  return path;
272 }
273 
274 Path Path::createCircle(const FloatPoint &center, float r)
275 {
276  return createEllipse(center, r, r);
277 }
278 
279 Path Path::createLine(const FloatPoint &start, const FloatPoint &end)
280 {
281  Path path;
282  if (start.x() == end.x() && start.y() == end.y()) {
283  return path;
284  }
285 
286  path.moveTo(start);
287  path.addLineTo(end);
288 
289  return path;
290 }
291 
292 }
This file is part of the HTML rendering engine for KDE.
KGuiItem apply()
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:06 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.