| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. | |
| 3 * All rights reserved. | |
| 4 * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies) | |
| 5 * Copyright (C) 2007 Alp Toker <alp@atoker.com> | |
| 6 * Copyright (C) 2008 Eric Seidel <eric@webkit.org> | |
| 7 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org> | |
| 8 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. | |
| 9 * Copyright (C) 2012, 2013 Intel Corporation. All rights reserved. | |
| 10 * Copyright (C) 2012, 2013 Adobe Systems Incorporated. All rights reserved. | |
| 11 * | |
| 12 * Redistribution and use in source and binary forms, with or without | |
| 13 * modification, are permitted provided that the following conditions | |
| 14 * are met: | |
| 15 * | |
| 16 * 1. Redistributions of source code must retain the above copyright | |
| 17 * notice, this list of conditions and the following disclaimer. | |
| 18 * 2. Redistributions in binary form must reproduce the above copyright | |
| 19 * notice, this list of conditions and the following disclaimer in the | |
| 20 * documentation and/or other materials provided with the distribution. | |
| 21 * | |
| 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY | |
| 23 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE | |
| 26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, | |
| 27 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 29 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | |
| 31 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF | |
| 32 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 33 * SUCH DAMAGE. | |
| 34 */ | |
| 35 | |
| 36 #include "modules/canvas2d/CanvasPathMethods.h" | |
| 37 | |
| 38 #include "bindings/core/v8/ExceptionState.h" | |
| 39 #include "core/dom/ExceptionCode.h" | |
| 40 #include "platform/geometry/FloatRect.h" | |
| 41 #include "platform/transforms/AffineTransform.h" | |
| 42 #include "platform/wtf/MathExtras.h" | |
| 43 | |
| 44 namespace blink { | |
| 45 | |
| 46 void CanvasPathMethods::closePath() { | |
| 47 if (path_.IsEmpty()) | |
| 48 return; | |
| 49 | |
| 50 FloatRect bound_rect = path_.BoundingRect(); | |
| 51 if (bound_rect.Width() || bound_rect.Height()) | |
| 52 path_.CloseSubpath(); | |
| 53 } | |
| 54 | |
| 55 void CanvasPathMethods::moveTo(float x, float y) { | |
| 56 if (!std::isfinite(x) || !std::isfinite(y)) | |
| 57 return; | |
| 58 if (!IsTransformInvertible()) | |
| 59 return; | |
| 60 path_.MoveTo(FloatPoint(x, y)); | |
| 61 } | |
| 62 | |
| 63 void CanvasPathMethods::lineTo(float x, float y) { | |
| 64 if (!std::isfinite(x) || !std::isfinite(y)) | |
| 65 return; | |
| 66 if (!IsTransformInvertible()) | |
| 67 return; | |
| 68 | |
| 69 FloatPoint p1 = FloatPoint(x, y); | |
| 70 if (!path_.HasCurrentPoint()) | |
| 71 path_.MoveTo(p1); | |
| 72 | |
| 73 path_.AddLineTo(p1); | |
| 74 } | |
| 75 | |
| 76 void CanvasPathMethods::quadraticCurveTo(float cpx, | |
| 77 float cpy, | |
| 78 float x, | |
| 79 float y) { | |
| 80 if (!std::isfinite(cpx) || !std::isfinite(cpy) || !std::isfinite(x) || | |
| 81 !std::isfinite(y)) | |
| 82 return; | |
| 83 if (!IsTransformInvertible()) | |
| 84 return; | |
| 85 if (!path_.HasCurrentPoint()) | |
| 86 path_.MoveTo(FloatPoint(cpx, cpy)); | |
| 87 | |
| 88 FloatPoint p1 = FloatPoint(x, y); | |
| 89 FloatPoint cp = FloatPoint(cpx, cpy); | |
| 90 | |
| 91 path_.AddQuadCurveTo(cp, p1); | |
| 92 } | |
| 93 | |
| 94 void CanvasPathMethods::bezierCurveTo(float cp1x, | |
| 95 float cp1y, | |
| 96 float cp2x, | |
| 97 float cp2y, | |
| 98 float x, | |
| 99 float y) { | |
| 100 if (!std::isfinite(cp1x) || !std::isfinite(cp1y) || !std::isfinite(cp2x) || | |
| 101 !std::isfinite(cp2y) || !std::isfinite(x) || !std::isfinite(y)) | |
| 102 return; | |
| 103 if (!IsTransformInvertible()) | |
| 104 return; | |
| 105 if (!path_.HasCurrentPoint()) | |
| 106 path_.MoveTo(FloatPoint(cp1x, cp1y)); | |
| 107 | |
| 108 FloatPoint p1 = FloatPoint(x, y); | |
| 109 FloatPoint cp1 = FloatPoint(cp1x, cp1y); | |
| 110 FloatPoint cp2 = FloatPoint(cp2x, cp2y); | |
| 111 | |
| 112 path_.AddBezierCurveTo(cp1, cp2, p1); | |
| 113 } | |
| 114 | |
| 115 void CanvasPathMethods::arcTo(float x1, | |
| 116 float y1, | |
| 117 float x2, | |
| 118 float y2, | |
| 119 float r, | |
| 120 ExceptionState& exception_state) { | |
| 121 if (!std::isfinite(x1) || !std::isfinite(y1) || !std::isfinite(x2) || | |
| 122 !std::isfinite(y2) || !std::isfinite(r)) | |
| 123 return; | |
| 124 | |
| 125 if (r < 0) { | |
| 126 exception_state.ThrowDOMException( | |
| 127 kIndexSizeError, | |
| 128 "The radius provided (" + String::Number(r) + ") is negative."); | |
| 129 return; | |
| 130 } | |
| 131 | |
| 132 if (!IsTransformInvertible()) | |
| 133 return; | |
| 134 | |
| 135 FloatPoint p1 = FloatPoint(x1, y1); | |
| 136 FloatPoint p2 = FloatPoint(x2, y2); | |
| 137 | |
| 138 if (!path_.HasCurrentPoint()) | |
| 139 path_.MoveTo(p1); | |
| 140 else if (p1 == path_.CurrentPoint() || p1 == p2 || !r) | |
| 141 lineTo(x1, y1); | |
| 142 else | |
| 143 path_.AddArcTo(p1, p2, r); | |
| 144 } | |
| 145 | |
| 146 namespace { | |
| 147 | |
| 148 float AdjustEndAngle(float start_angle, float end_angle, bool anticlockwise) { | |
| 149 float new_end_angle = end_angle; | |
| 150 /* http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-elem
ent.html#dom-context-2d-arc | |
| 151 * If the anticlockwise argument is false and endAngle-startAngle is equal | |
| 152 * to or greater than 2pi, or, | |
| 153 * if the anticlockwise argument is true and startAngle-endAngle is equal to | |
| 154 * or greater than 2pi, | |
| 155 * then the arc is the whole circumference of this ellipse, and the point at | |
| 156 * startAngle along this circle's circumference, measured in radians clockwise | |
| 157 * from the ellipse's semi-major axis, acts as both the start point and the | |
| 158 * end point. | |
| 159 */ | |
| 160 if (!anticlockwise && end_angle - start_angle >= twoPiFloat) | |
| 161 new_end_angle = start_angle + twoPiFloat; | |
| 162 else if (anticlockwise && start_angle - end_angle >= twoPiFloat) | |
| 163 new_end_angle = start_angle - twoPiFloat; | |
| 164 | |
| 165 /* | |
| 166 * Otherwise, the arc is the path along the circumference of this ellipse | |
| 167 * from the start point to the end point, going anti-clockwise if the | |
| 168 * anticlockwise argument is true, and clockwise otherwise. | |
| 169 * Since the points are on the ellipse, as opposed to being simply angles | |
| 170 * from zero, the arc can never cover an angle greater than 2pi radians. | |
| 171 */ | |
| 172 /* NOTE: When startAngle = 0, endAngle = 2Pi and anticlockwise = true, the | |
| 173 * spec does not indicate clearly. | |
| 174 * We draw the entire circle, because some web sites use arc(x, y, r, 0, | |
| 175 * 2*Math.PI, true) to draw circle. | |
| 176 * We preserve backward-compatibility. | |
| 177 */ | |
| 178 else if (!anticlockwise && start_angle > end_angle) | |
| 179 new_end_angle = | |
| 180 start_angle + (twoPiFloat - fmodf(start_angle - end_angle, twoPiFloat)); | |
| 181 else if (anticlockwise && start_angle < end_angle) | |
| 182 new_end_angle = | |
| 183 start_angle - (twoPiFloat - fmodf(end_angle - start_angle, twoPiFloat)); | |
| 184 | |
| 185 ASSERT(EllipseIsRenderable(start_angle, new_end_angle)); | |
| 186 return new_end_angle; | |
| 187 } | |
| 188 | |
| 189 inline void LineToFloatPoint(CanvasPathMethods* path, const FloatPoint& p) { | |
| 190 path->lineTo(p.X(), p.Y()); | |
| 191 } | |
| 192 | |
| 193 inline FloatPoint GetPointOnEllipse(float radius_x, | |
| 194 float radius_y, | |
| 195 float theta) { | |
| 196 return FloatPoint(radius_x * cosf(theta), radius_y * sinf(theta)); | |
| 197 } | |
| 198 | |
| 199 void CanonicalizeAngle(float* start_angle, float* end_angle) { | |
| 200 // Make 0 <= startAngle < 2*PI | |
| 201 float new_start_angle = fmodf(*start_angle, twoPiFloat); | |
| 202 | |
| 203 if (new_start_angle < 0) { | |
| 204 new_start_angle += twoPiFloat; | |
| 205 // Check for possible catastrophic cancellation in cases where | |
| 206 // newStartAngle was a tiny negative number (c.f. crbug.com/503422) | |
| 207 if (new_start_angle >= twoPiFloat) | |
| 208 new_start_angle -= twoPiFloat; | |
| 209 } | |
| 210 | |
| 211 float delta = new_start_angle - *start_angle; | |
| 212 *start_angle = new_start_angle; | |
| 213 *end_angle = *end_angle + delta; | |
| 214 | |
| 215 ASSERT(new_start_angle >= 0 && new_start_angle < twoPiFloat); | |
| 216 } | |
| 217 | |
| 218 /* | |
| 219 * degenerateEllipse() handles a degenerated ellipse using several lines. | |
| 220 * | |
| 221 * Let's see a following example: line to ellipse to line. | |
| 222 * _--^\ | |
| 223 * ( ) | |
| 224 * -----( ) | |
| 225 * ) | |
| 226 * /-------- | |
| 227 * | |
| 228 * If radiusX becomes zero, the ellipse of the example is degenerated. | |
| 229 * _ | |
| 230 * // P | |
| 231 * // | |
| 232 * -----// | |
| 233 * / | |
| 234 * /-------- | |
| 235 * | |
| 236 * To draw the above example, need to get P that is a local maximum point. | |
| 237 * Angles for P are 0.5Pi and 1.5Pi in the ellipse coordinates. | |
| 238 * | |
| 239 * If radiusY becomes zero, the result is as follows. | |
| 240 * -----__ | |
| 241 * --_ | |
| 242 * ---------- | |
| 243 * ``P | |
| 244 * Angles for P are 0 and Pi in the ellipse coordinates. | |
| 245 * | |
| 246 * To handle both cases, degenerateEllipse() lines to start angle, local maximum | |
| 247 * points(every 0.5Pi), and end angle. | |
| 248 * NOTE: Before ellipse() calls this function, adjustEndAngle() is called, so | |
| 249 * endAngle - startAngle must be equal to or less than 2Pi. | |
| 250 */ | |
| 251 void DegenerateEllipse(CanvasPathMethods* path, | |
| 252 float x, | |
| 253 float y, | |
| 254 float radius_x, | |
| 255 float radius_y, | |
| 256 float rotation, | |
| 257 float start_angle, | |
| 258 float end_angle, | |
| 259 bool anticlockwise) { | |
| 260 ASSERT(EllipseIsRenderable(start_angle, end_angle)); | |
| 261 ASSERT(start_angle >= 0 && start_angle < twoPiFloat); | |
| 262 ASSERT((anticlockwise && (start_angle - end_angle) >= 0) || | |
| 263 (!anticlockwise && (end_angle - start_angle) >= 0)); | |
| 264 | |
| 265 FloatPoint center(x, y); | |
| 266 AffineTransform rotation_matrix; | |
| 267 rotation_matrix.RotateRadians(rotation); | |
| 268 // First, if the object's path has any subpaths, then the method must add a | |
| 269 // straight line from the last point in the subpath to the start point of the | |
| 270 // arc. | |
| 271 LineToFloatPoint(path, center + rotation_matrix.MapPoint(GetPointOnEllipse( | |
| 272 radius_x, radius_y, start_angle))); | |
| 273 if ((!radius_x && !radius_y) || start_angle == end_angle) | |
| 274 return; | |
| 275 | |
| 276 if (!anticlockwise) { | |
| 277 // startAngle - fmodf(startAngle, piOverTwoFloat) + piOverTwoFloat is the | |
| 278 // one of (0, 0.5Pi, Pi, 1.5Pi, 2Pi) that is the closest to startAngle on | |
| 279 // the clockwise direction. | |
| 280 for (float angle = | |
| 281 start_angle - fmodf(start_angle, piOverTwoFloat) + piOverTwoFloat; | |
| 282 angle < end_angle; angle += piOverTwoFloat) | |
| 283 LineToFloatPoint( | |
| 284 path, center + rotation_matrix.MapPoint( | |
| 285 GetPointOnEllipse(radius_x, radius_y, angle))); | |
| 286 } else { | |
| 287 for (float angle = start_angle - fmodf(start_angle, piOverTwoFloat); | |
| 288 angle > end_angle; angle -= piOverTwoFloat) | |
| 289 LineToFloatPoint( | |
| 290 path, center + rotation_matrix.MapPoint( | |
| 291 GetPointOnEllipse(radius_x, radius_y, angle))); | |
| 292 } | |
| 293 | |
| 294 LineToFloatPoint(path, center + rotation_matrix.MapPoint(GetPointOnEllipse( | |
| 295 radius_x, radius_y, end_angle))); | |
| 296 } | |
| 297 | |
| 298 } // namespace | |
| 299 | |
| 300 void CanvasPathMethods::arc(float x, | |
| 301 float y, | |
| 302 float radius, | |
| 303 float start_angle, | |
| 304 float end_angle, | |
| 305 bool anticlockwise, | |
| 306 ExceptionState& exception_state) { | |
| 307 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(radius) || | |
| 308 !std::isfinite(start_angle) || !std::isfinite(end_angle)) | |
| 309 return; | |
| 310 | |
| 311 if (radius < 0) { | |
| 312 exception_state.ThrowDOMException( | |
| 313 kIndexSizeError, | |
| 314 "The radius provided (" + String::Number(radius) + ") is negative."); | |
| 315 return; | |
| 316 } | |
| 317 | |
| 318 if (!IsTransformInvertible()) | |
| 319 return; | |
| 320 | |
| 321 if (!radius || start_angle == end_angle) { | |
| 322 // The arc is empty but we still need to draw the connecting line. | |
| 323 lineTo(x + radius * cosf(start_angle), y + radius * sinf(start_angle)); | |
| 324 return; | |
| 325 } | |
| 326 | |
| 327 CanonicalizeAngle(&start_angle, &end_angle); | |
| 328 float adjusted_end_angle = | |
| 329 AdjustEndAngle(start_angle, end_angle, anticlockwise); | |
| 330 path_.AddArc(FloatPoint(x, y), radius, start_angle, adjusted_end_angle, | |
| 331 anticlockwise); | |
| 332 } | |
| 333 | |
| 334 void CanvasPathMethods::ellipse(float x, | |
| 335 float y, | |
| 336 float radius_x, | |
| 337 float radius_y, | |
| 338 float rotation, | |
| 339 float start_angle, | |
| 340 float end_angle, | |
| 341 bool anticlockwise, | |
| 342 ExceptionState& exception_state) { | |
| 343 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(radius_x) || | |
| 344 !std::isfinite(radius_y) || !std::isfinite(rotation) || | |
| 345 !std::isfinite(start_angle) || !std::isfinite(end_angle)) | |
| 346 return; | |
| 347 | |
| 348 if (radius_x < 0) { | |
| 349 exception_state.ThrowDOMException( | |
| 350 kIndexSizeError, "The major-axis radius provided (" + | |
| 351 String::Number(radius_x) + ") is negative."); | |
| 352 return; | |
| 353 } | |
| 354 if (radius_y < 0) { | |
| 355 exception_state.ThrowDOMException( | |
| 356 kIndexSizeError, "The minor-axis radius provided (" + | |
| 357 String::Number(radius_y) + ") is negative."); | |
| 358 return; | |
| 359 } | |
| 360 | |
| 361 if (!IsTransformInvertible()) | |
| 362 return; | |
| 363 | |
| 364 CanonicalizeAngle(&start_angle, &end_angle); | |
| 365 float adjusted_end_angle = | |
| 366 AdjustEndAngle(start_angle, end_angle, anticlockwise); | |
| 367 if (!radius_x || !radius_y || start_angle == adjusted_end_angle) { | |
| 368 // The ellipse is empty but we still need to draw the connecting line to | |
| 369 // start point. | |
| 370 DegenerateEllipse(this, x, y, radius_x, radius_y, rotation, start_angle, | |
| 371 adjusted_end_angle, anticlockwise); | |
| 372 return; | |
| 373 } | |
| 374 | |
| 375 path_.AddEllipse(FloatPoint(x, y), radius_x, radius_y, rotation, start_angle, | |
| 376 adjusted_end_angle, anticlockwise); | |
| 377 } | |
| 378 | |
| 379 void CanvasPathMethods::rect(float x, float y, float width, float height) { | |
| 380 if (!IsTransformInvertible()) | |
| 381 return; | |
| 382 | |
| 383 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(width) || | |
| 384 !std::isfinite(height)) | |
| 385 return; | |
| 386 | |
| 387 path_.AddRect(FloatRect(x, y, width, height)); | |
| 388 } | |
| 389 } // namespace blink | |
| OLD | NEW |