Commit 9975cde8 authored by Andrew Osmond's avatar Andrew Osmond
Browse files

Bug 691187 - Prune zero-length segments for canvas strokes. r=lsalzman

parent 19abba01
Loading
Loading
Loading
Loading
+61 −14
Original line number Diff line number Diff line
@@ -2955,7 +2955,7 @@ void CanvasRenderingContext2D::BeginPath() {
void CanvasRenderingContext2D::Fill(const CanvasWindingRule& aWinding) {
  EnsureUserSpacePath(aWinding);

  if (!mPath) {
  if (!mPath || mPath->IsEmpty()) {
    return;
  }

@@ -2991,7 +2991,7 @@ void CanvasRenderingContext2D::Fill(const CanvasPath& aPath,
  }

  RefPtr<gfx::Path> gfxpath = aPath.GetPath(aWinding, mTarget);
  if (!gfxpath) {
  if (!gfxpath || gfxpath->IsEmpty()) {
    return;
  }

@@ -3022,7 +3022,7 @@ void CanvasRenderingContext2D::Fill(const CanvasPath& aPath,
void CanvasRenderingContext2D::Stroke() {
  EnsureUserSpacePath();

  if (!mPath) {
  if (!mPath || mPath->IsEmpty()) {
    return;
  }

@@ -3065,7 +3065,7 @@ void CanvasRenderingContext2D::Stroke(const CanvasPath& aPath) {
  RefPtr<gfx::Path> gfxpath =
      aPath.GetPath(CanvasWindingRule::Nonzero, mTarget);

  if (!gfxpath) {
  if (!gfxpath || gfxpath->IsEmpty()) {
    return;
  }

@@ -3219,6 +3219,10 @@ void CanvasRenderingContext2D::ArcTo(double aX1, double aY1, double aX2,
  Point p1(aX1, aY1);
  Point p2(aX2, aY2);

  if (!p1.IsFinite() || !p2.IsFinite() || !std::isfinite(aRadius)) {
    return;
  }

  // Execute these calculations in double precision to avoid cumulative
  // rounding errors.
  double dir, a2, b2, c2, cosx, sinx, d, anx, any, bnx, bny, x3, y3, x4, y4, cx,
@@ -3226,7 +3230,7 @@ void CanvasRenderingContext2D::ArcTo(double aX1, double aY1, double aX2,
  bool anticlockwise;

  if (p0 == p1 || p1 == p2 || aRadius == 0) {
    LineTo(p1.x, p1.y);
    LineTo(p1);
    return;
  }

@@ -3234,7 +3238,7 @@ void CanvasRenderingContext2D::ArcTo(double aX1, double aY1, double aX2,
  dir = (p2.x.value - p1.x.value) * (p0.y.value - p1.y.value) +
        (p2.y.value - p1.y.value) * (p1.x.value - p0.x.value);
  if (dir == 0) {
    LineTo(p1.x, p1.y);
    LineTo(p1);
    return;
  }

@@ -3285,8 +3289,16 @@ void CanvasRenderingContext2D::Rect(double aX, double aY, double aW,
                                    double aH) {
  EnsureWritablePath();

  if (!std::isfinite(aX) || !std::isfinite(aY) || !std::isfinite(aW) ||
      !std::isfinite(aH)) {
    return;
  }

  if (mPathBuilder) {
    mPathBuilder->MoveTo(Point(aX, aY));
    if (aW == 0 && aH == 0) {
      return;
    }
    mPathBuilder->LineTo(Point(aX + aW, aY));
    mPathBuilder->LineTo(Point(aX + aW, aY + aH));
    mPathBuilder->LineTo(Point(aX, aY + aH));
@@ -3294,6 +3306,9 @@ void CanvasRenderingContext2D::Rect(double aX, double aY, double aW,
  } else {
    mDSPathBuilder->MoveTo(
        mTarget->GetTransform().TransformPoint(Point(aX, aY)));
    if (aW == 0 && aH == 0) {
      return;
    }
    mDSPathBuilder->LineTo(
        mTarget->GetTransform().TransformPoint(Point(aX + aW, aY)));
    mDSPathBuilder->LineTo(
@@ -6310,21 +6325,30 @@ void CanvasPath::ClosePath() {
void CanvasPath::MoveTo(double aX, double aY) {
  EnsurePathBuilder();

  mPathBuilder->MoveTo(Point(ToFloat(aX), ToFloat(aY)));
  Point pos(ToFloat(aX), ToFloat(aY));
  if (!pos.IsFinite()) {
    return;
  }

void CanvasPath::LineTo(double aX, double aY) {
  EnsurePathBuilder();
  mPathBuilder->MoveTo(pos);
}

  mPathBuilder->LineTo(Point(ToFloat(aX), ToFloat(aY)));
void CanvasPath::LineTo(double aX, double aY) {
  LineTo(Point(ToFloat(aX), ToFloat(aY)));
}

void CanvasPath::QuadraticCurveTo(double aCpx, double aCpy, double aX,
                                  double aY) {
  EnsurePathBuilder();

  mPathBuilder->QuadraticBezierTo(gfx::Point(ToFloat(aCpx), ToFloat(aCpy)),
                                  gfx::Point(ToFloat(aX), ToFloat(aY)));
  Point cp1(ToFloat(aCpx), ToFloat(aCpy));
  Point cp2(ToFloat(aX), ToFloat(aY));
  if (!cp1.IsFinite() || !cp2.IsFinite() ||
      (cp1 == mPathBuilder->CurrentPoint() && cp1 == cp2)) {
    return;
  }

  mPathBuilder->QuadraticBezierTo(cp1, cp2);
}

void CanvasPath::BezierCurveTo(double aCp1x, double aCp1y, double aCp2x,
@@ -6347,6 +6371,10 @@ void CanvasPath::ArcTo(double aX1, double aY1, double aX2, double aY2,
  Point p1(aX1, aY1);
  Point p2(aX2, aY2);

  if (!p1.IsFinite() || !p2.IsFinite() || !std::isfinite(aRadius)) {
    return;
  }

  // Execute these calculations in double precision to avoid cumulative
  // rounding errors.
  double dir, a2, b2, c2, cosx, sinx, d, anx, any, bnx, bny, x3, y3, x4, y4, cx,
@@ -6354,7 +6382,7 @@ void CanvasPath::ArcTo(double aX1, double aY1, double aX2, double aY2,
  bool anticlockwise;

  if (p0 == p1 || p1 == p2 || aRadius == 0) {
    LineTo(p1.x, p1.y);
    LineTo(p1);
    return;
  }

@@ -6362,7 +6390,7 @@ void CanvasPath::ArcTo(double aX1, double aY1, double aX2, double aY2,
  dir = (p2.x.value - p1.x.value) * (p0.y.value - p1.y.value) +
        (p2.y.value - p1.y.value) * (p1.x.value - p0.x.value);
  if (dir == 0) {
    LineTo(p1.x, p1.y);
    LineTo(p1);
    return;
  }

@@ -6397,7 +6425,17 @@ void CanvasPath::ArcTo(double aX1, double aY1, double aX2, double aY2,
}

void CanvasPath::Rect(double aX, double aY, double aW, double aH) {
  EnsurePathBuilder();

  if (!std::isfinite(aX) || !std::isfinite(aY) || !std::isfinite(aW) ||
      !std::isfinite(aH)) {
    return;
  }

  MoveTo(aX, aY);
  if (aW == 0 && aH == 0) {
    return;
  }
  LineTo(aX + aW, aY);
  LineTo(aX + aW, aY + aH);
  LineTo(aX, aY + aH);
@@ -6443,6 +6481,10 @@ void CanvasPath::Ellipse(double x, double y, double radiusX, double radiusY,
void CanvasPath::LineTo(const gfx::Point& aPoint) {
  EnsurePathBuilder();

  if (!aPoint.IsFinite() || aPoint == mPathBuilder->CurrentPoint()) {
    return;
  }

  mPathBuilder->LineTo(aPoint);
}

@@ -6450,6 +6492,11 @@ void CanvasPath::BezierTo(const gfx::Point& aCP1, const gfx::Point& aCP2,
                          const gfx::Point& aCP3) {
  EnsurePathBuilder();

  if (!aCP1.IsFinite() || !aCP2.IsFinite() || !aCP3.IsFinite() ||
      (aCP1 == mPathBuilder->CurrentPoint() && aCP1 == aCP2 && aCP1 == aCP3)) {
    return;
  }

  mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
}

+51 −14
Original line number Diff line number Diff line
@@ -337,11 +337,17 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
  void MoveTo(double aX, double aY) override {
    EnsureWritablePath();

    mozilla::gfx::Point pos(ToFloat(aX), ToFloat(aY));
    if (!pos.IsFinite()) {
      return;
    }

    if (mPathBuilder) {
      mPathBuilder->MoveTo(mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
      mPathBuilder->MoveTo(pos);
    } else {
      mDSPathBuilder->MoveTo(mTarget->GetTransform().TransformPoint(
          mozilla::gfx::Point(ToFloat(aX), ToFloat(aY))));
      mozilla::gfx::Point transformedPos =
          mTarget->GetTransform().TransformPoint(pos);
      mDSPathBuilder->MoveTo(transformedPos);
    }
  }

@@ -355,17 +361,25 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
                        double aY) override {
    EnsureWritablePath();

    mozilla::gfx::Point cp1(ToFloat(aCpx), ToFloat(aCpy));
    mozilla::gfx::Point cp2(ToFloat(aX), ToFloat(aY));
    if (!cp1.IsFinite() || !cp2.IsFinite()) {
      return;
    }

    if (mPathBuilder) {
      mPathBuilder->QuadraticBezierTo(
          mozilla::gfx::Point(ToFloat(aCpx), ToFloat(aCpy)),
          mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
      if (cp1 == mPathBuilder->CurrentPoint() && cp1 == cp2) {
        return;
      }
      mPathBuilder->QuadraticBezierTo(cp1, cp2);
    } else {
      mozilla::gfx::Matrix transform = mTarget->GetTransform();
      mDSPathBuilder->QuadraticBezierTo(
          transform.TransformPoint(
              mozilla::gfx::Point(ToFloat(aCpx), ToFloat(aCpy))),
          transform.TransformPoint(
              mozilla::gfx::Point(ToFloat(aX), ToFloat(aY))));
      mozilla::gfx::Point transformedPos = transform.TransformPoint(cp1);
      if (transformedPos == mDSPathBuilder->CurrentPoint() && cp1 == cp2) {
        return;
      }
      mDSPathBuilder->QuadraticBezierTo(transformedPos,
                                        transform.TransformPoint(cp2));
    }
  }

@@ -501,22 +515,45 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
  enum class Style : uint8_t { STROKE = 0, FILL, MAX };

  void LineTo(const mozilla::gfx::Point& aPoint) {
    if (!aPoint.IsFinite()) {
      return;
    }
    if (mPathBuilder) {
      if (mPathBuilder->CurrentPoint() == aPoint) {
        return;
      }
      mPathBuilder->LineTo(aPoint);
    } else {
      mDSPathBuilder->LineTo(mTarget->GetTransform().TransformPoint(aPoint));
      mozilla::gfx::Point transformedPt =
          mTarget->GetTransform().TransformPoint(aPoint);
      if (mDSPathBuilder->CurrentPoint() == transformedPt) {
        return;
      }
      mDSPathBuilder->LineTo(transformedPt);
    }
  }

  void BezierTo(const mozilla::gfx::Point& aCP1,
                const mozilla::gfx::Point& aCP2,
                const mozilla::gfx::Point& aCP3) {
    if (!aCP1.IsFinite() || !aCP2.IsFinite() || !aCP3.IsFinite()) {
      return;
    }

    if (mPathBuilder) {
      if (aCP1 == mPathBuilder->CurrentPoint() && aCP1 == aCP2 &&
          aCP1 == aCP3) {
        return;
      }
      mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
    } else {
      mozilla::gfx::Matrix transform = mTarget->GetTransform();
      mDSPathBuilder->BezierTo(transform.TransformPoint(aCP1),
                               transform.TransformPoint(aCP2),
      mozilla::gfx::Point transformedPos = transform.TransformPoint(aCP1);
      if (transformedPos == mDSPathBuilder->CurrentPoint() && aCP1 == aCP2 &&
          aCP1 == aCP3) {
        return;
      }
      mDSPathBuilder->BezierTo(transformedPos, transform.TransformPoint(aCP2),
                               transform.TransformPoint(aCP3));
    }
  }
+4 −4
Original line number Diff line number Diff line
@@ -14252,7 +14252,7 @@ ctx.lineTo(50, 25);
ctx.closePath();
ctx.stroke();
todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
isPixel(ctx, 50,25, 0,255,0,255, 0);
}
</script>
@@ -14324,7 +14324,7 @@ ctx.moveTo(50, 25);
ctx.bezierCurveTo(50, 25, 50, 25, 50, 25);
ctx.stroke();
todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
isPixel(ctx, 50,25, 0,255,0,255, 0);
}
</script>
@@ -14356,7 +14356,7 @@ ctx.moveTo(50, 25);
ctx.lineTo(50, 25);
ctx.stroke();
todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
isPixel(ctx, 50,25, 0,255,0,255, 0);
}
</script>
@@ -14389,7 +14389,7 @@ ctx.stroke();
ctx.strokeRect(50, 25, 0, 0);
todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
isPixel(ctx, 50,25, 0,255,0,255, 0);
}
</script>
+2 −0
Original line number Diff line number Diff line
@@ -1007,6 +1007,8 @@ class Path : public external::AtomicRefCounted<Path> {

  virtual Point ComputePointAtLength(Float aLength, Point* aTangent = nullptr);

  virtual bool IsEmpty() const { return false; }

 protected:
  Path();
  void EnsureFlattenedPath();
+6 −2
Original line number Diff line number Diff line
@@ -134,6 +134,7 @@ void PathBuilderD2D::LineTo(const Point& aPoint) {
  mSink->AddLine(D2DPoint(aPoint));

  mCurrentPoint = aPoint;
  mFigureEmpty = false;
}

void PathBuilderD2D::BezierTo(const Point& aCP1, const Point& aCP2,
@@ -143,6 +144,7 @@ void PathBuilderD2D::BezierTo(const Point& aCP1, const Point& aCP2,
      D2D1::BezierSegment(D2DPoint(aCP1), D2DPoint(aCP2), D2DPoint(aCP3)));

  mCurrentPoint = aCP3;
  mFigureEmpty = false;
}

void PathBuilderD2D::QuadraticBezierTo(const Point& aCP1, const Point& aCP2) {
@@ -151,6 +153,7 @@ void PathBuilderD2D::QuadraticBezierTo(const Point& aCP1, const Point& aCP2) {
      D2D1::QuadraticBezierSegment(D2DPoint(aCP1), D2DPoint(aCP2)));

  mCurrentPoint = aCP2;
  mFigureEmpty = false;
}

void PathBuilderD2D::Close() {
@@ -248,6 +251,7 @@ void PathBuilderD2D::Arc(const Point& aOrigin, Float aRadius, Float aStartAngle,
  }

  mCurrentPoint = endPoint;
  mFigureEmpty = false;
}

void PathBuilderD2D::EnsureActive(const Point& aPoint) {
@@ -269,8 +273,8 @@ already_AddRefed<Path> PathBuilderD2D::Finish() {
    return nullptr;
  }

  return MakeAndAddRef<PathD2D>(mGeometry, mFigureActive, mCurrentPoint,
                                mFillRule, mBackendType);
  return MakeAndAddRef<PathD2D>(mGeometry, mFigureActive, mFigureEmpty,
                                mCurrentPoint, mFillRule, mBackendType);
}

already_AddRefed<PathBuilder> PathD2D::CopyToBuilder(FillRule aFillRule) const {
Loading