OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2002, 2003 The Karbon Developers | 2 * Copyright (C) 2002, 2003 The Karbon Developers |
3 * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org> | 3 * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org> |
4 * Copyright (C) 2006, 2007 Rob Buis <buis@kde.org> | 4 * Copyright (C) 2006, 2007 Rob Buis <buis@kde.org> |
5 * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. | 5 * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. |
6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | 6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
7 * | 7 * |
8 * This library is free software; you can redistribute it and/or | 8 * This library is free software; you can redistribute it and/or |
9 * modify it under the terms of the GNU Library General Public | 9 * modify it under the terms of the GNU Library General Public |
10 * License as published by the Free Software Foundation; either | 10 * License as published by the Free Software Foundation; either |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
160 FloatPoint point1 = blendPoints(m_currentPoint, m_controlPoint); | 160 FloatPoint point1 = blendPoints(m_currentPoint, m_controlPoint); |
161 FloatPoint point2 = blendPoints(segment.targetPoint, m_controlPoint); | 161 FloatPoint point2 = blendPoints(segment.targetPoint, m_controlPoint); |
162 | 162 |
163 m_consumer->curveToCubic(point1, point2, segment.targetPoint, AbsoluteCoordi
nates); | 163 m_consumer->curveToCubic(point1, point2, segment.targetPoint, AbsoluteCoordi
nates); |
164 | 164 |
165 m_currentPoint = segment.targetPoint; | 165 m_currentPoint = segment.targetPoint; |
166 } | 166 } |
167 | 167 |
168 void SVGPathParser::emitArcToSegment(PathSegmentData& segment) | 168 void SVGPathParser::emitArcToSegment(PathSegmentData& segment) |
169 { | 169 { |
170 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (
a "lineto") joining the endpoints. | |
171 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters | |
172 // If the current point and target point for the arc are identical, it shoul
d be treated as a zero length | |
173 // path. This ensures continuity in animations. | |
174 float rx = fabsf(segment.arcRadii().x()); | |
175 float ry = fabsf(segment.arcRadii().y()); | |
176 | |
177 if (m_mode == RelativeCoordinates) | 170 if (m_mode == RelativeCoordinates) |
178 segment.targetPoint += m_currentPoint; | 171 segment.targetPoint += m_currentPoint; |
179 | 172 |
180 if (!rx || !ry || segment.targetPoint == m_currentPoint) { | 173 if (!decomposeArcToCubic(m_currentPoint, segment)) |
181 m_consumer->lineTo(segment.targetPoint, AbsoluteCoordinates); | 174 m_consumer->lineTo(segment.targetPoint, AbsoluteCoordinates); |
182 m_currentPoint = segment.targetPoint; | |
183 return; | |
184 } | |
185 | |
186 float angle = segment.arcAngle(); | |
187 FloatPoint point1 = m_currentPoint; | |
188 m_currentPoint = segment.targetPoint; | 175 m_currentPoint = segment.targetPoint; |
189 if (!decomposeArcToCubic(angle, rx, ry, point1, segment.targetPoint, segment
.arcLarge, segment.arcSweep)) | |
190 m_consumer->lineTo(segment.targetPoint, AbsoluteCoordinates); | |
191 } | 176 } |
192 | 177 |
193 bool SVGPathParser::parsePathDataFromSource(PathParsingMode pathParsingMode, boo
l checkForInitialMoveTo) | 178 bool SVGPathParser::parsePathDataFromSource(PathParsingMode pathParsingMode, boo
l checkForInitialMoveTo) |
194 { | 179 { |
195 ASSERT(m_source); | 180 ASSERT(m_source); |
196 ASSERT(m_consumer); | 181 ASSERT(m_consumer); |
197 | 182 |
198 m_controlPoint = FloatPoint(); | 183 m_controlPoint = FloatPoint(); |
199 m_currentPoint = FloatPoint(); | 184 m_currentPoint = FloatPoint(); |
200 m_subPathPoint = FloatPoint(); | 185 m_subPathPoint = FloatPoint(); |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
339 | 324 |
340 if (m_source->hasMoreData()) | 325 if (m_source->hasMoreData()) |
341 m_consumer->incrementPathSegmentCount(); | 326 m_consumer->incrementPathSegmentCount(); |
342 } | 327 } |
343 return true; | 328 return true; |
344 } | 329 } |
345 | 330 |
346 // This works by converting the SVG arc to "simple" beziers. | 331 // This works by converting the SVG arc to "simple" beziers. |
347 // Partly adapted from Niko's code in kdelibs/kdecore/svgicons. | 332 // Partly adapted from Niko's code in kdelibs/kdecore/svgicons. |
348 // See also SVG implementation notes: http://www.w3.org/TR/SVG/implnote.html#Arc
ConversionEndpointToCenter | 333 // See also SVG implementation notes: http://www.w3.org/TR/SVG/implnote.html#Arc
ConversionEndpointToCenter |
349 bool SVGPathParser::decomposeArcToCubic(float angle, float rx, float ry, const F
loatPoint& start, const FloatPoint& end, bool largeArcFlag, bool sweepFlag) | 334 bool SVGPathParser::decomposeArcToCubic(const FloatPoint& currentPoint, const Pa
thSegmentData& arcSegment) |
350 { | 335 { |
351 FloatSize midPointDistance = start - end; | 336 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (
a "lineto") joining the endpoints. |
| 337 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters |
| 338 float rx = fabsf(arcSegment.arcRadii().x()); |
| 339 float ry = fabsf(arcSegment.arcRadii().y()); |
| 340 if (!rx || !ry) |
| 341 return false; |
| 342 |
| 343 // If the current point and target point for the arc are identical, it shoul
d be treated as a zero length |
| 344 // path. This ensures continuity in animations. |
| 345 if (arcSegment.targetPoint == currentPoint) |
| 346 return false; |
| 347 |
| 348 float angle = arcSegment.arcAngle(); |
| 349 |
| 350 FloatSize midPointDistance = currentPoint - arcSegment.targetPoint; |
352 midPointDistance.scale(0.5f); | 351 midPointDistance.scale(0.5f); |
353 | 352 |
354 AffineTransform pointTransform; | 353 AffineTransform pointTransform; |
355 pointTransform.rotate(-angle); | 354 pointTransform.rotate(-angle); |
356 | 355 |
357 FloatPoint transformedMidPoint = pointTransform.mapPoint(FloatPoint(midPoint
Distance.width(), midPointDistance.height())); | 356 FloatPoint transformedMidPoint = pointTransform.mapPoint(FloatPoint(midPoint
Distance.width(), midPointDistance.height())); |
358 float squareRx = rx * rx; | 357 float squareRx = rx * rx; |
359 float squareRy = ry * ry; | 358 float squareRy = ry * ry; |
360 float squareX = transformedMidPoint.x() * transformedMidPoint.x(); | 359 float squareX = transformedMidPoint.x() * transformedMidPoint.x(); |
361 float squareY = transformedMidPoint.y() * transformedMidPoint.y(); | 360 float squareY = transformedMidPoint.y() * transformedMidPoint.y(); |
362 | 361 |
363 // Check if the radii are big enough to draw the arc, scale radii if not. | 362 // Check if the radii are big enough to draw the arc, scale radii if not. |
364 // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii | 363 // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii |
365 float radiiScale = squareX / squareRx + squareY / squareRy; | 364 float radiiScale = squareX / squareRx + squareY / squareRy; |
366 if (radiiScale > 1) { | 365 if (radiiScale > 1) { |
367 rx *= sqrtf(radiiScale); | 366 rx *= sqrtf(radiiScale); |
368 ry *= sqrtf(radiiScale); | 367 ry *= sqrtf(radiiScale); |
369 } | 368 } |
370 | 369 |
371 pointTransform.makeIdentity(); | 370 pointTransform.makeIdentity(); |
372 pointTransform.scale(1 / rx, 1 / ry); | 371 pointTransform.scale(1 / rx, 1 / ry); |
373 pointTransform.rotate(-angle); | 372 pointTransform.rotate(-angle); |
374 | 373 |
375 FloatPoint point1 = pointTransform.mapPoint(start); | 374 FloatPoint point1 = pointTransform.mapPoint(currentPoint); |
376 FloatPoint point2 = pointTransform.mapPoint(end); | 375 FloatPoint point2 = pointTransform.mapPoint(arcSegment.targetPoint); |
377 FloatSize delta = point2 - point1; | 376 FloatSize delta = point2 - point1; |
378 | 377 |
379 float d = delta.width() * delta.width() + delta.height() * delta.height(); | 378 float d = delta.width() * delta.width() + delta.height() * delta.height(); |
380 float scaleFactorSquared = std::max(1 / d - 0.25f, 0.f); | 379 float scaleFactorSquared = std::max(1 / d - 0.25f, 0.f); |
381 | 380 |
382 float scaleFactor = sqrtf(scaleFactorSquared); | 381 float scaleFactor = sqrtf(scaleFactorSquared); |
383 if (sweepFlag == largeArcFlag) | 382 if (arcSegment.arcSweep == arcSegment.arcLarge) |
384 scaleFactor = -scaleFactor; | 383 scaleFactor = -scaleFactor; |
385 | 384 |
386 delta.scale(scaleFactor); | 385 delta.scale(scaleFactor); |
387 FloatPoint centerPoint = point1 + point2; | 386 FloatPoint centerPoint = point1 + point2; |
388 centerPoint.scale(0.5f, 0.5f); | 387 centerPoint.scale(0.5f, 0.5f); |
389 centerPoint.move(-delta.height(), delta.width()); | 388 centerPoint.move(-delta.height(), delta.width()); |
390 | 389 |
391 float theta1 = FloatPoint(point1 - centerPoint).slopeAngleRadians(); | 390 float theta1 = FloatPoint(point1 - centerPoint).slopeAngleRadians(); |
392 float theta2 = FloatPoint(point2 - centerPoint).slopeAngleRadians(); | 391 float theta2 = FloatPoint(point2 - centerPoint).slopeAngleRadians(); |
393 | 392 |
394 float thetaArc = theta2 - theta1; | 393 float thetaArc = theta2 - theta1; |
395 if (thetaArc < 0 && sweepFlag) | 394 if (thetaArc < 0 && arcSegment.arcSweep) |
396 thetaArc += twoPiFloat; | 395 thetaArc += twoPiFloat; |
397 else if (thetaArc > 0 && !sweepFlag) | 396 else if (thetaArc > 0 && !arcSegment.arcSweep) |
398 thetaArc -= twoPiFloat; | 397 thetaArc -= twoPiFloat; |
399 | 398 |
400 pointTransform.makeIdentity(); | 399 pointTransform.makeIdentity(); |
401 pointTransform.rotate(angle); | 400 pointTransform.rotate(angle); |
402 pointTransform.scale(rx, ry); | 401 pointTransform.scale(rx, ry); |
403 | 402 |
404 // Some results of atan2 on some platform implementations are not exact enou
gh. So that we get more | 403 // Some results of atan2 on some platform implementations are not exact enou
gh. So that we get more |
405 // cubic curves than expected here. Adding 0.001f reduces the count of sgeme
nts to the correct count. | 404 // cubic curves than expected here. Adding 0.001f reduces the count of sgeme
nts to the correct count. |
406 int segments = ceilf(fabsf(thetaArc / (piOverTwoFloat + 0.001f))); | 405 int segments = ceilf(fabsf(thetaArc / (piOverTwoFloat + 0.001f))); |
407 for (int i = 0; i < segments; ++i) { | 406 for (int i = 0; i < segments; ++i) { |
(...skipping 15 matching lines...) Expand all Loading... |
423 point2 = targetPoint; | 422 point2 = targetPoint; |
424 point2.move(t * sinEndTheta, -t * cosEndTheta); | 423 point2.move(t * sinEndTheta, -t * cosEndTheta); |
425 | 424 |
426 m_consumer->curveToCubic(pointTransform.mapPoint(point1), pointTransform
.mapPoint(point2), | 425 m_consumer->curveToCubic(pointTransform.mapPoint(point1), pointTransform
.mapPoint(point2), |
427 pointTransform.mapPoint(targetPoint), AbsoluteC
oordinates); | 426 pointTransform.mapPoint(targetPoint), AbsoluteC
oordinates); |
428 } | 427 } |
429 return true; | 428 return true; |
430 } | 429 } |
431 | 430 |
432 } | 431 } |
OLD | NEW |