| 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 |