Title: [223925] trunk
Revision
223925
Author
d...@apple.com
Date
2017-10-24 14:46:41 -0700 (Tue, 24 Oct 2017)

Log Message

Implement resizing options for ImageBitmap rendering
https://bugs.webkit.org/show_bug.cgi?id=178687
<rdar://problem/35135417>

Reviewed by Sam Weinig.

Source/WebCore:

Implement many of the sizing options for createImageBitmap.

Test: http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing.html

* html/ImageBitmap.cpp:
(WebCore::croppedSourceRectangleWithFormatting):

    Implement the first bit of the algorithm from
    the HTML specification.

(WebCore::outputSizeForSourceRectangle):

    ... and then the second bit. Leave the rest in
    comments for now.

(WebCore::interpolationQualityForResizeQuality):

    Helper function to map ImageBitmapOptions to
    GraphicsContext.

(WebCore::ImageBitmap::createPromise):

    Implement more of the sizing algorithm.

* html/ImageBitmap.h: Origin tainting is defined to be false by default.

LayoutTests:

More ImageBitmap tests, this time focusing on sizing.

* http/wpt/2dcontext/imagebitmap/common.js:
(createCanvasOfSize):
(create9x9CanvasWith2dContext):
(create18x18CanvasWith2dContext):

    Helper functions.

* http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing-expected.txt: Added.
* http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing.html: Added.
* http/wpt/2dcontext/imagebitmap/drawImage-ImageBitmap.html:
* http/wpt/2dcontext/imagebitmap/resources/target-blue-dot-no-intrinsic.svg: Added.
* http/wpt/2dcontext/imagebitmap/resources/target-blue-dot.png: Renamed from LayoutTests/http/wpt/2dcontext/imagebitmap/target-blue-dot.png.
* http/wpt/2dcontext/imagebitmap/resources/target-blue-dot.svg: Added.

Modified Paths

Added Paths

Removed Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (223924 => 223925)


--- trunk/LayoutTests/ChangeLog	2017-10-24 21:35:30 UTC (rev 223924)
+++ trunk/LayoutTests/ChangeLog	2017-10-24 21:46:41 UTC (rev 223925)
@@ -1,3 +1,27 @@
+2017-10-24  Dean Jackson  <d...@apple.com>
+
+        Implement resizing options for ImageBitmap rendering
+        https://bugs.webkit.org/show_bug.cgi?id=178687
+        <rdar://problem/35135417>
+
+        Reviewed by Sam Weinig.
+
+        More ImageBitmap tests, this time focusing on sizing.
+
+        * http/wpt/2dcontext/imagebitmap/common.js:
+        (createCanvasOfSize):
+        (create9x9CanvasWith2dContext):
+        (create18x18CanvasWith2dContext):
+
+            Helper functions.
+
+        * http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing-expected.txt: Added.
+        * http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing.html: Added.
+        * http/wpt/2dcontext/imagebitmap/drawImage-ImageBitmap.html:
+        * http/wpt/2dcontext/imagebitmap/resources/target-blue-dot-no-intrinsic.svg: Added.
+        * http/wpt/2dcontext/imagebitmap/resources/target-blue-dot.png: Renamed from LayoutTests/http/wpt/2dcontext/imagebitmap/target-blue-dot.png.
+        * http/wpt/2dcontext/imagebitmap/resources/target-blue-dot.svg: Added.
+
 2017-10-24  Chris Dumez  <cdu...@apple.com>
 
         Add initial implementation for serviceWorker.postMessage()

Modified: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/common.js (223924 => 223925)


--- trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/common.js	2017-10-24 21:35:30 UTC (rev 223924)
+++ trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/common.js	2017-10-24 21:46:41 UTC (rev 223925)
@@ -57,10 +57,22 @@
             imgData.data[i * width * 4 + j * 4 + 2] = 255;
 }
 
+function createCanvasOfSize(width, height)
+{
+    const c = document.createElement("canvas");
+    c.width = width;
+    c.height = height;
+    return c;
+}
+
 function create9x9CanvasWith2dContext()
 {
-    let c = document.createElement("canvas");
-    c.width = 9;
-    c.height = 9;
+    const c = createCanvasOfSize(9, 9);
     return [c, c.getContext("2d")];
 }
+
+function create18x18CanvasWith2dContext()
+{
+    const c = createCanvasOfSize(18, 18);
+    return [c, c.getContext("2d")];
+}

Added: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing-expected.txt (0 => 223925)


--- trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing-expected.txt	2017-10-24 21:46:41 UTC (rev 223925)
@@ -0,0 +1,11 @@
+
+PASS createImageBitmap rejects with TypeError if resizeWidth is negative 
+PASS createImageBitmap rejects with TypeError if resizeHeight is negative 
+PASS createImageBitmap with cropped source 
+PASS createImageBitmap with a cropping rectangle that is bigger than the source 
+PASS createImageBitmap with resizeWidth and resizeHeight 
+PASS createImageBitmap with only resizeHeight should calculate resizeWidth 
+PASS createImageBitmap with only resizeWidth should calculate resizeHeight 
+PASS createImageBitmap with an SVG uses intrinsic size 
+PASS createImageBitmap with an SVG that has no intrinsic size uses resize parameters 
+
Property changes on: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing-expected.txt
___________________________________________________________________

Added: svn:eol-style

+native \ No newline at end of property

Added: svn:keywords

+Date Revision \ No newline at end of property

Added: svn:mime-type

+text/plain \ No newline at end of property

Added: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing.html (0 => 223925)


--- trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing.html	2017-10-24 21:46:41 UTC (rev 223925)
@@ -0,0 +1,189 @@
+<!DOCTYPE html>
+<html>
+<title>createImageBitmap with sizing test</title>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+<link rel="stylesheet" href=""
+<body>
+<script>
+(function() {
+
+    // The ImageBitmap part of the HTML specification says this should be an InvalidStateError, but
+    // the IDL requires an unsigned value, so the TypeError will hit first.
+    promise_test(function(t) {
+        return promise_rejects(t, new TypeError, createImageBitmap(new Image(), 0, 0, 10, 10, { resizeWidth: -10, resizeHeight: 10 }));
+    }, "createImageBitmap rejects with TypeError if resizeWidth is negative");
+
+    promise_test(function(t) {
+        return promise_rejects(t, new TypeError, createImageBitmap(new Image(), 0, 0, 10, 10, { resizeWidth: 10, resizeHeight: -10 }));
+    }, "createImageBitmap rejects with TypeError if resizeHeight is negative");
+
+    promise_test(function() {
+        return new Promise(function(resolve, reject) {
+            let img = new Image();
+            img._onload_ = function() { resolve(img); };
+            img.src = ""
+        }).then(function(img) {
+            // Crop the 9x9 image into 5x5.
+            return createImageBitmap(img, 0, 0, 5, 5);
+        }).then(function(imageBitmap) {
+            assert_equals(imageBitmap.width, 5, "ImageBitmap width should be 5");
+            assert_equals(imageBitmap.height, 5, "ImageBitmap height should be 5");
+
+            let [canvas, ctx] = create9x9CanvasWith2dContext();
+            ctx.drawImage(imageBitmap, 0, 0);
+            _assertPixel(canvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255");
+            _assertPixel(canvas, 4,3, 0,255,0,255, "4,3", "0,255,0,255");
+            _assertPixel(canvas, 3,4, 0,255,0,255, "3,4", "0,255,0,255");
+            _assertPixel(canvas, 4,4, 0,0,255,255, "4,4", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 5,4, 0,0,0,0, "5,4", "0,0,0,0"); // Outside source rectangle so transparent.
+            _assertPixel(canvas, 4,5, 0,0,0,0, "4,5", "0,0,0,0"); // Outside source rectangle so transparent.
+        });
+    }, "createImageBitmap with cropped source");
+
+    promise_test(function() {
+        return new Promise(function(resolve, reject) {
+            let img = new Image();
+            img._onload_ = function() { resolve(img); };
+            img.src = ""
+        }).then(function(img) {
+            // Source is only 9x9, so the 100x100 will be clamped.
+            return createImageBitmap(img, 0, 0, 100, 100);
+        }).then(function(imageBitmap) {
+            assert_equals(imageBitmap.width, 9, "ImageBitmap width should be 9");
+            assert_equals(imageBitmap.height, 9, "ImageBitmap height should be 9");
+
+            let [canvas, ctx] = create9x9CanvasWith2dContext();
+            ctx.drawImage(imageBitmap, 0, 0);
+            _assertPixel(canvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255");
+            _assertPixel(canvas, 4,3, 0,255,0,255, "4,3", "0,255,0,255");
+            _assertPixel(canvas, 3,4, 0,255,0,255, "3,4", "0,255,0,255");
+            _assertPixel(canvas, 4,4, 0,0,255,255, "4,4", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 5,4, 0,255,0,255, "5,4", "0,255,0,255");
+            _assertPixel(canvas, 4,5, 0,255,0,255, "4,5", "0,255,0,255");
+        });
+    }, "createImageBitmap with a cropping rectangle that is bigger than the source");
+
+    promise_test(function() {
+        return new Promise(function(resolve, reject) {
+            let img = new Image();
+            img._onload_ = function() { resolve(img); };
+            img.src = ""
+        }).then(function(img) {
+            // Resize the 9x9 into 18x18, but without interpolation so that
+            // we can still do accurate pixel tests.
+            return createImageBitmap(img, 0, 0, 9, 9, { resizeWidth: 18, resizeHeight: 18, resizeQuality: "pixelated" });
+        }).then(function(imageBitmap) {
+            assert_equals(imageBitmap.width, 18, "ImageBitmap width should be 18");
+            assert_equals(imageBitmap.height, 18, "ImageBitmap height should be 18");
+
+            let [canvas, ctx] = create18x18CanvasWith2dContext();
+            ctx.drawImage(imageBitmap, 0, 0);
+            // All the target points are now 2x what they would be normally.
+            _assertPixel(canvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255");
+            _assertPixel(canvas, 8,6, 0,255,0,255, "8,6", "0,255,0,255");
+            _assertPixel(canvas, 6,8, 0,255,0,255, "6,8", "0,255,0,255");
+            _assertPixel(canvas, 8,8, 0,0,255,255, "8,8", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 9,9, 0,0,255,255, "9,9", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 10,8, 0,255,0,255, "10,8", "0,255,0,255");
+            _assertPixel(canvas, 8,10, 0,255,0,255, "8,10", "0,255,0,255");
+
+        });
+    }, "createImageBitmap with resizeWidth and resizeHeight");
+
+    promise_test(function() {
+        return new Promise(function(resolve, reject) {
+            let img = new Image();
+            img._onload_ = function() { resolve(img); };
+            img.src = ""
+        }).then(function(img) {
+            // Only provide resizeHeight.
+            return createImageBitmap(img, 0, 0, 9, 9, { resizeHeight: 18, resizeQuality: "pixelated" });
+        }).then(function(imageBitmap) {
+            assert_equals(imageBitmap.width, 18, "ImageBitmap width should be 18");
+            assert_equals(imageBitmap.height, 18, "ImageBitmap height should be 18");
+
+            let [canvas, ctx] = create18x18CanvasWith2dContext();
+            ctx.drawImage(imageBitmap, 0, 0);
+            // All the target points are now 2x what they would be normally.
+            _assertPixel(canvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255");
+            _assertPixel(canvas, 8,6, 0,255,0,255, "8,6", "0,255,0,255");
+            _assertPixel(canvas, 6,8, 0,255,0,255, "6,8", "0,255,0,255");
+            _assertPixel(canvas, 8,8, 0,0,255,255, "8,8", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 9,9, 0,0,255,255, "9,9", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 10,8, 0,255,0,255, "10,8", "0,255,0,255");
+            _assertPixel(canvas, 8,10, 0,255,0,255, "8,10", "0,255,0,255");
+
+        });
+    }, "createImageBitmap with only resizeHeight should calculate resizeWidth");
+
+    promise_test(function() {
+        return new Promise(function(resolve, reject) {
+            let img = new Image();
+            img._onload_ = function() { resolve(img); };
+            img.src = ""
+        }).then(function(img) {
+            // Only provide resizeWidth.
+            return createImageBitmap(img, 0, 0, 9, 9, { resizeWidth: 18, resizeQuality: "pixelated" });
+        }).then(function(imageBitmap) {
+            assert_equals(imageBitmap.width, 18, "ImageBitmap width should be 18");
+            assert_equals(imageBitmap.height, 18, "ImageBitmap height should be 18");
+
+            let [canvas, ctx] = create18x18CanvasWith2dContext();
+            ctx.drawImage(imageBitmap, 0, 0);
+            // All the target points are now 2x what they would be normally.
+            _assertPixel(canvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255");
+            _assertPixel(canvas, 8,6, 0,255,0,255, "8,6", "0,255,0,255");
+            _assertPixel(canvas, 6,8, 0,255,0,255, "6,8", "0,255,0,255");
+            _assertPixel(canvas, 8,8, 0,0,255,255, "8,8", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 9,9, 0,0,255,255, "9,9", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 10,8, 0,255,0,255, "10,8", "0,255,0,255");
+            _assertPixel(canvas, 8,10, 0,255,0,255, "8,10", "0,255,0,255");
+
+        });
+    }, "createImageBitmap with only resizeWidth should calculate resizeHeight");
+
+    promise_test(function() {
+        return new Promise(function(resolve, reject) {
+            let img = new Image();
+            img._onload_ = function() { resolve(img); };
+            img.src = ""
+        }).then(function(img) {
+            return createImageBitmap(img);
+        }).then(function(imageBitmap) {
+            let [canvas, ctx] = create9x9CanvasWith2dContext();
+            ctx.drawImage(imageBitmap, 0, 0);
+            _assertPixel(canvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255");
+            _assertPixel(canvas, 4,3, 0,255,0,255, "4,3", "0,255,0,255");
+            _assertPixel(canvas, 3,4, 0,255,0,255, "3,4", "0,255,0,255");
+            _assertPixel(canvas, 4,4, 0,0,255,255, "4,4", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 5,4, 0,255,0,255, "5,4", "0,255,0,255");
+            _assertPixel(canvas, 4,5, 0,255,0,255, "4,5", "0,255,0,255");
+        });
+    }, "createImageBitmap with an SVG uses intrinsic size");
+
+    promise_test(function() {
+        return new Promise(function(resolve, reject) {
+            let img = new Image();
+            img._onload_ = function() { resolve(img); };
+            img.src = ""
+        }).then(function(img) {
+            return createImageBitmap(img, { resizeWidth: 9, resizeHeight: 9 });
+        }).then(function(imageBitmap) {
+            let [canvas, ctx] = create9x9CanvasWith2dContext();
+            ctx.drawImage(imageBitmap, 0, 0);
+            _assertPixel(canvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255");
+            _assertPixel(canvas, 4,3, 0,255,0,255, "4,3", "0,255,0,255");
+            _assertPixel(canvas, 3,4, 0,255,0,255, "3,4", "0,255,0,255");
+            _assertPixel(canvas, 4,4, 0,0,255,255, "4,4", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 5,4, 0,255,0,255, "5,4", "0,255,0,255");
+            _assertPixel(canvas, 4,5, 0,255,0,255, "4,5", "0,255,0,255");
+        });
+    }, "createImageBitmap with an SVG that has no intrinsic size uses resize parameters");
+
+})();
+</script>
+</body>
+</html>
Property changes on: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing.html
___________________________________________________________________

Added: svn:eol-style

+native \ No newline at end of property

Added: svn:keywords

+Date Revision \ No newline at end of property

Added: svn:mime-type

+text/html \ No newline at end of property

Modified: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/createImageBitmap.html (223924 => 223925)


--- trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/createImageBitmap.html	2017-10-24 21:35:30 UTC (rev 223924)
+++ trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/createImageBitmap.html	2017-10-24 21:46:41 UTC (rev 223925)
@@ -30,7 +30,7 @@
 
     promise_test(function() {
         return new Promise(function(resolve, reject) {
-            var img = new Image();
+            let img = new Image();
             img._onload_ = function() { resolve(img); };
             img.src = ""
         }).then(function(img) {
@@ -43,7 +43,7 @@
 
     promise_test(function() {
         return new Promise(function(resolve, reject) {
-            var img = new Image();
+            let img = new Image();
             img._onload_ = function() { resolve(img); };
             img.src = ""
         }).then(function(img) {

Modified: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/drawImage-ImageBitmap.html (223924 => 223925)


--- trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/drawImage-ImageBitmap.html	2017-10-24 21:35:30 UTC (rev 223924)
+++ trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/drawImage-ImageBitmap.html	2017-10-24 21:46:41 UTC (rev 223925)
@@ -8,7 +8,7 @@
 <link rel="stylesheet" href=""
 <body>
 <script>
-// The target-blue-dot.png image is a 9x9 bitmap with a #00ff00 background
+// The resources/target-blue-dot.png image is a 9x9 bitmap with a #00ff00 background
 // and a single #0000ff pixel right in the middle (at 4,4).
 
 (function() {
@@ -16,7 +16,7 @@
         return new Promise(function(resolve, reject) {
             let img = new Image();
             img._onload_ = function() { resolve(img); };
-            img.src = ""
+            img.src = ""
         }).then(function(img) {
             return createImageBitmap(img);
         }).then(function(imageBitmap) {
@@ -23,11 +23,11 @@
             let [canvas, ctx] = create9x9CanvasWith2dContext();
             ctx.drawImage(imageBitmap, 0, 0);
             _assertPixel(canvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 4,3, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 3,4, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 4,4, 0,0,255,255, "5,5", "0,0,255,255"); // Target blue dot.
-            _assertPixel(canvas, 5,4, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 4,5, 0,255,0,255, "0,0", "0,255,0,255");
+            _assertPixel(canvas, 4,3, 0,255,0,255, "4,3", "0,255,0,255");
+            _assertPixel(canvas, 3,4, 0,255,0,255, "3,4", "0,255,0,255");
+            _assertPixel(canvas, 4,4, 0,0,255,255, "4,4", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 5,4, 0,255,0,255, "5,4", "0,255,0,255");
+            _assertPixel(canvas, 4,5, 0,255,0,255, "4,5", "0,255,0,255");
         });
     }, "drawImage of ImageBitmap");
 
@@ -35,7 +35,7 @@
         return new Promise(function(resolve, reject) {
             let img = new Image();
             img._onload_ = function() { resolve(img); };
-            img.src = ""
+            img.src = ""
         }).then(function(img) {
             return createImageBitmap(img);
         }).then(function(imageBitmap) {
@@ -42,11 +42,11 @@
             let [canvas, ctx] = create9x9CanvasWith2dContext();
             ctx.drawImage(imageBitmap, 0, 0, 5, 5, 0, 0, 5, 5);
             _assertPixel(canvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 4,3, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 3,4, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 4,4, 0,0,255,255, "5,5", "0,0,255,255"); // Target blue dot.
-            _assertPixel(canvas, 5,4, 0,0,0,0, "0,0", "0,0,0,0"); // Didn't draw into these parts.
-            _assertPixel(canvas, 4,5, 0,0,0,0, "0,0", "0,0,0,0");
+            _assertPixel(canvas, 4,3, 0,255,0,255, "4,3", "0,255,0,255");
+            _assertPixel(canvas, 3,4, 0,255,0,255, "3,4", "0,255,0,255");
+            _assertPixel(canvas, 4,4, 0,0,255,255, "4,4", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 5,4, 0,0,0,0, "5,4", "0,0,0,0"); // Didn't draw into these parts.
+            _assertPixel(canvas, 4,5, 0,0,0,0, "4,5", "0,0,0,0");
         });
     }, "drawImage of ImageBitmap with cropping");
 
@@ -54,7 +54,7 @@
         return new Promise(function(resolve, reject) {
             let img = new Image();
             img._onload_ = function() { resolve(img); };
-            img.src = ""
+            img.src = ""
         }).then(function(img) {
             return createImageBitmap(img);
         }).then(function(imageBitmap) {
@@ -61,12 +61,12 @@
             let [canvas, ctx] = create9x9CanvasWith2dContext();
             ctx.drawImage(imageBitmap, 2, 2, 5, 5, 0, 0, 5, 5);
             _assertPixel(canvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 2,1, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 1,2, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 2,2, 0,0,255,255, "5,5", "0,0,255,255");
-            _assertPixel(canvas, 3,2, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 2,3, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 8,8, 0,0,0,0, "0,0", "0,0,0,0");
+            _assertPixel(canvas, 2,1, 0,255,0,255, "2,1", "0,255,0,255");
+            _assertPixel(canvas, 1,2, 0,255,0,255, "1,2", "0,255,0,255");
+            _assertPixel(canvas, 2,2, 0,0,255,255, "2,2", "0,0,255,255");
+            _assertPixel(canvas, 3,2, 0,255,0,255, "3,2", "0,255,0,255");
+            _assertPixel(canvas, 2,3, 0,255,0,255, "2,3", "0,255,0,255");
+            _assertPixel(canvas, 8,8, 0,0,0,0, "8,8", "0,0,0,0");
         });
     }, "drawImage of ImageBitmap with cropping and different origin");
 
@@ -74,7 +74,7 @@
         return new Promise(function(resolve, reject) {
             let img = new Image();
             img._onload_ = function() { resolve(img); };
-            img.src = ""
+            img.src = ""
         }).then(function(img) {
             return createImageBitmap(img);
         }).then(function(imageBitmap) {
@@ -81,12 +81,12 @@
             let [canvas, ctx] = create9x9CanvasWith2dContext();
             ctx.drawImage(imageBitmap, 2, 2, 5, 5, 2, 2, 5, 5);
             _assertPixel(canvas, 0,0, 0,0,0,0, "0,0", "0,0,0,0"); // Didn't draw here.
-            _assertPixel(canvas, 4,3, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 3,4, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 4,4, 0,0,255,255, "5,5", "0,0,255,255"); // Target blue dot.
-            _assertPixel(canvas, 5,4, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 4,5, 0,255,0,255, "0,0", "0,255,0,255");
-            _assertPixel(canvas, 8,8, 0,0,0,0, "0,0", "0,0,0,0");
+            _assertPixel(canvas, 4,3, 0,255,0,255, "4,3", "0,255,0,255");
+            _assertPixel(canvas, 3,4, 0,255,0,255, "3,4", "0,255,0,255");
+            _assertPixel(canvas, 4,4, 0,0,255,255, "4,4", "0,0,255,255"); // Target blue dot.
+            _assertPixel(canvas, 5,4, 0,255,0,255, "5,4", "0,255,0,255");
+            _assertPixel(canvas, 4,5, 0,255,0,255, "4,5", "0,255,0,255");
+            _assertPixel(canvas, 8,8, 0,0,0,0, "8,8", "0,0,0,0");
         });
     }, "drawImage of ImageBitmap with cropping and different non-zero origin");
 
@@ -97,13 +97,13 @@
         return new Promise(function(resolve, reject) {
             let img = new Image();
             img._onload_ = function() { resolve(img); };
-            img.src = ""
+            img.src = ""
         }).then(function(img) {
             return createImageBitmap(img);
         }).then(function(imageBitmap) {
             imageBitmap.close();
             let [canvas, ctx] = create9x9CanvasWith2dContext();
-            assert_throws('InvalidStateError', function () {
+            assert_throws("InvalidStateError", function () {
                 ctx.drawImage(imageBitmap, 0, 0);
             }, "A closed ImageBitmap can't be drawn.");
         });
@@ -113,12 +113,12 @@
         return new Promise(function(resolve, reject) {
             let img = new Image();
             img._onload_ = function() { resolve(img); };
-            img.src = ""
+            img.src = ""
         }).then(function(img) {
             return createImageBitmap(img);
         }).then(function(imageBitmap) {
             let [canvas, ctx] = create9x9CanvasWith2dContext();
-            assert_throws('IndexSizeError', function () {
+            assert_throws("IndexSizeError", function () {
                 ctx.drawImage(imageBitmap, 0, 0, 0, 0, 0, 0, 9, 9);
             }, "drawImage with an empty source rectangle should fail.");
         });

Added: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/resources/target-blue-dot-no-intrinsic.svg (0 => 223925)


--- trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/resources/target-blue-dot-no-intrinsic.svg	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/resources/target-blue-dot-no-intrinsic.svg	2017-10-24 21:46:41 UTC (rev 223925)
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 9 9">
+    <rect width="9" height="9" fill="#00ff00"/>
+    <rect x="4" y="4" width="1" height="1" fill="#0000ff"/>
+</svg>
Property changes on: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/resources/target-blue-dot-no-intrinsic.svg
___________________________________________________________________

Added: svn:eol-style

+native \ No newline at end of property

Added: svn:keywords

+Date Revision \ No newline at end of property

Added: svn:mime-type

+text/xml \ No newline at end of property

Copied: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/resources/target-blue-dot.png (from rev 223924, trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/target-blue-dot.png)


(Binary files differ)

Added: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/resources/target-blue-dot.svg (0 => 223925)


--- trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/resources/target-blue-dot.svg	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/resources/target-blue-dot.svg	2017-10-24 21:46:41 UTC (rev 223925)
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="9" height="9" viewBox="0 0 9 9">
+    <rect width="9" height="9" fill="#00ff00"/>
+    <rect x="4" y="4" width="1" height="1" fill="#0000ff"/>
+</svg>
Property changes on: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/resources/target-blue-dot.svg
___________________________________________________________________

Added: svn:eol-style

+native \ No newline at end of property

Added: svn:keywords

+Date Revision \ No newline at end of property

Added: svn:mime-type

+text/xml \ No newline at end of property

Deleted: trunk/LayoutTests/http/wpt/2dcontext/imagebitmap/target-blue-dot.png


(Binary files differ)

Modified: trunk/Source/WebCore/ChangeLog (223924 => 223925)


--- trunk/Source/WebCore/ChangeLog	2017-10-24 21:35:30 UTC (rev 223924)
+++ trunk/Source/WebCore/ChangeLog	2017-10-24 21:46:41 UTC (rev 223925)
@@ -1,3 +1,37 @@
+2017-10-24  Dean Jackson  <d...@apple.com>
+
+        Implement resizing options for ImageBitmap rendering
+        https://bugs.webkit.org/show_bug.cgi?id=178687
+        <rdar://problem/35135417>
+
+        Reviewed by Sam Weinig.
+
+        Implement many of the sizing options for createImageBitmap.
+
+        Test: http/wpt/2dcontext/imagebitmap/createImageBitmap-sizing.html
+
+        * html/ImageBitmap.cpp:
+        (WebCore::croppedSourceRectangleWithFormatting):
+
+            Implement the first bit of the algorithm from
+            the HTML specification.
+
+        (WebCore::outputSizeForSourceRectangle):
+
+            ... and then the second bit. Leave the rest in
+            comments for now.
+
+        (WebCore::interpolationQualityForResizeQuality):
+
+            Helper function to map ImageBitmapOptions to
+            GraphicsContext.
+
+        (WebCore::ImageBitmap::createPromise):
+
+            Implement more of the sizing algorithm.
+
+        * html/ImageBitmap.h: Origin tainting is defined to be false by default.
+
 2017-10-24  Chris Dumez  <cdu...@apple.com>
 
         Add initial implementation for serviceWorker.postMessage()

Modified: trunk/Source/WebCore/html/ImageBitmap.cpp (223924 => 223925)


--- trunk/Source/WebCore/html/ImageBitmap.cpp	2017-10-24 21:35:30 UTC (rev 223924)
+++ trunk/Source/WebCore/html/ImageBitmap.cpp	2017-10-24 21:46:41 UTC (rev 223925)
@@ -54,6 +54,7 @@
 static RenderingMode bufferRenderingMode = Unaccelerated;
 #endif
 
+
 Ref<ImageBitmap> ImageBitmap::create()
 {
     return adoptRef(*new ImageBitmap);
@@ -104,11 +105,112 @@
     return false;
 }
 
+// https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting
+static ExceptionOr<IntRect> croppedSourceRectangleWithFormatting(IntSize inputSize, ImageBitmapOptions& options, std::optional<IntRect> rect)
+{
+    // 2. If either or both of resizeWidth and resizeHeight members of options are less
+    //    than or equal to 0, then return a promise rejected with "InvalidStateError"
+    //    DOMException and abort these steps.
+    if ((options.resizeWidth && options.resizeWidth.value() <= 0) || (options.resizeHeight && options.resizeHeight.value() <= 0))
+        return Exception { InvalidStateError, "Invalid resize dimensions" };
+
+    // 3. If sx, sy, sw and sh are specified, let sourceRectangle be a rectangle whose
+    //    corners are the four points (sx, sy), (sx+sw, sy),(sx+sw, sy+sh), (sx,sy+sh).
+    //    Otherwise let sourceRectangle be a rectangle whose corners are the four points
+    //    (0,0), (width of input, 0), (width of input, height of input), (0, height of
+    //    input).
+    auto sourceRectangle = rect.value_or(IntRect { 0, 0, inputSize.width(), inputSize.height() });
+
+    // 4. Clip sourceRectangle to the dimensions of input.
+
+    sourceRectangle.setWidth(std::min(sourceRectangle.width(), inputSize.width()));
+    sourceRectangle.setHeight(std::min(sourceRectangle.height(), inputSize.height()));
+
+    return sourceRectangle;
+}
+
+static IntSize outputSizeForSourceRectangle(IntRect sourceRectangle, ImageBitmapOptions& options)
+{
+    // 5. Let outputWidth be determined as follows:
+    auto outputWidth = [&] () -> int {
+        if (options.resizeWidth)
+            return options.resizeWidth.value();
+        if (options.resizeHeight)
+            return ceil(sourceRectangle.width() * static_cast<double>(options.resizeHeight.value()) / sourceRectangle.height());
+        return sourceRectangle.width();
+    }();
+
+    // 6. Let outputHeight be determined as follows:
+    auto outputHeight = [&] () -> int {
+        if (options.resizeHeight)
+            return options.resizeHeight.value();
+        if (options.resizeWidth)
+            return ceil(sourceRectangle.height() * static_cast<double>(options.resizeWidth.value()) / sourceRectangle.width());
+        return sourceRectangle.height();
+    }();
+
+    return { outputWidth, outputHeight };
+}
+
+static InterpolationQuality interpolationQualityForResizeQuality(ImageBitmapOptions::ResizeQuality resizeQuality)
+{
+    switch (resizeQuality) {
+    case ImageBitmapOptions::ResizeQuality::Pixelated:
+        return InterpolationNone;
+    case ImageBitmapOptions::ResizeQuality::Low:
+        return InterpolationDefault; // Low is the default.
+    case ImageBitmapOptions::ResizeQuality::Medium:
+        return InterpolationMedium;
+    case ImageBitmapOptions::ResizeQuality::High:
+        return InterpolationHigh;
+    }
+}
+
+// FIXME: More steps from https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting
+
+// 7. Place input on an infinite transparent black grid plane, positioned so that its
+//    top left corner is at the origin of the plane, with the x-coordinate increasing
+//    to the right, and the y-coordinate increasing down, and with each pixel in the
+//    input image data occupying a cell on the plane's grid.
+
+// 8. Let output be the rectangle on the plane denoted by sourceRectangle.
+
+// 9. Scale output to the size specified by outputWidth and outputHeight. The user
+//    agent should use the value of the resizeQuality option to guide the choice of
+//    scaling algorithm.
+
+// 10. If the value of the imageOrientation member of options is "flipY", output must
+//     be flipped vertically, disregarding any image orientation metadata of the source
+//     (such as EXIF metadata), if any.
+
+// 11. If image is an img element or a Blob object, let val be the value of the
+//     colorSpaceConversion member of options, and then run these substeps:
+//
+//     1. If val is "default", the color space conversion behavior is implementation-specific,
+//        and should be chosen according to the color space that the implementation uses for
+//        drawing images onto the canvas.
+//
+//     2. If val is "none", output must be decoded without performing any color space
+//        conversions. This means that the image decoding algorithm must ignore color profile
+//        metadata embedded in the source data as well as the display device color profile.
+
+// 12. Let val be the value of premultiplyAlpha member of options, and then run these substeps:
+//
+//     1. If val is "default", the alpha premultiplication behavior is implementation-specific,
+//        and should be chosen according to implementation deems optimal for drawing images
+//        onto the canvas.
+//
+//     2. If val is "premultiply", the output that is not premultiplied by alpha must have its
+//        color components multiplied by alpha and that is premultiplied by alpha must be left
+//        untouched.
+//
+//     3. If val is "none", the output that is not premultiplied by alpha must be left untouched
+//        and that is premultiplied by alpha must have its color components divided by alpha.
+
+// 13. Return output.
+
 void ImageBitmap::createPromise(ScriptExecutionContext&, RefPtr<HTMLImageElement>& imageElement, ImageBitmapOptions&& options, std::optional<IntRect> rect, ImageBitmap::Promise&& promise)
 {
-    UNUSED_PARAM(options);
-    UNUSED_PARAM(rect);
-
     // 2. If image is not completely available, then return a promise rejected with
     // an "InvalidStateError" DOMException and abort these steps.
 
@@ -123,18 +225,36 @@
     //    resizeHeight options are not specified, then return a promise rejected with
     //    an "InvalidStateError" DOMException and abort these steps.
 
+    auto imageSize = cachedImage->imageSizeForRenderer(imageElement->renderer(), 1.0f);
+    if ((!imageSize.width() || !imageSize.height()) && (!options.resizeWidth || !options.resizeHeight)) {
+        promise.reject(InvalidStateError, "Cannot create ImageBitmap from a source with no intrinsic size without providing resize dimensions");
+        return;
+    }
+
     // 4. If image's media data has no intrinsic dimensions (e.g. it's a vector graphics
     //    with no specified content size), it should be rendered to a bitmap of the size
     //    specified by the resizeWidth and the resizeHeight options.
 
+    if (!imageSize.width() && !imageSize.height()) {
+        imageSize.setWidth(options.resizeWidth.value());
+        imageSize.setHeight(options.resizeHeight.value());
+    }
+
     // 5. If the sw and sh arguments are not specified and image's media data has both or
     //    either of its intrinsic width and intrinsic height values equal to 0, then return
     //    a promise rejected with an "InvalidStateError" DOMException and abort these steps.
-
     // 6. If the sh argument is not specified and image's media data has an intrinsic height
     //    of 0, then return a promise rejected with an "InvalidStateError" DOMException and
     //    abort these steps.
 
+    // FIXME: It's unclear how these steps can happen, since step 4 required setting a
+    // width and height for the image.
+
+    if (!rect && (!imageSize.width() || !imageSize.height())) {
+        promise.reject(InvalidStateError, "Cannot create ImageBitmap from a source with no intrinsic size without providing dimensions");
+        return;
+    }
+
     // 7. Create a new ImageBitmap object.
 
     auto imageBitmap = create();
@@ -145,17 +265,27 @@
     //    one that the format defines is to be used when animation is not supported or is disabled),
     //    or, if there is no such image, the first frame of the animation.
 
-    // FIXME: Move this into a separate function and handle the cropping/resizing.
-    auto bitmapData = ImageBuffer::create(FloatSize(imageElement->width(), imageElement->height()), bufferRenderingMode);
+    auto sourceRectangle = croppedSourceRectangleWithFormatting(roundedIntSize(imageSize), options, WTFMove(rect));
+    if (sourceRectangle.hasException()) {
+        promise.reject(sourceRectangle.releaseException());
+        return;
+    }
 
-    auto imageForRender = imageElement->cachedImage()->imageForRenderer(imageElement->renderer());
+    auto outputSize = outputSizeForSourceRectangle(sourceRectangle.returnValue(), options);
+    auto bitmapData = ImageBuffer::create(FloatSize(outputSize.width(), outputSize.height()), bufferRenderingMode);
+
+    auto imageForRender = cachedImage->imageForRenderer(imageElement->renderer());
     if (!imageForRender) {
         promise.reject(InvalidStateError, "Cannot create ImageBitmap from image that can't be rendered");
         return;
     }
-    FloatRect drawRect(FloatPoint(), FloatSize(imageElement->width(), imageElement->height()));
-    bitmapData->context().drawImage(*imageForRender, drawRect, drawRect);
 
+    FloatRect destRect(FloatPoint(), outputSize);
+    ImagePaintingOptions paintingOptions;
+    paintingOptions.m_interpolationQuality = interpolationQualityForResizeQuality(options.resizeQuality);
+
+    bitmapData->context().drawImage(*imageForRender, destRect, sourceRectangle.releaseReturnValue(), paintingOptions);
+
     imageBitmap->m_bitmapData = WTFMove(bitmapData);
 
     // 9. If the origin of image's image is not the same origin as the origin specified by the

Modified: trunk/Source/WebCore/html/ImageBitmap.h (223924 => 223925)


--- trunk/Source/WebCore/html/ImageBitmap.h	2017-10-24 21:35:30 UTC (rev 223924)
+++ trunk/Source/WebCore/html/ImageBitmap.h	2017-10-24 21:46:41 UTC (rev 223925)
@@ -91,7 +91,7 @@
 
     std::unique_ptr<ImageBuffer> m_bitmapData;
     bool m_detached { false };
-    bool m_originClean { false };
+    bool m_originClean { true };
 };
 
 }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to