Bug 768067 - Implement canvasRenderingContext2D.get/setLineDash. r=roc
authorRik Cabanier <cabanier@adobe.com>
Wed, 16 Oct 2013 08:27:59 -0400
changeset 164752 b16182c733bb21bf986aab75afac9dd1af737667
parent 164751 205345b505b25279c517610b15a5f51c675e5c4e
child 164753 f8e64e91e443b88c37eb632014ce17843304f7dd
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs768067
milestone27.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 768067 - Implement canvasRenderingContext2D.get/setLineDash. r=roc
content/canvas/src/CanvasRenderingContext2D.cpp
content/canvas/src/CanvasRenderingContext2D.h
content/canvas/test/test_canvas.html
dom/webidl/CanvasRenderingContext2D.webidl
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -2850,16 +2850,51 @@ void
 CanvasRenderingContext2D::SetMozDashOffset(double mozDashOffset)
 {
   ContextState& state = CurrentState();
   if (!state.dash.IsEmpty()) {
     state.dashOffset = mozDashOffset;
   }
 }
 
+void
+CanvasRenderingContext2D::SetLineDash(const mozilla::dom::AutoSequence<double>& mSegments) {
+  FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
+  dash.Clear();
+
+  for(mozilla::dom::AutoSequence<double>::index_type x = 0; x < mSegments.Length(); x++) {
+    dash.AppendElement(mSegments[x]);
+  }
+  if(mSegments.Length()%2) { // If the number of elements is odd, concatenate again
+    for(mozilla::dom::AutoSequence<double>::index_type x = 0; x < mSegments.Length(); x++) {
+      dash.AppendElement(mSegments[x]);
+    }
+  }
+}
+
+void
+CanvasRenderingContext2D::GetLineDash(nsTArray<double>& mSegments) const {
+  const FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
+  mSegments.Clear();
+
+  for(FallibleTArray<mozilla::gfx::Float>::index_type x = 0; x < dash.Length(); x++) {
+    mSegments.AppendElement(dash[x]);
+  }
+}
+
+void
+CanvasRenderingContext2D::SetLineDashOffset(double mOffset) {
+  CurrentState().dashOffset = mOffset;
+}
+
+double
+CanvasRenderingContext2D::LineDashOffset() const {
+  return CurrentState().dashOffset;
+}
+
 bool
 CanvasRenderingContext2D::IsPointInPath(double x, double y, const CanvasWindingRule& winding)
 {
   if (!FloatValidate(x,y)) {
     return false;
   }
 
   EnsureUserSpacePath(winding);
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -328,16 +328,22 @@ public:
                                      JS::Handle<JSObject*> currentTransform,
                                      mozilla::ErrorResult& error);
   void GetFillRule(nsAString& fillRule);
   void SetFillRule(const nsAString& fillRule);
   JS::Value GetMozDash(JSContext* cx, mozilla::ErrorResult& error);
   void SetMozDash(JSContext* cx, const JS::Value& mozDash,
                   mozilla::ErrorResult& error);
 
+  void SetLineDash(const mozilla::dom::AutoSequence<double>& mSegments);
+  void GetLineDash(nsTArray<double>& mSegments) const;
+
+  void SetLineDashOffset(double mOffset);
+  double LineDashOffset() const;
+
   double MozDashOffset()
   {
     return CurrentState().dashOffset;
   }
   void SetMozDashOffset(double mozDashOffset);
 
   void GetMozTextStyle(nsAString& mozTextStyle)
   {
@@ -839,16 +845,20 @@ protected:
   };
 
   nsAutoTArray<ContextState, 3> mStyleStack;
 
   inline ContextState& CurrentState() {
     return mStyleStack[mStyleStack.Length() - 1];
   }
 
+  inline const ContextState& CurrentState() const {
+    return mStyleStack[mStyleStack.Length() - 1];
+  }
+
   friend class CanvasGeneralPattern;
   friend class AdjustedTarget;
 
   // other helpers
   void GetAppUnitsValues(int32_t *perDevPixel, int32_t *perCSSPixel)
   {
     // If we don't have a canvas element, we just return something generic.
     int32_t devPixel = 60;
--- a/content/canvas/test/test_canvas.html
+++ b/content/canvas/test/test_canvas.html
@@ -21483,16 +21483,91 @@ function test_getImageData_after_zero_ca
     var same = false;
     ok(imgdata.data.length === oldimgdata.data.length, "not the same length");
     for (var i = 0; i < imgdata.data.length; ++i)
         same = imgdata.data[i] === oldimgdata.data[i];
     ok(same, "changing dimensions broke canvas");
 }
 </script>
 
