TEST_F(DisplayItemPropertyTreeBuilderTest, TransformDisplayItemCreatesTransformNode) { // 2D transform display items should create a transform node as well, // unless the transform is a 2D translation only. AffineTransform rotation; rotation.rotate(45); processDummyDisplayItem(); auto transformClient = processBeginTransform(rotation); processDummyDisplayItem(); processEndTransform(transformClient); processDummyDisplayItem(); finishPropertyTrees(); // There should be two transform nodes. ASSERT_EQ(2u, transformTree().nodeCount()); EXPECT_TRUE(transformTree().nodeAt(0).isRoot()); EXPECT_EQ(0u, transformTree().nodeAt(1).parentNodeIndex); EXPECT_TRANSFORMS_ALMOST_EQ(TransformationMatrix(rotation), transformTree().nodeAt(1).matrix); // There should be three range records, the middle one affected by the // rotation. EXPECT_THAT(rangeRecordsAsStdVector(), ElementsAre( AllOf(hasRange(0, 1), hasTransformNode(0)), AllOf(hasRange(2, 3), hasTransformNode(1)), AllOf(hasRange(4, 5), hasTransformNode(0)))); }
void GraphicsContext::rotate(float radians) { if (paintingDisabled()) return; AffineTransform current; platformContext()->getTransform((double*)¤t); current.rotate(rad2deg(radians)); platformContext()->setTransform((double*)¤t); }
AffineTransform SVGResourceMarker::markerTransformation(const FloatPoint& origin, float angle, float strokeWidth) const { ASSERT(m_renderer); AffineTransform transform; transform.translate(origin.x(), origin.y()); transform.rotate(m_angle == -1 ? angle : m_angle); transform = m_renderer->markerContentTransformation(transform, m_referencePoint, m_useStrokeWidth ? strokeWidth : -1); return transform; }
static void affineTransformCompose(AffineTransform& m, const double sr[9]) { m.setA(sr[3]); m.setB(sr[4]); m.setC(sr[5]); m.setD(sr[6]); m.setE(sr[7]); m.setF(sr[8]); m.rotate(rad2deg(sr[2])); m.scale(sr[0], sr[1]); }
AffineTransform RenderSVGResourceMarker::markerTransformation(const FloatPoint& origin, float autoAngle, float strokeWidth) const { float markerAngle = angle(); bool useStrokeWidth = markerElement().markerUnits() == SVGMarkerUnitsStrokeWidth; AffineTransform transform; transform.translate(origin.x(), origin.y()); transform.rotate(markerAngle == -1 ? autoAngle : markerAngle); transform = markerContentTransformation(transform, referencePoint(), useStrokeWidth ? strokeWidth : -1); return transform; }
AffineTransform LayoutSVGResourceMarker::markerTransformation(const FloatPoint& origin, float autoAngle, float strokeWidth) const { SVGMarkerElement* marker = toSVGMarkerElement(element()); ASSERT(marker); float markerAngle = angle(); bool useStrokeWidth = marker->markerUnits()->currentValue()->enumValue() == SVGMarkerUnitsStrokeWidth; AffineTransform transform; transform.translate(origin.x(), origin.y()); transform.rotate(markerAngle == -1 ? autoAngle : markerAngle); transform = markerContentTransformation(transform, referencePoint(), useStrokeWidth ? strokeWidth : -1); return transform; }
AffineTransform RenderSVGResourceMarker::markerTransformation(const FloatPoint& origin, float autoAngle, float strokeWidth) const { SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node()); ASSERT(marker); float markerAngle = angle(); bool useStrokeWidth = (marker->markerUnits() == SVGMarkerElement::SVG_MARKERUNITS_STROKEWIDTH); AffineTransform transform; transform.translate(origin.x(), origin.y()); transform.rotate(markerAngle == -1 ? autoAngle : markerAngle); transform = markerContentTransformation(transform, referencePoint(), useStrokeWidth ? strokeWidth : -1); return transform; }
static v8::Handle<v8::Value> rotateCallback(const v8::Arguments& args) { INC_STATS("DOM.SVGMatrix.rotate"); V8SVGPODTypeWrapper<AffineTransform>* impWrapper = V8SVGPODTypeWrapper<AffineTransform>::toNative(args.Holder()); AffineTransform impInstance = *impWrapper; AffineTransform* imp = &impInstance; EXCEPTION_BLOCK(float, angle, static_cast<float>(args[0]->NumberValue())); AffineTransform result = *imp; result.rotate(angle); RefPtr<V8SVGPODTypeWrapper<AffineTransform> > wrapper = V8SVGStaticPODTypeWrapper<AffineTransform>::create(result); SVGElement* context = V8Proxy::svgContext(impWrapper); V8Proxy::setSVGContext(wrapper.get(), context); impWrapper->commitChange(impInstance, context); return toV8(wrapper.release()); }
AffineTransform LayoutSVGResourceMarker::markerTransformation( const FloatPoint& origin, float autoAngle, float strokeWidth) const { // Apply scaling according to markerUnits ('strokeWidth' or 'userSpaceOnUse'.) float markerScale = markerUnits() == SVGMarkerUnitsStrokeWidth ? strokeWidth : 1; AffineTransform transform; transform.translate(origin.x(), origin.y()); transform.rotate(orientType() == SVGMarkerOrientAngle ? angle() : autoAngle); transform.scale(markerScale); // The 'origin' coordinate maps to SVGs refX/refY, given in coordinates // relative to the viewport established by the marker. FloatPoint mappedReferencePoint = viewportTransform().mapPoint(referencePoint()); transform.translate(-mappedReferencePoint.x(), -mappedReferencePoint.y()); return transform; }
void SVGAnimateMotionElement::applyAnimatedValueToElement() { if (!targetElement()->isStyledTransformable()) return; SVGStyledTransformableElement* transformableElement = static_cast<SVGStyledTransformableElement*>(targetElement()); RefPtr<SVGTransformList> transformList = transformableElement->transform(); if (!transformList) return; ExceptionCode ec; if (!isAdditive()) transformList->clear(ec); AffineTransform transform; transform.rotate(m_animatedAngle); transform.translate(m_animatedTranslation.width(), m_animatedTranslation.height()); if (!transform.isIdentity()) { transformList->appendItem(SVGTransform(transform), ec); transformableElement->updateLocalTransform(transformList.get()); } }
void CanvasRenderingContext2D::rotate(float angleInRadians) { GraphicsContext* c = drawingContext(); if (!c) return; if (!state().m_invertibleCTM) return; if (!isfinite(angleInRadians)) return; AffineTransform newTransform = state().m_transform; newTransform.rotate(angleInRadians / piDouble * 180.0); if (!newTransform.isInvertible()) { state().m_invertibleCTM = false; return; } state().m_transform = newTransform; c->rotate(angleInRadians); m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0)); }
void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned, SVGSMILElement*) { SVGElement* targetElement = this->targetElement(); if (!targetElement) return; AffineTransform* transform = targetElement->supplementalTransform(); if (!transform) return; if (RenderObject* targetRenderer = targetElement->renderer()) targetRenderer->setNeedsTransformUpdate(); if (!isAdditive()) transform->makeIdentity(); // FIXME: Implement accumulate. if (animationMode() == PathAnimation) { ASSERT(!animationPath().isEmpty()); Path path = animationPath(); float positionOnPath = path.length() * percentage; bool ok; FloatPoint position = path.pointAtLength(positionOnPath, ok); if (ok) { transform->translate(position.x(), position.y()); RotateMode rotateMode = this->rotateMode(); if (rotateMode == RotateAuto || rotateMode == RotateAutoReverse) { float angle = path.normalAngleAtLength(positionOnPath, ok); if (rotateMode == RotateAutoReverse) angle += 180; transform->rotate(angle); } } return; } FloatSize diff = m_toPoint - m_fromPoint; transform->translate(diff.width() * percentage + m_fromPoint.x(), diff.height() * percentage + m_fromPoint.y()); }
// This works by converting the SVG arc to "simple" beziers. // Partly adapted from Niko's code in kdelibs/kdecore/svgicons. // See also SVG implementation notes: http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter bool SVGPathParser::decomposeArcToCubic(float angle, float rx, float ry, FloatPoint& point1, FloatPoint& point2, bool largeArcFlag, bool sweepFlag) { FloatSize midPointDistance = point1 - point2; midPointDistance.scale(0.5f); AffineTransform pointTransform; pointTransform.rotate(-angle); FloatPoint transformedMidPoint = pointTransform.mapPoint(FloatPoint(midPointDistance.width(), midPointDistance.height())); float squareRx = rx * rx; float squareRy = ry * ry; float squareX = transformedMidPoint.x() * transformedMidPoint.x(); float squareY = transformedMidPoint.y() * transformedMidPoint.y(); // Check if the radii are big enough to draw the arc, scale radii if not. // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii float radiiScale = squareX / squareRx + squareY / squareRy; if (radiiScale > 1) { rx *= sqrtf(radiiScale); ry *= sqrtf(radiiScale); } pointTransform.makeIdentity(); pointTransform.scale(1 / rx, 1 / ry); pointTransform.rotate(-angle); point1 = pointTransform.mapPoint(point1); point2 = pointTransform.mapPoint(point2); FloatSize delta = point2 - point1; float d = delta.width() * delta.width() + delta.height() * delta.height(); float scaleFactorSquared = std::max(1 / d - 0.25f, 0.f); float scaleFactor = sqrtf(scaleFactorSquared); if (sweepFlag == largeArcFlag) scaleFactor = -scaleFactor; delta.scale(scaleFactor); FloatPoint centerPoint = point1 + point2; centerPoint.scale(0.5f); centerPoint.move(-delta.height(), delta.width()); float theta1 = FloatPoint(point1 - centerPoint).slopeAngleRadians(); float theta2 = FloatPoint(point2 - centerPoint).slopeAngleRadians(); float thetaArc = theta2 - theta1; if (thetaArc < 0 && sweepFlag) thetaArc += 2 * piFloat; else if (thetaArc > 0 && !sweepFlag) thetaArc -= 2 * piFloat; pointTransform.makeIdentity(); pointTransform.rotate(angle); pointTransform.scale(rx, ry); // Some results of atan2 on some platform implementations are not exact enough. So that we get more // cubic curves than expected here. Adding 0.001f reduces the count of sgements to the correct count. int segments = ceilf(fabsf(thetaArc / (piOverTwoFloat + 0.001f))); for (int i = 0; i < segments; ++i) { float startTheta = theta1 + i * thetaArc / segments; float endTheta = theta1 + (i + 1) * thetaArc / segments; float t = (8 / 6.f) * tanf(0.25f * (endTheta - startTheta)); if (!std::isfinite(t)) return false; float sinStartTheta = sinf(startTheta); float cosStartTheta = cosf(startTheta); float sinEndTheta = sinf(endTheta); float cosEndTheta = cosf(endTheta); point1 = FloatPoint(cosStartTheta - t * sinStartTheta, sinStartTheta + t * cosStartTheta); point1.move(centerPoint.x(), centerPoint.y()); FloatPoint targetPoint = FloatPoint(cosEndTheta, sinEndTheta); targetPoint.move(centerPoint.x(), centerPoint.y()); point2 = targetPoint; point2.move(t * sinEndTheta, -t * cosEndTheta); m_consumer.curveToCubic(pointTransform.mapPoint(point1), pointTransform.mapPoint(point2), pointTransform.mapPoint(targetPoint), AbsoluteCoordinates); } return true; }
// This works by converting the SVG arc to "simple" beziers. // Partly adapted from Niko's code in kdelibs/kdecore/svgicons. // See also SVG implementation notes: // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter bool SVGPathNormalizer::decomposeArcToCubic(const FloatPoint& currentPoint, const PathSegmentData& arcSegment) { // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a // "lineto") joining the endpoints. // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters float rx = fabsf(arcSegment.arcRadii().x()); float ry = fabsf(arcSegment.arcRadii().y()); if (!rx || !ry) return false; // If the current point and target point for the arc are identical, it should // be treated as a zero length path. This ensures continuity in animations. if (arcSegment.targetPoint == currentPoint) return false; float angle = arcSegment.arcAngle(); FloatSize midPointDistance = currentPoint - arcSegment.targetPoint; midPointDistance.scale(0.5f); AffineTransform pointTransform; pointTransform.rotate(-angle); FloatPoint transformedMidPoint = pointTransform.mapPoint( FloatPoint(midPointDistance.width(), midPointDistance.height())); float squareRx = rx * rx; float squareRy = ry * ry; float squareX = transformedMidPoint.x() * transformedMidPoint.x(); float squareY = transformedMidPoint.y() * transformedMidPoint.y(); // Check if the radii are big enough to draw the arc, scale radii if not. // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii float radiiScale = squareX / squareRx + squareY / squareRy; if (radiiScale > 1) { rx *= sqrtf(radiiScale); ry *= sqrtf(radiiScale); } pointTransform.makeIdentity(); pointTransform.scale(1 / rx, 1 / ry); pointTransform.rotate(-angle); FloatPoint point1 = pointTransform.mapPoint(currentPoint); FloatPoint point2 = pointTransform.mapPoint(arcSegment.targetPoint); FloatSize delta = point2 - point1; float d = delta.width() * delta.width() + delta.height() * delta.height(); float scaleFactorSquared = std::max(1 / d - 0.25f, 0.f); float scaleFactor = sqrtf(scaleFactorSquared); if (arcSegment.arcSweep == arcSegment.arcLarge) scaleFactor = -scaleFactor; delta.scale(scaleFactor); FloatPoint centerPoint = point1 + point2; centerPoint.scale(0.5f, 0.5f); centerPoint.move(-delta.height(), delta.width()); float theta1 = FloatPoint(point1 - centerPoint).slopeAngleRadians(); float theta2 = FloatPoint(point2 - centerPoint).slopeAngleRadians(); float thetaArc = theta2 - theta1; if (thetaArc < 0 && arcSegment.arcSweep) thetaArc += twoPiFloat; else if (thetaArc > 0 && !arcSegment.arcSweep) thetaArc -= twoPiFloat; pointTransform.makeIdentity(); pointTransform.rotate(angle); pointTransform.scale(rx, ry); // Some results of atan2 on some platform implementations are not exact // enough. So that we get more cubic curves than expected here. Adding 0.001f // reduces the count of sgements to the correct count. int segments = ceilf(fabsf(thetaArc / (piOverTwoFloat + 0.001f))); for (int i = 0; i < segments; ++i) { float startTheta = theta1 + i * thetaArc / segments; float endTheta = theta1 + (i + 1) * thetaArc / segments; float t = (8 / 6.f) * tanf(0.25f * (endTheta - startTheta)); if (!std::isfinite(t)) return false; float sinStartTheta = sinf(startTheta); float cosStartTheta = cosf(startTheta); float sinEndTheta = sinf(endTheta); float cosEndTheta = cosf(endTheta); point1 = FloatPoint(cosStartTheta - t * sinStartTheta, sinStartTheta + t * cosStartTheta); point1.move(centerPoint.x(), centerPoint.y()); FloatPoint targetPoint = FloatPoint(cosEndTheta, sinEndTheta); targetPoint.move(centerPoint.x(), centerPoint.y()); point2 = targetPoint; point2.move(t * sinEndTheta, -t * cosEndTheta); PathSegmentData cubicSegment; cubicSegment.command = PathSegCurveToCubicAbs; cubicSegment.point1 = pointTransform.mapPoint(point1); cubicSegment.point2 = pointTransform.mapPoint(point2); cubicSegment.targetPoint = pointTransform.mapPoint(targetPoint); m_consumer->emitSegment(cubicSegment); } return true; }