Bug 985178 - Implementation + test cases for isPointInPath and isPointInStroke. r=roc
authorRik Cabanier <cabanier@adobe.com>
Wed, 19 Mar 2014 13:42:38 -0400
changeset 174362 c53d21ff80232d861e0c745b30b93b24453c41a4
parent 174361 978ca21ae2dcabb0f1d2c22207fd3705d533f065
child 174363 90debab0343c942d6a83a7ca458fcdc10febbf4d
push id41267
push userryanvm@gmail.com
push dateWed, 19 Mar 2014 17:41:59 +0000
treeherdermozilla-inbound@90debab0343c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs985178
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 985178 - Implementation + test cases for isPointInPath and isPointInStroke. r=roc
content/canvas/src/CanvasRenderingContext2D.cpp
content/canvas/src/CanvasRenderingContext2D.h
content/canvas/test/test_canvas_path.html
dom/webidl/CanvasRenderingContext2D.webidl
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -3125,16 +3125,28 @@ CanvasRenderingContext2D::IsPointInPath(
 
   if (mPathTransformWillUpdate) {
     return mPath->ContainsPoint(Point(x, y), mPathToDS);
   }
 
   return mPath->ContainsPoint(Point(x, y), mTarget->GetTransform());
 }
 
+bool CanvasRenderingContext2D::IsPointInPath(const CanvasPath& mPath, double x, double y, const CanvasWindingRule& mWinding)
+{
+  if (!FloatValidate(x,y)) {
+    return false;
+  }
+
+  EnsureTarget();
+  RefPtr<gfx::Path> tempPath = mPath.GetPath(mWinding, mTarget);
+
+  return tempPath->ContainsPoint(Point(x, y), mTarget->GetTransform());
+}
+
 bool
 CanvasRenderingContext2D::IsPointInStroke(double x, double y)
 {
   if (!FloatValidate(x,y)) {
     return false;
   }
 
   EnsureUserSpacePath();
@@ -3153,16 +3165,38 @@ CanvasRenderingContext2D::IsPointInStrok
                               state.dashOffset);
 
   if (mPathTransformWillUpdate) {
     return mPath->StrokeContainsPoint(strokeOptions, Point(x, y), mPathToDS);
   }
   return mPath->StrokeContainsPoint(strokeOptions, Point(x, y), mTarget->GetTransform());
 }
 
+bool CanvasRenderingContext2D::IsPointInStroke(const CanvasPath& mPath, double x, double y)
+{
+  if (!FloatValidate(x,y)) {
+    return false;
+  }
+
+  EnsureTarget();
+  RefPtr<gfx::Path> tempPath = mPath.GetPath(CanvasWindingRule::Nonzero, mTarget);
+
+  const ContextState &state = CurrentState();
+
+  StrokeOptions strokeOptions(state.lineWidth,
+                              state.lineJoin,
+                              state.lineCap,
+                              state.miterLimit,
+                              state.dash.Length(),
+                              state.dash.Elements(),
+                              state.dashOffset);
+
+  return tempPath->StrokeContainsPoint(strokeOptions, Point(x, y), mTarget->GetTransform());
+}
+
 //
 // image
 //
 
 // drawImage(in HTMLImageElement image, in float dx, in float dy);
 //   -- render image from 0,0 at dx,dy top-left coords
 // drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
 //   -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -226,17 +226,19 @@ public:
   void Fill(const CanvasPath& path, const CanvasWindingRule& winding);
   void Stroke();
   void Stroke(const CanvasPath& path);
   void DrawFocusIfNeeded(mozilla::dom::Element& element);
   bool DrawCustomFocusRing(mozilla::dom::Element& element);
   void Clip(const CanvasWindingRule& winding);
   void Clip(const CanvasPath& path, const CanvasWindingRule& winding);
   bool IsPointInPath(double x, double y, const CanvasWindingRule& winding);
+  bool IsPointInPath(const CanvasPath& path, double x, double y, const CanvasWindingRule& winding);
   bool IsPointInStroke(double x, double y);
+  bool IsPointInStroke(const CanvasPath& path, double x, double y);
   void FillText(const nsAString& text, double x, double y,
                 const Optional<double>& maxWidth,
                 mozilla::ErrorResult& error);
   void StrokeText(const nsAString& text, double x, double y,
                   const Optional<double>& maxWidth,
                   mozilla::ErrorResult& error);
   TextMetrics*
     MeasureText(const nsAString& rawText, mozilla::ErrorResult& error);
