Bug 1190210 - Part 4: Test cases. r=smaug, a=sledru
authorKaku Kuo <tkuo@mozilla.com>
Thu, 06 Aug 2015 21:24:53 +0800
changeset 288940 d3402c8dcdd4aa27b8fcbf0c1171ae7dc971e96f
parent 288939 2ba0588344a220b3f4f28583686b29e1c8ce2ca5
child 288941 4e1bb46da4397a90fd82bbcc15b7e1bf2bc578db
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, sledru
bugs1190210
milestone42.0a2
Bug 1190210 - Part 4: Test cases. r=smaug, a=sledru
dom/canvas/test/mochitest.ini
dom/canvas/test/test_imagebitmap_cropping.html
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -234,16 +234,17 @@ skip-if = os == "android" || appname == 
 support-files = captureStream_common.js
 [test_drawImageIncomplete.html]
 [test_drawImage_document_domain.html]
 [test_drawImage_edge_cases.html]
 [test_drawWindow.html]
 support-files = file_drawWindow_source.html file_drawWindow_common.js
 skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk')
 [test_imagebitmap.html]
+[test_imagebitmap_cropping.html]
 [test_imagebitmap_on_worker.html]
 [test_imagebitmap_structuredclone.html]
 [test_imagebitmap_structuredclone_iframe.html]
 [test_imagebitmap_structuredclone_window.html]
 [test_ImageData_ctor.html]
 [test_isPointInStroke.html]
 [test_mozDashOffset.html]
 [test_mozGetAsFile.html]
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_imagebitmap_cropping.html
@@ -0,0 +1,274 @@
+<!DOCTYPE HTML>
+<title>Test ImageBitmap Cropping (Bug 1190210)</title>
+<meta charset="utf-8">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body>
+<script type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/**
+ * [isPixel description]
+ * @param  {[type]}  ctx : canvas context
+ * @param  {[type]}  x   : pixel x coordinate
+ * @param  {[type]}  y   : pixel y coordinate
+ * @param  {[type]}  c   : a rgba color code
+ * @param  {[type]}  d   : error duration
+ * @return {Promise}
+ */
+function isPixel(ctx, x, y, c, d) {
+  var pos = x + "," + y;
+  var color = c[0] + "," + c[1] + "," + c[2] + "," + c[3];
+  var pixel = ctx.getImageData(x, y, 1, 1);
+  var pr = pixel.data[0],
+      pg = pixel.data[1],
+      pb = pixel.data[2],
+      pa = pixel.data[3];
+  ok(c[0]-d <= pr && pr <= c[0]+d &&
+     c[1]-d <= pg && pg <= c[1]+d &&
+     c[2]-d <= pb && pb <= c[2]+d &&
+     c[3]-d <= pa && pa <= c[3]+d,
+     "pixel "+pos+" of "+ctx.canvas.id+" is "+pr+","+pg+","+pb+","+pa+"; expected "+color+" +/- "+d);
+}
+
+//
+//  The pattern of the 320x240.ogv video.
+//  .------------------------------------------------.
+//  | 255  | 255  | 0    | 0    | 255  | 255  | 0    |
+//  | 255  | 255  | 255  | 255  | 0    | 0    | 0    |
+//  | 255  | 0    | 255  | 0    | 255  | 0    | 255  |
+//  |      |      |      |      |      |      |      |
+//  ^      ^      ^      ^      ^      ^      ^      ^
+//  0      46     92     138    184    230    276    319
+//
+//
+// TEST_BITMAPS is a collection of test cases.
+// Each object in the TEST_BITMAPS array is a test case with the following
+// properties:
+// 1) croppingArea: indicating the cropping area in format (x, y, width, height).
+// 2) testedPixels: an array of pixels that is going to be checked.
+//    Each item in the testedPixels array contains:
+//    2.1) "pixel": the coordinate of this pixel (x, y).
+//    2.2) "expectedColor": the expected color of this pixel (r, g, b, a).
+//    2.3) "tolerance": the acceptable tolerance of pixel values.
+//
+var TEST_BITMAPS = [
+  // Cropping area is exactly the same as source surface.
+  {'croppingArea': [0,    0,    320, 240],
+   'testedPixels': [{"pixel": [0,    0], "expectedColor": [255, 255, 255, 255], "tolerance": 5},
+                    {"pixel": [50,   0], "expectedColor": [255, 255, 0,   255], "tolerance": 5}]},
+  // Cropping area completely covers the source surface.
+  {'croppingArea': [-100, -100, 520, 440],
+   'testedPixels': [{"pixel": [0,    0],   "expectedColor": [0,   0,   0,   0], "tolerance": 5},
+                    {"pixel": [519,  0],   "expectedColor": [0,   0,   0,   0], "tolerance": 5},
+                    {"pixel": [0,    439], "expectedColor": [0,   0,   0,   0], "tolerance": 5},
+                    {"pixel": [519,  439], "expectedColor": [0,   0,   0,   0], "tolerance": 5},
+                    {"pixel": [100,  100], "expectedColor": [255, 255, 255, 255], "tolerance": 5},
+                    {"pixel": [150,  120], "expectedColor": [255, 255, 0,   255], "tolerance": 5},
+                    {"pixel": [200,  140], "expectedColor": [0,   255, 255, 255], "tolerance": 5}]},
+  // Cropping area partially covers the left-upper part of the source surface.
+  {'croppingArea': [-100, -100, 320, 240],
+   'testedPixels': [{"pixel": [0,    0],   "expectedColor": [0,   0,   0,   0  ], "tolerance": 5},
+                    {"pixel": [100,  100], "expectedColor": [255, 255, 255, 255], "tolerance": 5},
+                    {"pixel": [150,  100], "expectedColor": [255, 255, 0,   255], "tolerance": 5}]},
+  // Cropping area partially covers the middle-upper part of the source surface.
+  {'croppingArea': [ 100, -100, 220, 240],
+   'testedPixels': [{"pixel": [0,    0],   "expectedColor": [0,   0,   0,   0  ], "tolerance": 5},
+                    {"pixel": [0,    100], "expectedColor": [0,   255, 255, 255], "tolerance": 5},
+                    {"pixel": [150,  100], "expectedColor": [255, 0,   0,   255], "tolerance": 5}]},
+  // Cropping area partially covers the right-upper part of the source surface.
+  {'croppingArea': [ 200, -100, 320, 240],
+   'testedPixels': [{"pixel": [0,    0],   "expectedColor": [0,   0,   0,   0  ], "tolerance": 5},
+                    {"pixel": [0,    100], "expectedColor": [255, 0,   255, 255], "tolerance": 5},
+                    {"pixel": [100,  100], "expectedColor": [0,   0,   255, 255], "tolerance": 5}]},
+  // Cropping area partially covers the left-center part of the source surface.
+  {'croppingArea': [-100,  100, 320, 120],
+   'testedPixels': [{"pixel": [0,    0],   "expectedColor": [0,   0,   0,   0  ], "tolerance": 5},
+                    {"pixel": [200,  0],   "expectedColor": [0,   255, 255, 255], "tolerance": 5},
+                    {"pixel": [250,  10],  "expectedColor": [0,   255, 0,   255], "tolerance": 5}]},
+  // Cropping area partially covers the left-bottom part of the source surface.
+  {'croppingArea': [-100,  200, 320, 240],
+   'testedPixels': [{"pixel": [0,    0],   "expectedColor": [0,   0,   0,   0  ], "tolerance": 5},
+                    {"pixel": [100,  0],   "expectedColor": [0,   60,  136, 255], "tolerance": 5},
+                    {"pixel": [180,  10],  "expectedColor": [255, 255, 255, 255], "tolerance": 5}]},
+  // Cropping area partially covers the middle-bottom part of the source surface.
+  {'croppingArea': [  40,  200, 200, 100],
+   'testedPixels': [{"pixel": [0,    0],   "expectedColor": [0,   60,  136, 255], "tolerance": 5},
+                    {"pixel": [100,  20],  "expectedColor": [107, 0,   210, 255], "tolerance": 5},
+                    {"pixel": [80,   150], "expectedColor": [0,   0,   0,   0  ], "tolerance": 5}]},
+  // Cropping area partially covers the right-bottom part of the source surface.
+  {'croppingArea': [ 160,  100, 300, 300],
+   'testedPixels': [{"pixel": [0,    0],   "expectedColor": [0,   255, 0,   255], "tolerance": 5},
+                    {"pixel": [120,  20],  "expectedColor": [0,   0,   255, 255], "tolerance": 5},
+                    {"pixel": [299,  299], "expectedColor": [0,   0,   0,   0  ], "tolerance": 5}]},
+  // Cropping area is completely outside the source surface. (upper-left)
+  {'croppingArea': [-500, -500,  20,  20],
+   'testedPixels': [{"pixel": [0,    0],   "expectedColor": [0,   0,   0,   0], "tolerance": 5},
+                    {"pixel": [19,   19],  "expectedColor": [0,   0,   0,   0], "tolerance": 5}]},
+  // Cropping area is completely outside the source surface. (upper-right)
+  {'croppingArea': [ 500, -500,  20,  20],
+   'testedPixels': [{"pixel": [0,    0],   "expectedColor": [0,   0,   0,   0], "tolerance": 5},
+                    {"pixel": [19,   19],  "expectedColor": [0,   0,   0,   0], "tolerance": 5}]},
+  // Cropping area is completely outside the source surface. (bottom-left)
+  {'croppingArea': [-200,  500,  20,  20],
+   'testedPixels': [{"pixel": [0,    0],   "expectedColor": [0,   0,   0,   0], "tolerance": 5},
+                    {"pixel": [19,   19],  "expectedColor": [0,   0,   0,   0], "tolerance": 5}]},
+  // Cropping area is completely outside the source surface. (bottom-right)
+  {'croppingArea': [ 500,  200,  20,  20],
+   'testedPixels': [{"pixel": [0,    0],   "expectedColor": [0,   0,   0,   0], "tolerance": 5},
+                    {"pixel": [19,   19],  "expectedColor": [0,   0,   0,   0], "tolerance": 5}]},
+];
+
+function failed(ex) {
+  ok(false, "Promise failure: " + ex);
+}
+
+var gImage;
+var gVideo;
+var gCanvas;
+var gCtx;
+var gImageData;
+var gImageBitmap;
+var gPNGBlob;
+var gJPEGBlob;
+
+function prepareSources() {
+  gVideo = document.createElement("video");
+  gVideo.src = "http://example.com/tests/dom/canvas/test/crossorigin/video.sjs?name=tests/dom/media/test/320x240.ogv&type=video/ogg&cors=anonymous";
+  gVideo.crossOrigin = "anonymous";
+  gVideo.autoplay = "true"
+
+
+  gCanvas = document.createElement("canvas");
+  gCtx = gCanvas.getContext("2d");
+
+  var resolver;
+  var promise = new Promise(function(resolve, reject) {
+    resolver = resolve;
+  });
+
+  // Prepare video.
+  gVideo.onloadeddata = function() {
+    ok(gVideo, "[Prepare Sources] gVideo is ok.");
+
+    // Prepare canvas.
+    gCanvas.width = gVideo.videoWidth;
+    gCanvas.height = gVideo.videoHeight;
+    gCtx.drawImage(gVideo, 0, 0);
+    ok(gCanvas, "[Prepare Sources] gCanvas is ok.");
+    ok(gCtx, "[Prepare Sources] gCtx is ok.");
+
+    // Prepare image.
+    gImage = document.createElement("img");
+    gImage.src = gCanvas.toDataURL();
+    var resolverImage;
+    var promiseImage = new Promise(function(resolve, reject) {
+      resolverImage = resolve;
+    });
+    gImage.onload = function() {
+      resolverImage(true);
+    }
+
+    // Prepare ImageData.
+    gImageData = gCtx.getImageData(0, 0, gCanvas.width, gCanvas.height);
+    ok(gImageData, "[Prepare Sources] gImageData is ok.");
+
+    // Prepapre PNG Blob.
+    var promisePNGBlob = new Promise(function(resolve, reject) {
+      gCanvas.toBlob(function(blob) {
+        gPNGBlob = blob;
+        ok(gPNGBlob, "[Prepare Sources] gPNGBlob is ok.");
+        resolve(true);
+      });
+    });
+
+    // Prepare JPEG Blob.
+    var promiseJPEGBlob = new Promise(function(resolve, reject) {
+      gCanvas.toBlob(function(blob) {
+        gJPEGBlob = blob;
+        ok(gJPEGBlob, "[Prepare Sources] gJPEGBlob is ok.");
+        resolve(true);
+      }, "image/jpeg", 0.95);
+    });
+
+    // Prepare ImageBitmap.
+    var promiseImageBitmap = new Promise(function(resolve, reject) {
+      var p = createImageBitmap(gVideo);
+      p.then(function(bitmap) {
+        gImageBitmap = bitmap;
+        ok(gImageBitmap, "[Prepare Sources] gImageBitmap is ok.");
+        resolve(true);
+      });
+    });
+
+    resolver(Promise.all([
+      promiseImage,
+      promisePNGBlob,
+      promiseJPEGBlob,
+      promiseImageBitmap
+    ]))
+  }
+
+  return promise;
+}
+
+function testCropping_randomTest(source) {
+  var canvasSrouce = document.createElement("canvas");
+  var ctxSource = canvasSrouce.getContext("2d");
+
+  var p = createImageBitmap(source);
+  p.then(function(bitmap) {
+    canvasSrouce.width = bitmap.width;
+    canvasSrouce.height = bitmap.height;
+  });
+}
+
+function testCropping(source) {
+  var canvas = document.createElement("canvas");
+  var ctx = canvas.getContext("2d");
+  document.body.appendChild(canvas);
+
+  function createBitmap(def) {
+    return createImageBitmap(source, def.croppingArea[0], def.croppingArea[1], def.croppingArea[2], def.croppingArea[3])
+      .then(function (bitmap) { def.bitmap = bitmap; }, failed);
+  };
+
+  var promise = new Promise(function(resolve, reject) {
+    resolve(Promise.all(TEST_BITMAPS.map(createBitmap)))
+  });
+
+  function testPixel(testedPixel) {
+    isPixel(ctx, testedPixel.pixel[0], testedPixel.pixel[1], testedPixel.expectedColor, testedPixel.tolerance);
+  };
+
+  return promise.then(function() {
+    TEST_BITMAPS.forEach(function (testCase) {
+      if (!testCase.bitmap) { return; }
+      ctx.clearRect(0, 0, canvas.width, canvas.height);
+      canvas.width = testCase.bitmap.width;
+      canvas.height = testCase.bitmap.height;
+      ctx.drawImage(testCase.bitmap, 0, 0);
+      testCase.testedPixels.forEach(testPixel);
+    });
+  });
+}
+
+function runTests() {
+
+  prepareSources().
+    then( function() { return Promise.all([testCropping(gImage),
+                                           testCropping(gVideo),
+                                           testCropping(gCanvas),
+                                           testCropping(gCtx),
+                                           testCropping(gImageData),
+                                           testCropping(gImageBitmap),
+                                           testCropping(gPNGBlob),
+                                           testCropping(gJPEGBlob)]); }).
+    then(SimpleTest.finish, function(ev) { failed(ev); SimpleTest.finish(); });
+}
+
+addLoadEvent(runTests);
+
+</script>
+</body>