+<p>Canvas test: zero_dimensions_image_data</p>
+<canvas id="c687" width="150" height="50"></canvas>
+<script type="text/javascript">
+
+function test_linedash() {
+  var c = document.getElementById("c687");
+  var ctx = c.getContext("2d");
+  ok(ctx.lineDashOffset==0, "initial dash offset is not 0");
+
+  ctx.setLineDash([15, 10]);
+  ctx.lineDashOffset = 5;
+  ctx.strokeRect (10,10,100,100);
+
+  var lineDash = ctx.getLineDash();
+  ok(lineDash[0]==15&&lineDash[1]==10, "dash pattern [15, 10] is wrong");
+  ok(ctx.lineDashOffset==5, "dash offset is wrong");
+
+  ctx.setLineDash([5, 10, 15]);
+  ctx.strokeRect(20, 20, 120, 120);
+  lineDash = ctx.getLineDash();
+  ok(lineDash[0]==5
+    && lineDash[1]==10
+    && lineDash[2]==15
+    && lineDash[3]==5
+    && lineDash[4]==10
+    && lineDash[5]==15, "dash pattern [5, 10, 15] is wrong");
+
+  ctx.setLineDash(["1", 2]);
+  lineDash = ctx.getLineDash();
+  ok(lineDash[0] == 1 && lineDash[1] == 2, "dash pattern ['1', 2] is wrong");
+
+  ctx.clearRect(0, 0, 700, 700);
+  ok(ctx.lineDashOffset==5, "dash offset is wrong");
+
+  ctx.setLineDash([20, 10]);
+  ctx.lineDashOffset = 0;
+  ctx.lineWidth = 4; // To make the test immune to plaform anti-aliasing discrepancies
+  ctx.strokeStyle = '#00FF00';
+  ctx.strokeRect(10.5, 10.5, 30, 30);
+
+  isPixel(ctx, 25, 10, 0, 255, 0, 255, 0);
+  isPixel(ctx, 35, 10, 0, 0, 0, 0, 0);
+  isPixel(ctx, 40, 25, 0, 255, 0, 255, 0);
+  isPixel(ctx, 40, 35, 0, 0, 0, 0, 0);
+  isPixel(ctx, 25, 40, 0, 255, 0, 255, 0);
+  isPixel(ctx, 15, 40, 0, 0, 0, 0, 0);
+  isPixel(ctx, 10, 25, 0, 255, 0, 255, 0);
+  isPixel(ctx, 10, 15, 0, 0, 0, 0, 0);
+
+  // Verify that lineDashOffset works as expected
+  ctx.lineDashOffset = 20;
+  ctx.strokeRect(50.5, 10.5, 30, 30);
+  isPixel(ctx, 55, 10, 0, 0, 0, 0, 0);
+  isPixel(ctx, 65, 10, 0, 255, 0, 255, 0);
+  isPixel(ctx, 80, 15, 0, 0, 0, 0, 0);
+  isPixel(ctx, 80, 25, 0, 255, 0, 255, 0);
+  isPixel(ctx, 75, 40, 0, 0, 0, 0, 0);
+  isPixel(ctx, 65, 40, 0, 255, 0, 255, 0);
+  isPixel(ctx, 50, 35, 0, 0, 0, 0, 0);
+  isPixel(ctx, 50, 25, 0, 255, 0, 255, 0);
+
+  // Verify negative lineDashOffset
+  ctx.lineDashOffset = -10;
+  ctx.strokeRect(90.5, 10.5, 30, 30);
+  isPixel(ctx, 95, 10, 0, 0, 0, 0, 0);
+  isPixel(ctx, 105, 10, 0, 255, 0, 255, 0);
+  isPixel(ctx, 120, 15, 0, 0, 0, 0, 0);
+  isPixel(ctx, 120, 25, 0, 255, 0, 255, 0);
+  isPixel(ctx, 115, 40, 0, 0, 0, 0, 0);
+  isPixel(ctx, 105, 40, 0, 255, 0, 255, 0);
+  isPixel(ctx, 90, 35, 0, 0, 0, 0, 0);
+  isPixel(ctx, 90, 25, 0, 255, 0, 255, 0);
+}
+</script>
+
 <script>
 
 function asyncTestsDone() {
 	if (isDone_test_2d_drawImage_animated_apng &&
 		isDone_test_2d_drawImage_animated_gif) {
 		SimpleTest.finish();
 	} else {
 		setTimeout(asyncTestsDone, 500);
@@ -24774,16 +24849,22 @@ function runTests() {
  }
  try {
   test_getImageData_after_zero_canvas();
  } catch(e) {
   throw e;
   ok(false, "unexpected exception thrown in: test_getImageData_after_zero_canvas");
  }
  try {
+  test_linedash();
+ } catch(e) {
+  throw e;
+  ok(false, "unexpected exception thrown in: test_linedash");
+ }
+ try {
   // run this test last since it replaces the getContext method
   test_type_replace();
  } catch (e) {
   ok(false, "unexpected exception thrown in: test_type_replace");
  }
  
  //run the asynchronous tests
  try {
--- a/dom/webidl/CanvasRenderingContext2D.webidl
+++ b/dom/webidl/CanvasRenderingContext2D.webidl
@@ -224,19 +224,19 @@ interface CanvasDrawingStyles {
            attribute double lineWidth; // (default 1)
            attribute DOMString lineCap; // "butt", "round", "square" (default "butt")
            [GetterThrows]
            attribute DOMString lineJoin; // "round", "bevel", "miter" (default "miter")
            [LenientFloat]
            attribute double miterLimit; // (default 10)
 
   // dashed lines
-// NOT IMPLEMENTED    [LenientFloat] void setLineDash(sequence<double> segments); // default empty
-// NOT IMPLEMENTED    sequence<double> getLineDash();
-// NOT IMPLEMENTED             [LenientFloat] attribute double lineDashOffset;
+    [LenientFloat] void setLineDash(sequence<double> segments); // default empty
+    sequence<double> getLineDash();
+    [LenientFloat] attribute double lineDashOffset;
 
   // text
            [SetterThrows]
            attribute DOMString font; // (default 10px sans-serif)
            attribute DOMString textAlign; // "start", "end", "left", "right", "center" (default: "start")
            attribute DOMString textBaseline; // "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" (default: "alphabetic")
 };