--- a/content/canvas/test/test_canvas_path.html
+++ b/content/canvas/test/test_canvas_path.html
@@ -157,16 +157,169 @@ function test_large_canvas() {
   ctx.fillStyle = 'rgb(255,0,0)';
   ctx.fillRect(0, 0, 100, 100);
   ctx.fillStyle = 'rgb(0,255,0)';
   ctx.fill(path, 'evenodd');
   isPixel(ctx, 50, 50, [255, 0, 0, 255], 5);
 }
 </script>
 
+<p>Canvas test: test_isPointInPath_canvas</p>
+<canvas id="c5" class="output" width="100" height="100">+
+</canvas>
+<script type="text/javascript">
+
+function shouldThrow(ctx, s) {
+  var _ok = false;
+  try {
+    eval(s);
+  } catch(e) {
+    _ok = true;
+  }
+  ok(_ok, s);
+}
+
+function shouldBeTrue(ctx, path, s) {
+  var _ok = eval(s);
+  ok(_ok, s);
+}
+function shouldBeFalse(ctx, path, s) {
+  var _ok = !eval(s);
+  ok(_ok, s);
+}
+
+function test_isPointInPath_canvas() {
+  var c = document.getElementById("c5");
+  var ctx = c.getContext("2d");
+
+  var path = new Path2D();
+  path.rect(0, 0, 100, 100);
+  path.rect(25, 25, 50, 50);
+  shouldBeTrue(ctx, path, "ctx.isPointInPath(path, 50, 50)");
+  shouldBeFalse(ctx, path, "ctx.isPointInPath(path, NaN, 50)");
+  shouldBeFalse(ctx, path, "ctx.isPointInPath(path, 50, NaN)");
+
+  path = new Path2D();
+  path.rect(0, 0, 100, 100);
+  path.rect(25, 25, 50, 50);
+  shouldBeTrue(ctx, path, "ctx.isPointInPath(path, 50, 50, 'nonzero')");
+
+  path = new Path2D();
+  path.rect(0, 0, 100, 100);
+  path.rect(25, 25, 50, 50);
+  shouldBeFalse(ctx, path, "ctx.isPointInPath(path, 50, 50, 'evenodd')");
+
+  shouldThrow(ctx, "ctx.isPointInPath(null, 50, 50)");
+  shouldThrow(ctx, "ctx.isPointInPath(null, 50, 50, 'nonzero')");
+  shouldThrow(ctx, "ctx.isPointInPath(null, 50, 50, 'evenodd')");
+  shouldThrow(ctx, "ctx.isPointInPath(path, 50, 50)");
+  shouldThrow(ctx, "ctx.isPointInPath(path, 50, 50, 'nonzero')");
+  shouldThrow(ctx, "ctx.isPointInPath(path, 50, 50, 'evenodd')");
+
+  shouldThrow(ctx, "ctx.isPointInPath([], 50, 50)");
+  shouldThrow(ctx, "ctx.isPointInPath([], 50, 50, 'nonzero')");
+  shouldThrow(ctx, "ctx.isPointInPath([], 50, 50, 'evenodd')");
+  shouldThrow(ctx, "ctx.isPointInPath({}, 50, 50)");
+  shouldThrow(ctx, "ctx.isPointInPath({}, 50, 50, 'nonzero')");
+  shouldThrow(ctx, "ctx.isPointInPath({}, 50, 50, 'evenodd')");
+}
+</script>
+
+<p>Canvas test: test_isPointInStroke_canvas</p>
+<canvas id="c6" class="output" width="100" height="100">+
+</canvas>
+<script type="text/javascript">
+
+function test_isPointInStroke_canvas() {
+  var c = document.getElementById("c6");
+  var ctx = c.getContext("2d");
+
+  ctx.strokeStyle = '#0ff';
+
+  var path = new Path2D();
+  path.rect(20,20,100,100);
+
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,20,20)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,120,20)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,20,120)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,120,120)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,70,20)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,20,70)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,120,70)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,70,120)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,22,22)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,118,22)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,22,118)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,118,118)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,70,18)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,122,70)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,70,122)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,18,70)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,NaN,122)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,18,NaN)");
+
+  shouldThrow(ctx, "ctx.isPointInStroke(null,70,20)");
+  shouldThrow(ctx, "ctx.isPointInStroke([],20,70)");
+  shouldThrow(ctx, "ctx.isPointInStroke({},120,70)");
+
+  ctx.lineWidth = 10;
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,22,22)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,118,22)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,22,118)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,118,118)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,70,18)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,122,70)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,70,122)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,18,70)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,26,70)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,70,26)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,70,114)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,114,70)");
+
+  path = new Path2D();
+  path.moveTo(10,10);
+  path.lineTo(110,20);
+  path.lineTo(10,30);
+  ctx.lineJoin = "bevel";
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,113,20)");
+
+  ctx.miterLimit = 40.0;
+  ctx.lineJoin = "miter";
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,113,20)");
+
+  ctx.miterLimit = 2.0;
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,113,20)");
+
+  path = new Path2D();
+  path.moveTo(10,10);
+  path.lineTo(110,10);
+  ctx.lineCap = "butt";
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,112,10)");
+
+  ctx.lineCap = "round";
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,112,10)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,117,10)");
+
+  ctx.lineCap = "square";
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,112,10)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,117,10)");
+
+  ctx.lineCap = "butt";
+  ctx.setLineDash([10,10]);
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,15,10)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,25,10)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,35,10)");
+
+  ctx.lineDashOffset = 10;
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,15,10)");
+  shouldBeTrue(ctx, path, "ctx.isPointInStroke(path,25,10)");
+  shouldBeFalse(ctx, path, "ctx.isPointInStroke(path,35,10)");
+}
+</script>
+
 <script>
 
 function runTests() {
  try {
   test_drawClipPath_canvas();
  } catch(e) {
   throw e;
   ok(false, "unexpected exception thrown in: test_drawClipPath_canvas");
@@ -184,16 +337,27 @@ function runTests() {
   ok(false, "unexpected exception thrown in: test_drawStrokePath_canvas");
  }
  try {
   test_large_canvas();
  } catch(e) {
   throw e;
   ok(false, "unexpected exception thrown in: test_large_canvas");
  }
-
+ try {
+  test_isPointInPath_canvas();
+ } catch(e) {
+  throw e;
+  ok(false, "unexpected exception thrown in: test_isPointInPath_canvas");
+ }
+ try {
+  test_isPointInStroke_canvas();
+ } catch(e) {
+  throw e;
+  ok(false, "unexpected exception thrown in: test_isPointInStroke_canvas");
+ }
  SpecialPowers.setBoolPref("canvas.path.enabled", false);
  SimpleTest.finish();
 }
 
 addLoadEvent(runTests);
 
 </script>
--- a/dom/webidl/CanvasRenderingContext2D.webidl
+++ b/dom/webidl/CanvasRenderingContext2D.webidl
@@ -92,18 +92,19 @@ interface CanvasRenderingContext2D {
   [Pref="canvas.customfocusring.enabled"] boolean drawCustomFocusRing(Element element);
 // NOT IMPLEMENTED  boolean drawCustomFocusRing(Path path, HTMLElement element);
 // NOT IMPLEMENTED  void scrollPathIntoView();
 // NOT IMPLEMENTED  void scrollPathIntoView(Path path);
   void clip(optional CanvasWindingRule winding = "nonzero");
   void clip(Path2D path, optional CanvasWindingRule winding = "nonzero");
 // NOT IMPLEMENTED  void resetClip();
   boolean isPointInPath(unrestricted double x, unrestricted double y, optional CanvasWindingRule winding = "nonzero");
-// NOT IMPLEMENTED  boolean isPointInPath(Path path, unrestricted double x, unrestricted double y);
+  boolean isPointInPath(Path2D path, unrestricted double x, unrestricted double y, optional CanvasWindingRule winding = "nonzero");
   boolean isPointInStroke(double x, double y);
+  boolean isPointInStroke(Path2D path, unrestricted double x, unrestricted double y);
 
   // text (see also the CanvasDrawingStyles interface)
   [Throws, LenientFloat]
   void fillText(DOMString text, double x, double y, optional double maxWidth);
   [Throws, LenientFloat]
   void strokeText(DOMString text, double x, double y, optional double maxWidth);
   [NewObject, Throws]
   TextMetrics measureText(DOMString text);