PathSegmentData SVGPathByteStreamSource::parseSegment()
{
    ASSERT(hasMoreData());
    PathSegmentData segment;
    segment.command = static_cast<SVGPathSegType>(readSVGSegmentType());

    switch (segment.command) {
    case PathSegCurveToCubicRel:
    case PathSegCurveToCubicAbs:
        segment.point1 = readFloatPoint();
        /* fall through */
    case PathSegCurveToCubicSmoothRel:
    case PathSegCurveToCubicSmoothAbs:
        segment.point2 = readFloatPoint();
        /* fall through */
    case PathSegMoveToRel:
    case PathSegMoveToAbs:
    case PathSegLineToRel:
    case PathSegLineToAbs:
    case PathSegCurveToQuadraticSmoothRel:
    case PathSegCurveToQuadraticSmoothAbs:
        segment.targetPoint = readFloatPoint();
        break;
    case PathSegLineToHorizontalRel:
    case PathSegLineToHorizontalAbs:
        segment.targetPoint.setX(readFloat());
        break;
    case PathSegLineToVerticalRel:
    case PathSegLineToVerticalAbs:
        segment.targetPoint.setY(readFloat());
        break;
    case PathSegClosePath:
        break;
    case PathSegCurveToQuadraticRel:
    case PathSegCurveToQuadraticAbs:
        segment.point1 = readFloatPoint();
        segment.targetPoint = readFloatPoint();
        break;
    case PathSegArcRel:
    case PathSegArcAbs: {
        segment.arcRadii() = readFloatPoint();
        segment.setArcAngle(readFloat());
        segment.arcLarge = readFlag();
        segment.arcSweep = readFlag();
        segment.targetPoint = readFloatPoint();
        break;
    }
    default:
        ASSERT_NOT_REACHED();
    }
    return segment;
}
PathSegmentData consumeInterpolableArc(const InterpolableValue& value,
                                       SVGPathSegType segType,
                                       PathCoordinates& coordinates) {
  const InterpolableList& list = toInterpolableList(value);
  bool isAbsolute = isAbsolutePathSegType(segType);
  PathSegmentData segment;
  segment.command = segType;
  segment.targetPoint.setX(consumeInterpolableCoordinateAxis(
      list.get(0), isAbsolute, coordinates.currentX));
  segment.targetPoint.setY(consumeInterpolableCoordinateAxis(
      list.get(1), isAbsolute, coordinates.currentY));
  segment.arcRadii().setX(toInterpolableNumber(list.get(2))->value());
  segment.arcRadii().setY(toInterpolableNumber(list.get(3))->value());
  segment.setArcAngle(toInterpolableNumber(list.get(4))->value());
  segment.arcLarge = toInterpolableBool(list.get(5))->value();
  segment.arcSweep = toInterpolableBool(list.get(6))->value();
  return segment;
}
示例#3
0
bool SVGPathBlender::BlendState::blendSegments(
    const PathSegmentData& fromSeg,
    const PathSegmentData& toSeg,
    PathSegmentData& blendedSegment) {
  if (!canBlend(fromSeg, toSeg))
    return false;

  blendedSegment.command =
      m_isInFirstHalfOfAnimation ? fromSeg.command : toSeg.command;

  switch (toSeg.command) {
    case PathSegCurveToCubicRel:
    case PathSegCurveToCubicAbs:
      blendedSegment.point1 =
          blendAnimatedFloatPoint(fromSeg.point1, toSeg.point1);
    /* fall through */
    case PathSegCurveToCubicSmoothRel:
    case PathSegCurveToCubicSmoothAbs:
      blendedSegment.point2 =
          blendAnimatedFloatPoint(fromSeg.point2, toSeg.point2);
    /* fall through */
    case PathSegMoveToRel:
    case PathSegMoveToAbs:
    case PathSegLineToRel:
    case PathSegLineToAbs:
    case PathSegCurveToQuadraticSmoothRel:
    case PathSegCurveToQuadraticSmoothAbs:
      blendedSegment.targetPoint =
          blendAnimatedFloatPoint(fromSeg.targetPoint, toSeg.targetPoint);
      break;
    case PathSegLineToHorizontalRel:
    case PathSegLineToHorizontalAbs:
      blendedSegment.targetPoint.setX(blendAnimatedDimensonalFloat(
          fromSeg.targetPoint.x(), toSeg.targetPoint.x(), BlendHorizontal));
      break;
    case PathSegLineToVerticalRel:
    case PathSegLineToVerticalAbs:
      blendedSegment.targetPoint.setY(blendAnimatedDimensonalFloat(
          fromSeg.targetPoint.y(), toSeg.targetPoint.y(), BlendVertical));
      break;
    case PathSegClosePath:
      break;
    case PathSegCurveToQuadraticRel:
    case PathSegCurveToQuadraticAbs:
      blendedSegment.targetPoint =
          blendAnimatedFloatPoint(fromSeg.targetPoint, toSeg.targetPoint);
      blendedSegment.point1 =
          blendAnimatedFloatPoint(fromSeg.point1, toSeg.point1);
      break;
    case PathSegArcRel:
    case PathSegArcAbs:
      blendedSegment.targetPoint =
          blendAnimatedFloatPoint(fromSeg.targetPoint, toSeg.targetPoint);
      blendedSegment.point1 = blendAnimatedFloatPointSameCoordinates(
          fromSeg.arcRadii(), toSeg.arcRadii());
      blendedSegment.point2 =
          blendAnimatedFloatPointSameCoordinates(fromSeg.point2, toSeg.point2);
      if (m_addTypesCount) {
        blendedSegment.arcLarge = fromSeg.arcLarge || toSeg.arcLarge;
        blendedSegment.arcSweep = fromSeg.arcSweep || toSeg.arcSweep;
      } else {
        blendedSegment.arcLarge =
            m_isInFirstHalfOfAnimation ? fromSeg.arcLarge : toSeg.arcLarge;
        blendedSegment.arcSweep =
            m_isInFirstHalfOfAnimation ? fromSeg.arcSweep : toSeg.arcSweep;
      }
      break;
    default:
      ASSERT_NOT_REACHED();
  }

  updateCurrentPoint(m_fromSubPathPoint, m_fromCurrentPoint, fromSeg);
  updateCurrentPoint(m_toSubPathPoint, m_toCurrentPoint, toSeg);

  return true;
}
void SVGPathBuilder::emitSegment(const PathSegmentData& segment)
{
    switch (segment.command) {
    case PathSegClosePath:
        emitClose();
        break;
    case PathSegMoveToAbs:
        emitMoveTo(
            segment.targetPoint);
        break;
    case PathSegMoveToRel:
        emitMoveTo(
            m_currentPoint + segment.targetPoint);
        break;
    case PathSegLineToAbs:
        emitLineTo(
            segment.targetPoint);
        break;
    case PathSegLineToRel:
        emitLineTo(
            m_currentPoint + segment.targetPoint);
        break;
    case PathSegLineToHorizontalAbs:
        emitLineTo(
            FloatPoint(segment.targetPoint.x(), m_currentPoint.y()));
        break;
    case PathSegLineToHorizontalRel:
        emitLineTo(
            m_currentPoint + FloatSize(segment.targetPoint.x(), 0));
        break;
    case PathSegLineToVerticalAbs:
        emitLineTo(
            FloatPoint(m_currentPoint.x(), segment.targetPoint.y()));
        break;
    case PathSegLineToVerticalRel:
        emitLineTo(
            m_currentPoint + FloatSize(0, segment.targetPoint.y()));
        break;
    case PathSegCurveToQuadraticAbs:
        emitQuadTo(
            segment.point1,
            segment.targetPoint);
        break;
    case PathSegCurveToQuadraticRel:
        emitQuadTo(
            m_currentPoint + segment.point1,
            m_currentPoint + segment.targetPoint);
        break;
    case PathSegCurveToQuadraticSmoothAbs:
        emitSmoothQuadTo(
            segment.targetPoint);
        break;
    case PathSegCurveToQuadraticSmoothRel:
        emitSmoothQuadTo(
            m_currentPoint + segment.targetPoint);
        break;
    case PathSegCurveToCubicAbs:
        emitCubicTo(
            segment.point1,
            segment.point2,
            segment.targetPoint);
        break;
    case PathSegCurveToCubicRel:
        emitCubicTo(
            m_currentPoint + segment.point1,
            m_currentPoint + segment.point2,
            m_currentPoint + segment.targetPoint);
        break;
    case PathSegCurveToCubicSmoothAbs:
        emitSmoothCubicTo(
            segment.point2,
            segment.targetPoint);
        break;
    case PathSegCurveToCubicSmoothRel:
        emitSmoothCubicTo(
            m_currentPoint + segment.point2,
            m_currentPoint + segment.targetPoint);
        break;
    case PathSegArcAbs:
        emitArcTo(
            segment.targetPoint,
            toFloatSize(segment.arcRadii()),
            segment.arcAngle(),
            segment.largeArcFlag(),
            segment.sweepFlag());
        break;
    case PathSegArcRel:
        emitArcTo(
            m_currentPoint + segment.targetPoint,
            toFloatSize(segment.arcRadii()),
            segment.arcAngle(),
            segment.largeArcFlag(),
            segment.sweepFlag());
        break;
    default:
        ASSERT_NOT_REACHED();
    }

    m_lastCommand = segment.command;
}
示例#5
0
// 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;
}