diff options
Diffstat (limited to 'testing/web-platform/tests/html/canvas/element/manual/imagebitmap')
39 files changed, 1876 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-ImageBitmap-close.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-ImageBitmap-close.html new file mode 100644 index 0000000000..fca8274528 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-ImageBitmap-close.html @@ -0,0 +1,89 @@ +<!DOCTYPE html> +<body> + <p>Tests that the close method of ImageBitmap does dispose the image data.</p> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + +promise_test(function(t) { + var worker = new Worker('worker-onmessage-noop.js'); + + var imgHeight = 10; + var imgWidth = 10; + var imageData = new ImageData(10, 10); + var bitmap; + var ctx; + return createImageBitmap(imageData).then(imageBitmap => { + bitmap = imageBitmap; + assert_equals(bitmap.width, imgWidth, "bitmap.width = 10"); + assert_equals(bitmap.height, imgWidth, "bitmap.height = 10"); + + // Apply structured clone to the bitmap, nothing should be changed + worker.postMessage({data: bitmap}); + assert_equals(bitmap.width, imgWidth, "bitmap.width = 10"); + assert_equals(bitmap.height, imgWidth, "bitmap.height = 10"); + + // After calling close, the image data associated with the bitmap should no longer exist + bitmap.close(); + assert_equals(bitmap.width, 0, "bitmap.width = 0"); + assert_equals(bitmap.height, 0, "bitmap.height = 0"); + + var canvas = document.createElement("canvas"); + canvas.width = imgWidth; + canvas.height = imgHeight; + ctx = canvas.getContext("2d"); + assert_throws_dom("InvalidStateError", function() { ctx.drawImage(bitmap, 0, 0); }); + + // Try to apply structured clone to an already closed bitmap + try { + worker.postMessage({data: bitmap}); + throw new Error("Apply clone to an closed bitmap should be rejected"); + } + catch(ex) { + // Apply structured clone to an already closed bitmap is rejected as expected. + } + + // Try to apply transferring to an already closed bitmap + try { + worker.postMessage({data: bitmap}, [bitmap]); + throw new Error("Apply transferring to an closed bitmap should be rejected"); + } catch(ex) { + // Apply structured clone to an already closed bitmap is rejected as expected. + } + + // Calling createImageBitmap from a closed bitmap should be rejected + return createImageBitmap(bitmap).then(function() { + throw new Error("createImageBitmap from a closed bitmap should be rejected"); + }, ex => { + // Calling createImageBitmap from a closed ImageBitmap is rejected as expected. + }); + }).then(() => { + // Call close to a already closed bitmap should be noop. + bitmap.close(); + assert_equals(bitmap.width, 0, "bitmap.height = 0"); + assert_equals(bitmap.height, 0, "bitmap.height = 0"); + + return createImageBitmap(imageData).then(imageBitmap => { + bitmap = imageBitmap; + assert_equals(bitmap.width, imgWidth, "bitmap.width = 10"); + assert_equals(bitmap.height, imgWidth, "bitmap.height = 10"); + + // Transfer the bitmap to a worker + worker.postMessage({data: bitmap}, [bitmap]); + + // After transferring, the bitmap is neutered. + assert_equals(bitmap.width, 0, "bitmap.height = 0"); + assert_equals(bitmap.height, 0, "bitmap.height = 0"); + + // Calling close to a neutered bitmap should be noop. + bitmap.close(); + assert_equals(bitmap.width, 0, "bitmap.height = 0"); + assert_equals(bitmap.height, 0, "bitmap.height = 0"); + + }); + }).catch(function(ex) { + throw new Error("No exception should be thrown."); + }) +}); +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-resize.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-resize.html new file mode 100644 index 0000000000..782c7e130c --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-resize.html @@ -0,0 +1,170 @@ +<!DOCTYPE HTML> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +function checkNoCrop(imageBitmap) +{ + var canvas = document.createElement("canvas"); + canvas.width = 50; + canvas.height = 50; + var ctx = canvas.getContext("2d"); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(imageBitmap, 0, 0); + var d = ctx.getImageData(0, 0, 1, 1).data; + assert_array_equals(d, [255, 0, 0, 255], "This pixel should be red."); + d = ctx.getImageData(39, 0, 1, 1).data; + assert_array_equals(d, [0, 255, 0, 255], "This pixel should be green."); + d = ctx.getImageData(0, 39, 1, 1).data; + assert_array_equals(d, [0, 0, 255, 255], "This pixel should be blue."); + d = ctx.getImageData(39, 39, 1, 1).data; + assert_array_equals(d, [0, 0, 0, 255], "This pixel should be black."); + d = ctx.getImageData(41, 41, 1, 1).data; + assert_array_equals(d, [0, 0, 0, 0], "This pixel should be transparent black."); +} + +function checkCrop(imageBitmap) +{ + var canvas = document.createElement("canvas"); + canvas.width = 50; + canvas.height = 50; + var ctx = canvas.getContext("2d"); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(imageBitmap, 0, 0); + var d = ctx.getImageData(0, 0, 1, 1).data; + assert_array_equals(d, [255, 0, 0, 255], "This pixel should be red."); + d = ctx.getImageData(19, 0, 1, 1).data; + assert_array_equals(d, [0, 255, 0, 255], "This pixel should be green."); + d = ctx.getImageData(0, 19, 1, 1).data; + assert_array_equals(d, [0, 0, 255, 255], "This pixel should be blue."); + d = ctx.getImageData(19, 19, 1, 1).data; + assert_array_equals(d, [0, 0, 0, 255], "This pixel should be black."); + d = ctx.getImageData(21, 21, 1, 1).data; + assert_array_equals(d, [0, 0, 0, 0], "This pixel should be transparent black."); +} + +function compareBitmaps(bitmap1, bitmap2) +{ + var canvas1 = document.createElement("canvas"); + var canvas2 = document.createElement("canvas"); + canvas1.width = 50; + canvas1.height = 50; + canvas2.width = 50; + canvas2.height = 50; + var ctx1 = canvas1.getContext("2d"); + var ctx2 = canvas2.getContext("2d"); + ctx1.clearRect(0, 0, canvas1.width, canvas1.height); + ctx2.clearRect(0, 0, canvas2.width, canvas2.height); + ctx1.drawImage(bitmap1, 0, 0); + ctx2.drawImage(bitmap2, 0, 0); + var data1 = ctx1.getImageData(0, 0, 50, 50).data; + var data2 = ctx2.getImageData(0, 0, 50, 50).data; + var dataMatched = true; + for (var i = 0; i < data1.length; i++) { + if (data1[i] != data2[i]) { + dataMatched = false; + break; + } + } + assert_false(dataMatched); +} + +function testImageBitmap(source) +{ + return Promise.all([ + createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "high"}), + createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "medium"}), + createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "low"}), + createImageBitmap(source, {resizeWidth: 40, resizeHeight: 40, resizeQuality: "pixelated"}), + createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "high"}), + createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "medium"}), + createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "low"}), + createImageBitmap(source, 5, 5, 10, 10, {resizeWidth: 20, resizeHeight: 20, resizeQuality: "pixelated"}), + ]).then(([noCropHigh, noCropMedium, noCropLow, noCropPixelated, cropHigh, cropMedium, cropLow, cropPixelated]) => { + checkNoCrop(noCropHigh); + checkNoCrop(noCropMedium); + checkNoCrop(noCropLow); + checkNoCrop(noCropPixelated); + checkCrop(cropHigh); + checkCrop(cropMedium); + checkCrop(cropLow); + checkCrop(cropPixelated); + // Brute-force comparison among all bitmaps is too expensive + compareBitmaps(noCropHigh, noCropMedium); + compareBitmaps(noCropLow, noCropPixelated); + compareBitmaps(cropHigh, cropMedium); + compareBitmaps(cropLow, cropPixelated); + }); +} + +function initializeTestCanvas() +{ + var testCanvas = document.createElement("canvas"); + testCanvas.width = 20; + testCanvas.height = 20; + var testCtx = testCanvas.getContext("2d"); + testCtx.fillStyle = "rgb(255, 0, 0)"; + testCtx.fillRect(0, 0, 10, 10); + testCtx.fillStyle = "rgb(0, 255, 0)"; + testCtx.fillRect(10, 0, 10, 10); + testCtx.fillStyle = "rgb(0, 0, 255)"; + testCtx.fillRect(0, 10, 10, 10); + testCtx.fillStyle = "rgb(0, 0, 0)"; + testCtx.fillRect(10, 10, 10, 10); + return testCanvas; +} + +// Blob +promise_test(function() { + return new Promise((resolve, reject) => { + var xhr = new XMLHttpRequest(); + xhr.open("GET", '/images/pattern.png'); + xhr.responseType = 'blob'; + xhr.send(); + xhr.onload = function() { + resolve(xhr.response); + }; + }).then(testImageBitmap); +}, 'createImageBitmap from a Blob with resize option.'); + +// HTMLCanvasElement +promise_test(function() { + var testCanvas = initializeTestCanvas(); + return testImageBitmap(testCanvas); +}, 'createImageBitmap from a HTMLCanvasElement with resize option.'); + +// HTMLImageElement +promise_test(function() { + return new Promise((resolve, reject) => { + var image = new Image(); + image.onload = function() { + resolve(image); + } + image.src = '/images/pattern.png' + }).then(testImageBitmap); +}, 'createImageBitmap from a HTMLImageElement with resize option.'); + +// HTMLImageElement of svg with no specified size +promise_test(function() { + return new Promise((resolve, reject) => { + var image = new Image(); + image.onload = function() { + resolve(image); + } + image.src = '/images/pattern-nosize.svg' + }).then(testImageBitmap); +}, 'createImageBitmap from a HTMLImageElement of svg with no specified size with resize option.'); + +// ImageBitmap +promise_test(function() { + var testCanvas = initializeTestCanvas(); + return createImageBitmap(testCanvas).then(testImageBitmap); +}, 'createImageBitmap from an ImageBitmap with resize option.'); + +// ImageData +promise_test(function() { + var canvas = initializeTestCanvas(); + var ctx = canvas.getContext("2d"); + var data = ctx.getImageData(0, 0, 20, 20); + return testImageBitmap(data); +}, 'createImageBitmap from an ImageData with resize option.'); +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-video-resize.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-video-resize.html new file mode 100644 index 0000000000..d48d9e80ca --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/canvas-createImageBitmap-video-resize.html @@ -0,0 +1,62 @@ +<!DOCTYPE HTML> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/media.js"></script> +<script> +function createNewCanvas(width, height) +{ + var canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + var ctx = canvas.getContext("2d"); + ctx.clearRect(0, 0, width, height); + return ctx; +} + +function checkLowResult(imageBitmap, bw, bh, video, sx, sy, sw, sh) +{ + var ctx1 = createNewCanvas(bw, bh); + var ctx2 = createNewCanvas(bw, bh); + ctx1.drawImage(imageBitmap, 0, 0); + ctx2.drawImage(video, sx, sy, sw, sh, 0, 0, bw, bh); + var data1 = ctx1.getImageData(0, 0, bw, bh).data; + var data2 = ctx2.getImageData(0, 0, bw, bh).data; + var dataMatched = true; + for (var i = 0; i < data1.length; i++) { + // data1[i] is strictly the same as data2[i] on software rendering. + // But on GPU, the difference could be quite significant. + if (Math.abs(data1[i] - data2[i]) > 33) { + dataMatched = false; + break; + } + } + assert_true(dataMatched); +} + +function generateTest() +{ + bitmapWidth = video.videoWidth/2; + bitmapHeight = video.videoHeight/2; + return Promise.all([ + createImageBitmap(video, {resizeWidth: bitmapWidth, resizeHeight: bitmapHeight, resizeQuality: "low"}), + createImageBitmap(video, 10, 10, bitmapWidth, bitmapHeight, {resizeWidth: bitmapWidth, resizeHeight: bitmapHeight, resizeQuality: "low"}), + ]).then(t.step_func_done(([noCropLow, cropLow]) => { + checkLowResult(noCropLow, bitmapWidth, bitmapHeight, video, 0, 0, video.videoWidth, video.videoHeight); + checkLowResult(cropLow, bitmapWidth, bitmapHeight, video, 10, 10, bitmapWidth, bitmapHeight); + }), t.step_func_done(function() { + assert_true(false, 'Promise rejected'); + })); +} + +var t = async_test('createImageBitmap(HTMLVideoElement) with resize option'); + +// HTMLVideoElement +var video = document.createElement("video"); +video.preload = "auto"; +if (video.requestVideoFrameCallback) { + video.requestVideoFrameCallback(t.step_func(() => generateTest())); +} else { + video.oncanplaythrough = t.step_func(() => generateTest()); +} +video.src = getVideoURI("/media/counting"); +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/common.sub.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/common.sub.js new file mode 100644 index 0000000000..1889035202 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/common.sub.js @@ -0,0 +1,168 @@ +function makeCanvas() { + return new Promise(resolve => { + var testCanvas = document.createElement("canvas"); + testCanvas.width = 20; + testCanvas.height = 20; + var testCtx = testCanvas.getContext("2d"); + testCtx.fillStyle = "rgb(255, 0, 0)"; + testCtx.fillRect(0, 0, 10, 10); + testCtx.fillStyle = "rgb(0, 255, 0)"; + testCtx.fillRect(10, 0, 10, 10); + testCtx.fillStyle = "rgb(0, 0, 255)"; + testCtx.fillRect(0, 10, 10, 10); + testCtx.fillStyle = "rgb(0, 0, 0)"; + testCtx.fillRect(10, 10, 10, 10); + resolve(testCanvas); + }); +} + +function makeOffscreenCanvas() { + return new Promise(resolve => { + let canvas = new OffscreenCanvas(20, 20); + var testCtx = canvas.getContext("2d"); + testCtx.fillStyle = "rgb(255, 0, 0)"; + testCtx.fillRect(0, 0, 10, 10); + testCtx.fillStyle = "rgb(0, 255, 0)"; + testCtx.fillRect(10, 0, 10, 10); + testCtx.fillStyle = "rgb(0, 0, 255)"; + testCtx.fillRect(0, 10, 10, 10); + testCtx.fillStyle = "rgb(0, 0, 0)"; + testCtx.fillRect(10, 10, 10, 10); + resolve(canvas); + }); +} + +var imageBitmapVideoPromise = new Promise(function(resolve, reject) { + var video = document.createElement("video"); + video.oncanplaythrough = function() { + resolve(video); + }; + video.onerror = reject; + + // preload=auto is required to ensure a frame is available once + // canplaythrough is fired. The default of preload=metadata does not + // gaurantee this. + video.preload = "auto"; + video.src = getVideoURI("/images/pattern"); + + // Prevent WebKit from garbage collecting event handlers. + window._video = video; +}); + +function makeVideo() { + return imageBitmapVideoPromise; +} + +var imageBitmapDataUrlVideoPromise = fetch(getVideoURI("/images/pattern")) + .then(response => Promise.all([response.headers.get("Content-Type"), response.arrayBuffer()])) + .then(([type, data]) => { + return new Promise(function(resolve, reject) { + var video = document.createElement("video"); + video.oncanplaythrough = function() { + resolve(video); + }; + video.onerror = reject; + + var encoded = btoa(String.fromCodePoint(...new Uint8Array(data))); + var dataUrl = `data:${type};base64,${encoded}`; + + // preload=auto is required to ensure a frame is available once + // canplaythrough is fired. The default of preload=metadata does not + // gaurantee this. + video.preload = "auto"; + video.src = dataUrl; + + // Prevent WebKit from garbage collecting event handlers. + window._dataVideo = video; + }); + }); + +function makeDataUrlVideo() { + return imageBitmapDataUrlVideoPromise; +} + +function makeMakeHTMLImage(src) { + return function() { + return new Promise((resolve, reject) => { + var img = new Image(); + img.onload = function() { + resolve(img); + }; + img.onerror = reject; + img.src = src; + }); + } +} + +function makeMakeSVGImage(src) { + return function() { + return new Promise((resolve, reject) => { + var image = document.createElementNS("http://www.w3.org/2000/svg", "image"); + image.onload = () => resolve(image); + image.onerror = reject; + image.setAttribute("externalResourcesRequired", "true"); + image.setAttributeNS("http://www.w3.org/1999/xlink", 'xlink:href', src); + document.body.appendChild(image); + }); + } +} + +function makeImageData() { + return new Promise(function(resolve, reject) { + var width = 20, height = 20; + var imgData = new ImageData(width, height); + for (var i = 0; i < width * height * 4; i += 4) { + imgData.data[i] = 0; + imgData.data[i + 1] = 0; + imgData.data[i + 2] = 0; + imgData.data[i + 3] = 255; //alpha channel: 255 + } + var halfWidth = width / 2; + var halfHeight = height / 2; + // initialize to R, G, B, Black, with each one 10*10 pixels + for (var i = 0; i < halfHeight; i++) + for (var j = 0; j < halfWidth; j++) + imgData.data[i * width * 4 + j * 4] = 255; + for (var i = 0; i < halfHeight; i++) + for (var j = halfWidth; j < width; j++) + imgData.data[i * width * 4 + j * 4 + 1] = 255; + for (var i = halfHeight; i < height; i++) + for (var j = 0; j < halfWidth; j++) + imgData.data[i * width * 4 + j * 4 + 2] = 255; + resolve(imgData); + }); +} + +function makeImageBitmap() { + return makeCanvas().then(canvas => { + return createImageBitmap(canvas); + }); +} + +function makeBlob(src) { + return function () { + return new Promise(function(resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", src); + xhr.responseType = 'blob'; + xhr.send(); + xhr.onload = function() { + resolve(xhr.response); + }; + }); + } +} + +var imageSourceTypes = [ + { name: 'an HTMLCanvasElement', factory: makeCanvas }, + { name: 'an HTMLVideoElement', factory: makeVideo }, + { name: 'an HTMLVideoElement from a data URL', factory: makeDataUrlVideo }, + { name: 'a bitmap HTMLImageElement', factory: makeMakeHTMLImage("/images/pattern.png") }, + { name: 'a vector HTMLImageElement', factory: makeMakeHTMLImage("/images/pattern.svg") }, + { name: 'a bitmap SVGImageElement', factory: makeMakeSVGImage("/images/pattern.png") }, + { name: 'a vector SVGImageElement', factory: makeMakeSVGImage("/images/pattern.svg") }, + { name: 'an OffscreenCanvas', factory: makeOffscreenCanvas }, + { name: 'an ImageData', factory: makeImageData }, + { name: 'an ImageBitmap', factory: makeImageBitmap }, + { name: 'a Blob', factory: makeBlob("/images/pattern.png") }, +]; diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-blob-invalidtype.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-blob-invalidtype.html new file mode 100644 index 0000000000..23af96408a --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-blob-invalidtype.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<title>createImageBitmap: blob with wrong mime type</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/html/canvas/resources/canvas-tests.js"></script> +<script> +promise_test(t => { + // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain) + const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + + "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="); + + let bytes = new Array(IMAGE.length); + for (let i = 0; i < IMAGE.length; i++) { + bytes[i] = IMAGE.charCodeAt(i); + } + + let blob = new Blob([new Uint8Array(bytes)], { type: "text/html"}); + + return window.createImageBitmap(blob) + .then(imageBitmap => { + assert_true(true, "Image created!"); + assert_equals(imageBitmap.width, 1, "Image is 1x1"); + assert_equals(imageBitmap.height, 1, "Image is 1x1"); + }); +}); +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-bounds.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-bounds.html new file mode 100644 index 0000000000..a2dcf0cc0b --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-bounds.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<html> +<title>createImageBitmap: clipping to the bitmap</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/html/canvas/resources/canvas-tests.js"></script> +<div id="results"></div> +<script> +const color = 204; +function testClip( name, sx, sy, sw, sh, expectedColors, expectedWidth, expectedHeight ) { + promise_test(function(t) { + return new Promise(function(resolve, reject) { + const image = new Image(); + image.onload = function() { resolve(image); }; + image.onerror = function() { reject(); }; + image.src = "/images/green-16x16.png"; + }).then(function(image) { + return createImageBitmap(image, sx, sy, sw, sh); + }).then(function(imageBitmap) { + + assert_equals(imageBitmap.width, expectedWidth); + assert_equals(imageBitmap.height, expectedHeight); + + const canvas = document.createElement("canvas"); + canvas.width = 16; + canvas.height = 16; + + // debug + document.getElementById("results").append(canvas); + canvas.setAttribute("style", "width: 100px; height: 100px;"); + + const ctx = canvas.getContext("2d"); + ctx.fillStyle = `rgb(${color}, ${color}, ${color})`; + ctx.fillRect(0, 0, 20, 20); + ctx.drawImage(imageBitmap, 0, 0); + + for (let [x, y, r, g, b, a] of expectedColors) { + _assertPixel(canvas, x,y, r,g,b,a); + } + }); + }, name); +} +testClip( "simple clip inside", + 8, 8, 8, 8, [ + [ 4, 4, 0,255,0,255], [12, 4, color,color,color,255], + [ 4, 12, color,color,color,255], [12, 12, color,color,color,255] + ], 8, 8 +); +testClip( "clip outside negative", + -8, -8, 16, 16, [ + [ 4, 4, color,color,color,255], [12, 4, color,color,color,255], + [ 4, 12, color,color,color,255], [12, 12, 0,255,0,255] + ], 16, 16 +); +testClip( "clip outside positive", + 8, 8, 16, 16, [ + [ 4, 4, 0,255,0,255], [12, 4, color,color,color,255], + [ 4, 12, color,color,color,255], [12, 12, color,color,color,255] + ], 16, 16 +); +testClip( "clip inside using negative width and height", + 24, 24, -16, -16, [ + [ 4, 4, 0,255,0,255], [12, 4, color,color,color,255], + [ 4, 12, color,color,color,255], [12, 12, color,color,color,255] + ], 16, 16 +); +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html new file mode 100644 index 0000000000..2ddf3648f5 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-colorSpaceConversion.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html> +<title>Test colorSpaceConversion option for createImageBitmap</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/html/canvas/resources/canvas-tests.js"></script> +<script src="/common/media.js"></script> +<script src="common.sub.js"></script> +<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css"> +<body> +<script> +function testCanvasDisplayingPattern(canvas, width, height) +{ + var tolerance = 10; // high tolerance for differing color management results + const check = (x, y, r, g, b, a) => + _assertPixelApprox(canvas, x,y, r,g,b,a, tolerance); + + check(1 * width / 4, 1 * height / 4, 124,0,27,255); + check(3 * width / 4, 1 * height / 4, 0,124,46,255); + check(1 * width / 4, 3 * height / 4, 60,0,123,255); + check(3 * width / 4, 3 * height / 4, 0,0,0,255); +} + +function testDrawImageBitmap(source, options) +{ + var canvas = document.createElement("canvas"); + canvas.width = 20; + canvas.height = 20; + var ctx = canvas.getContext("2d"); + return createImageBitmap(source, options).then(imageBitmap => { + ctx.drawImage(imageBitmap, 0, 0); + testCanvasDisplayingPattern(canvas, 20, 20); + }); +} + +var wideGamutImageSourceTypes = [ + {name: 'a bitmap HTMLImageElement', factory: makeMakeHTMLImage("/images/wide-gamut-pattern.png")}, + {name: 'a Blob', factory: makeBlob("/images/wide-gamut-pattern.png")}, +]; + +for (let { name, factory } of wideGamutImageSourceTypes) { + promise_test(function() { + return factory().then(function(img) { + return testDrawImageBitmap(img, {colorSpaceConversion: "none"}); + }); + }, `createImageBitmap from ${name}, and drawImage on the created ImageBitmap with colorSpaceConversion: none`); +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage-closed.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage-closed.html new file mode 100644 index 0000000000..3b8644cff5 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage-closed.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<title>attempt to draw a closed ImageBitmap to a 2d canvas throws INVALID_STATE_ERR</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/html/canvas/resources/canvas-tests.js"></script> +<script> +promise_test(function(t) { + return new Promise(function(resolve, reject) { + const image = new Image(); + image.onload = function() { resolve(image); }; + image.onerror = function() { reject(); }; + image.src = "/images/green-16x16.png"; + }).then(function(image) { + return createImageBitmap(image, 0, 0, 16, 16); + }).then(function(imageBitmap) { + imageBitmap.close(); + + const canvas = document.createElement("canvas"); + canvas.width = 16; + canvas.height = 16; + + const ctx = canvas.getContext("2d"); + assert_throws_dom("InvalidStateError", function() { ctx.drawImage(imageBitmap, 0, 0); }); + }); +}); +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html new file mode 100644 index 0000000000..5b5698813a --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html @@ -0,0 +1,87 @@ +<!DOCTYPE html> +<html> +<title>createImageBitmap + drawImage test</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/html/canvas/resources/canvas-tests.js"></script> +<script src="/common/media.js"></script> +<script src="common.sub.js"></script> +<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css"> +<body> +<script> +function testCanvasDisplayingPattern(canvas, width, height, sourceIsVideo) +{ + var tolerance = 3; + let topLeft = [255, 0, 0, 255]; + let topRight = [0, 255, 0, 255]; + let bottomLeft = [0, 0, 255, 255]; + let bottomRight = [0, 0, 0, 255]; + if (sourceIsVideo) { + // The source video uses colors in the Rec.601 color space whose + // values are close to full red, full green, full blue, and black, + // but when converted to sRGB, are somewhat different. + topLeft = [247, 37, 0, 255]; + topRight = [63, 251, 0, 255]; + bottomLeft = [28, 35, 255, 255]; + bottomRight = [5, 0, 2, 255]; + } + const check = (x, y, [r, g, b, a]) => + _assertPixelApprox(canvas, x,y, r,g,b,a, tolerance); + check(1 * width / 4, 1 * height / 4, topLeft); + check(3 * width / 4, 1 * height / 4, topRight); + check(1 * width / 4, 3 * height / 4, bottomLeft); + check(3 * width / 4, 3 * height / 4, bottomRight); +} + +function testDrawImageBitmap(source, args = [], { resizeWidth = 20, resizeHeight = 20 } = {}) +{ + let sourceIsVideo = source instanceof HTMLVideoElement; + var canvas = document.createElement("canvas"); + canvas.width = resizeWidth; + canvas.height = resizeHeight; + var ctx = canvas.getContext("2d"); + return createImageBitmap(source, ...args).then(imageBitmap => { + assert_equals(imageBitmap.width, resizeWidth); + assert_equals(imageBitmap.height, resizeHeight); + ctx.drawImage(imageBitmap, 0, 0); + testCanvasDisplayingPattern(canvas, resizeWidth, resizeHeight, sourceIsVideo); + }); +} + +for (let { name, factory } of imageSourceTypes) { + promise_test(function() { + return factory().then(function(img) { + return testDrawImageBitmap(img); + }); + }, `createImageBitmap from ${name}, and drawImage on the created ImageBitmap`); + + promise_test(function() { + return factory().then(function(img) { + const options = { resizeWidth: 10, resizeHeight: 10 }; + return testDrawImageBitmap(img, [options], options); + }); + }, `createImageBitmap from ${name} scaled down, and drawImage on the created ImageBitmap`); + + promise_test(function() { + return factory().then(function(img) { + const options = { resizeWidth: 40, resizeHeight: 40 }; + return testDrawImageBitmap(img, [options], options); + }); + }, `createImageBitmap from ${name} scaled up, and drawImage on the created ImageBitmap`); + + promise_test(function() { + return factory().then(function(img) { + const options = { resizeWidth: 10, resizeHeight: 40 }; + return testDrawImageBitmap(img, [options], options); + }); + }, `createImageBitmap from ${name} resized, and drawImage on the created ImageBitmap`); + + promise_test(function() { + return factory().then(function(img) { + return testDrawImageBitmap(img, [20, 20, -20, -20]); + }); + }, `createImageBitmap from ${name} with negative sw/sh, and drawImage on the created ImageBitmap`); +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html new file mode 100644 index 0000000000..8b2a33e85b --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html @@ -0,0 +1,121 @@ +<!DOCTYPE html> +<title>Test that createImageBitmap honors EXIF orientation</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style>canvas { outline: 1px solid black; margin-right: 1em; }</style> +<body> +<script> +function loadImage(src) { + return new Promise(function(resolve) { + const image = new Image(); + image.addEventListener("load", () => resolve(image), { once: true }); + image.src = src; + }); +} + +function checkColors(ctx, w, h, expectedColors) { + let data = ctx.getImageData(0, 0, w, h).data; + for (let [row, col, r, g, b, a] of expectedColors) { + let x = col * 80 + 40; + let y = row * 80 + 40; + let i = (x + y * w) * 4; + + let expected = [r, g, b, a]; + let actual = [data[i], data[i + 1], data[i + 2], data[i + 3]]; + + assert_array_approx_equals(actual, expected, 1, `Pixel value at (${x},${y}) ${expected} =~ ${actual}.`); + } +} + +async_test(function(t) { + const canvas = document.createElement("canvas"); + canvas.width = 320; + canvas.height = 160; + document.body.append(canvas); + + const ctx = canvas.getContext("2d"); + loadImage("resources/squares_6.jpg") + .then((image) => createImageBitmap(image)) + .then(t.step_func_done(function(imageBitmap) { + ctx.drawImage(imageBitmap, 0, 0); + checkColors(ctx, canvas.width, canvas.height, [ + // row, col, r, g, b, a + [0, 0, 255, 0, 0, 255], + [0, 1, 0, 255, 0, 255], + [0, 2, 0, 0, 255, 255], + [0, 3, 0, 0, 0, 255], + [1, 0, 255, 128, 128, 255], + [1, 1, 128, 255, 128, 255], + [1, 2, 128, 128, 255, 255], + [1, 3, 128, 128, 128, 255], + ]); + })); +}, "createImageBitmap with EXIF rotation, imageOrientation from-image, and no cropping"); + +async_test(function(t) { + const canvas = document.createElement("canvas"); + canvas.width = 320; + canvas.height = 160; + document.body.append(canvas); + + const ctx = canvas.getContext("2d"); + loadImage("resources/squares_6.jpg") + .then((image) => createImageBitmap(image, { imageOrientation: "flipY" })) + .then(t.step_func_done(function(imageBitmap) { + ctx.drawImage(imageBitmap, 0, 0); + checkColors(ctx, canvas.width, canvas.height, [ + // row, col, r, g, b, a + [0, 0, 255, 128, 128, 255], + [0, 1, 128, 255, 128, 255], + [0, 2, 128, 128, 255, 255], + [0, 3, 128, 128, 128, 255], + [1, 0, 255, 0, 0, 255], + [1, 1, 0, 255, 0, 255], + [1, 2, 0, 0, 255, 255], + [1, 3, 0, 0, 0, 255], + ]); + })); +}, "createImageBitmap with EXIF rotation, imageOrientation flipY, and no cropping"); + +async_test(function(t) { + const canvas = document.createElement("canvas"); + canvas.width = 160; + canvas.height = 160; + document.body.append(canvas); + + const ctx = canvas.getContext("2d"); + loadImage("resources/squares_6.jpg") + .then(image => createImageBitmap(image, 80, 0, 160, 160)) + .then(t.step_func_done(function(imageBitmap) { + ctx.drawImage(imageBitmap, 0, 0); + checkColors(ctx, canvas.width, canvas.height, [ + // row, col, r, g, b, a + [0, 0, 0, 255, 0, 255], + [0, 1, 0, 0, 255, 255], + [1, 0, 128, 255, 128, 255], + [1, 1, 128, 128, 255, 255], + ]); + })); +}, "createImageBitmap with EXIF rotation, imageOrientation from-image, and cropping"); + +async_test(function(t) { + const canvas = document.createElement("canvas"); + canvas.width = 160; + canvas.height = 160; + document.body.append(canvas); + + const ctx = canvas.getContext("2d"); + loadImage("resources/squares_6.jpg") + .then(image => createImageBitmap(image, 80, 0, 160, 160, { imageOrientation: "flipY" })) + .then(t.step_func_done(function(imageBitmap) { + ctx.drawImage(imageBitmap, 0, 0); + checkColors(ctx, canvas.width, canvas.height, [ + // row, col, r, g, b, a + [0, 0, 128, 255, 128, 255], + [0, 1, 128, 128, 255, 255], + [1, 0, 0, 255, 0, 255], + [1, 1, 0, 0, 255, 255], + ]); + })); +}, "createImageBitmap with EXIF rotation, imageOrientation flipY, and cropping"); +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation_none.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation_none.html new file mode 100644 index 0000000000..807925b88a --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation_none.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<title>Test that createImageBitmap honors EXIF orientation</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style>canvas { outline: 1px solid black; margin-right: 1em; }</style> +<body> +<script> +function loadImage(src) { + return new Promise(function(resolve) { + const image = new Image(); + image.addEventListener("load", () => resolve(image), { once: true }); + image.src = src; + }); +} + +function checkColors(ctx, w, h, is_verticle, expectedColors) { + let data = ctx.getImageData(0, 0, w, h).data; + row_width = 80; + col_width = 80; + + for (let [row, col, r, g, b, a] of expectedColors) { + let x = col * row_width + 10; + let y = row * col_width + 10; + let i = (x + y * w) * 4; + + let expected = [r, g, b, a]; + let actual = [data[i], data[i + 1], data[i + 2], data[i + 3]]; + + assert_array_approx_equals(actual, expected, 1, `Pixel value at (${x},${y}) ${expected} =~ ${actual}.`); + } +} + +for (let orientation of [1, 2, 3, 4, 5, 6, 7, 8]) { + async_test(function(t) { + const canvas = document.createElement("canvas"); + canvas.width = 160; + canvas.height = 320; + document.body.append(canvas); + + const ctx = canvas.getContext("2d"); + loadImage(`resources/squares_${orientation}.jpg`) + .then((image) => createImageBitmap(image, { imageOrientation: "none" })) + .then(t.step_func_done(function(imageBitmap) { + ctx.drawImage(imageBitmap, 0, 0, 160, 320); + + checkColors(ctx, canvas.width, canvas.height, false, [ + // row, col, r, g, b, a + [0, 0, 0, 0, 0, 255], + [0, 1, 128, 128, 128, 255], + [1, 0, 0, 0, 255, 255], + [1, 1, 128, 128, 255, 255], + [2, 0, 0, 255, 0, 255], + [2, 1, 128, 255, 128, 255], + [3, 0, 255, 0, 0, 255], + [3, 1, 255, 128, 128, 255], + ]); + })); + }, `createImageBitmap with Orientation ${orientation}`); +} + +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html new file mode 100644 index 0000000000..9b0d2dfb79 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html @@ -0,0 +1,73 @@ +<!DOCTYPE html> +<html> +<title>createImageBitmap + drawImage test</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/html/canvas/resources/canvas-tests.js"></script> +<script src="/common/media.js"></script> +<script src="common.sub.js"></script> +<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css"> +<body> +<script> +function testCanvasDisplayingPattern(canvas, width, height, sourceIsVideo, flipped) +{ + var tolerance = 3; + let topLeft = [255, 0, 0, 255]; + let topRight = [0, 255, 0, 255]; + let bottomLeft = [0, 0, 255, 255]; + let bottomRight = [0, 0, 0, 255]; + if (sourceIsVideo) { + // The source video uses colors in the Rec.601 color space whose + // values are close to full red, full green, full blue, and black, + // but when converted to sRGB, are somewhat different. + topLeft = [247, 37, 0, 255]; + topRight = [63, 251, 0, 255]; + bottomLeft = [28, 35, 255, 255]; + bottomRight = [5, 0, 2, 255]; + } + if (flipped) { + [topLeft, bottomLeft] = [bottomLeft, topLeft]; + [topRight, bottomRight] = [bottomRight, topRight]; + } + const check = (x, y, [r, g, b, a]) => + _assertPixelApprox(canvas, x,y, r,g,b,a, tolerance); + check(1 * width / 4, 1 * height / 4, topLeft); + check(3 * width / 4, 1 * height / 4, topRight); + check(1 * width / 4, 3 * height / 4, bottomLeft); + check(3 * width / 4, 3 * height / 4, bottomRight); +} + +function testDrawImageBitmap(source, args = [], flipped, { resizeWidth = 20, resizeHeight = 20 } = {}) +{ + let sourceIsVideo = source instanceof HTMLVideoElement; + var canvas = document.createElement("canvas"); + canvas.width = resizeWidth; + canvas.height = resizeHeight; + var ctx = canvas.getContext("2d"); + return createImageBitmap(source, ...args).then(imageBitmap => { + assert_equals(imageBitmap.width, resizeWidth); + assert_equals(imageBitmap.height, resizeHeight); + ctx.drawImage(imageBitmap, 0, 0); + testCanvasDisplayingPattern(canvas, resizeWidth, resizeHeight, sourceIsVideo, flipped); + }); +} + +for (let { name, factory } of imageSourceTypes) { + promise_test(function() { + return factory().then(function(img) { + const options = { imageOrientation: 'from-image' }; + return testDrawImageBitmap(img, [options], false); + }); + }, `createImageBitmap from ${name} imageOrientation: "from-image", and drawImage on the created ImageBitmap`); + + promise_test(function() { + return factory().then(function(img) { + const options = { imageOrientation: 'flipY' }; + return testDrawImageBitmap(img, [options], true); + }); + }, `createImageBitmap from ${name} imageOrientation: "flipY", and drawImage on the created ImageBitmap`); + +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-in-worker-transfer.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-in-worker-transfer.html new file mode 100644 index 0000000000..727a8a4978 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-in-worker-transfer.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>createImageBitmap in worker and transfer</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id=log></div> +<script> +promise_test(function(t) { + return new Promise(function(resolve, reject) { + var worker = new Worker("createImageBitmap-worker.js"); + worker.addEventListener("message", function(evt) { + var bitmap = evt.data; + assert_equals(bitmap.width, 20); + assert_equals(bitmap.height, 20); + resolve(); + }); + worker.postMessage('test'); + }); +}, 'Transfer ImageBitmap created in worker'); +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html new file mode 100644 index 0000000000..3cae3523ef --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html @@ -0,0 +1,239 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/media.js"></script> +<script src="common.sub.js"></script> +<script> + +function makeOversizedCanvas() { + + return new Promise(resolve => { + let canvas = document.createElement('canvas'); + canvas.setAttribute('width', '100000000'); + canvas.setAttribute('height', '100000000'); + resolve(canvas); + }); +} + +function makeOversizedOffscreenCanvas() { + return new Promise(resolve =>{ + let canvas = new OffscreenCanvas(100000000, 100000000); + resolve(canvas); + }); +} + +function makeInvalidBlob() { + return new Promise(resolve => { + resolve(new Blob()); // Blob with no data cannot be decoded. + }); +} + +function makeBrokenImage() { + return new Promise((resolve, reject) => { + const image = new Image(); + image.src = "data:,x"; + image.onload = reject; + image.onerror = () => resolve(image); + }); +} + +function makeAvailableButBrokenImage(path) { + return new Promise((resolve, reject) => { + const image = new Image(); + image.src = path; + image.onload = () => resolve(image); + image.onerror = reject; + }); +} + +testCases = [ + { + description: 'createImageBitmap with <sourceType> source and sw set to 0', + promiseTestFunction: + (source, t) => { + return promise_rejects_js(t, RangeError, + createImageBitmap(source, 0, 0, 0, 10)); + } + }, + { + description: 'createImageBitmap with <sourceType> source and sh set to 0', + promiseTestFunction: + (source, t) => { + return promise_rejects_js(t, RangeError, + createImageBitmap(source, 0, 0, 10, 0)); + } + }, + { + description: 'createImageBitmap with <sourceType> source and oversized ' + + '(unallocatable) crop region', + promiseTestFunction: + (source, t) => { + return createImageBitmap(source, 0, 0, 100000000, 100000000) + .then(i => { + assert_equals(i.width, 100000000); + assert_equals(i.height, 100000000); + }) + .catch(e => { + // This case is not explicitly documented in the specification for + // createImageBitmap, but it is expected that internal failures + // cause InvalidStateError. + // + // Note: https://bugs.chromium.org/p/chromium/issues/detail?id=799025 + assert_throws_dom("InvalidStateError", () => { throw e }); + }); + } + }, + { + description: 'createImageBitmap with <sourceType> source and ' + + 'a value of 0 int resizeWidth', + promiseTestFunction: + (source, t) => { + return createImageBitmap(source, {resizeWidth:0, resizeHeight:10}) + .catch(e => { + assert_throws_dom("InvalidStateError", () => { throw e }); + }); + } + }, + { + description: 'createImageBitmap with <sourceType> source and ' + + 'a value of 0 in resizeHeight', + promiseTestFunction: + (source, t) => { + return createImageBitmap(source, {resizeWidth:10, resizeHeight:0}) + .catch(e => { + assert_throws_dom("InvalidStateError", () => { throw e }); + }); + } + }, + { + description: 'createImageBitmap with <sourceType> source and ' + + 'a value between 0 and 1 in resizeWidth', + promiseTestFunction: + (source, t) => { + return createImageBitmap(source, {resizeWidth:0.5, resizeHeight:10}) + .catch(e => { + assert_throws_dom("InvalidStateError", () => { throw e }); + }); + } + }, + { + description: 'createImageBitmap with <sourceType> source and ' + + 'a value between 0 and 1 in resizeHeight', + promiseTestFunction: + (source, t) => { + return createImageBitmap(source, {resizeWidth:10, resizeHeight:0.5}) + .catch(e => { + assert_throws_dom("InvalidStateError", () => { throw e }); + }); + } + }, +]; + +// Generate the test matrix for each sourceType + testCase combo. +imageSourceTypes.forEach(imageSourceType => { + testCases.forEach(testCase => { + let description = testCase.description.replace('<sourceType>', + imageSourceType.name); + promise_test( t => { + return imageSourceType.factory().then(source => { + return testCase.promiseTestFunction(source, t); + }); + }, description); + }); +}); + +promise_test( t => { + return promise_rejects_js(t, TypeError, createImageBitmap(undefined)); +}, "createImageBitmap with undefined image source."); + +promise_test( t => { + return promise_rejects_js(t, TypeError, createImageBitmap(null)); +}, "createImageBitmap with null image source."); + +promise_test( t => { + var context = document.createElement("canvas").getContext("2d"); + return promise_rejects_js(t, TypeError, createImageBitmap(context)); +}, "createImageBitmap with CanvasRenderingContext2D image source."); + +promise_test( t => { + var context = document.createElement("canvas").getContext("webgl"); + return promise_rejects_js(t, TypeError, createImageBitmap(context)); +}, "createImageBitmap with WebGLRenderingContext image source."); + +promise_test( t => { + var buffer = new Uint8Array(); + return promise_rejects_js(t, TypeError, createImageBitmap(buffer)); +}, "createImageBitmap with Uint8Array image source."); + +promise_test( t => { + var buffer = new ArrayBuffer(8); + return promise_rejects_js(t, TypeError, createImageBitmap(buffer)); +}, "createImageBitmap with ArrayBuffer image source."); + +promise_test( t => { + return promise_rejects_dom(t, "InvalidStateError", + createImageBitmap(new Image())); +}, "createImageBitmap with empty image source."); + +promise_test( t => { + return promise_rejects_dom(t, "InvalidStateError", + createImageBitmap(document.createElement('video'))); +}, "createImageBitmap with empty video source."); + +promise_test( t => { + return makeOversizedCanvas().then(canvas => { + return promise_rejects_dom(t, "InvalidStateError", + createImageBitmap(canvas)); + }); +}, "createImageBitmap with an oversized canvas source."); + +promise_test( t => { + return makeOversizedOffscreenCanvas().then(offscreenCanvas => { + return promise_rejects_dom(t, "InvalidStateError", + createImageBitmap(offscreenCanvas)); + }); +}, "createImageBitmap with an invalid OffscreenCanvas source."); + +promise_test( t => { + return makeInvalidBlob().then(blob => { + return promise_rejects_dom(t, "InvalidStateError", + createImageBitmap(blob)); + }); +}, "createImageBitmap with an undecodable blob source."); + +promise_test( t => { + return makeBrokenImage().then(image => { + return promise_rejects_dom(t, "InvalidStateError", + createImageBitmap(image)); + }); +}, "createImageBitmap with a broken image source."); + +promise_test( t => { + return makeAvailableButBrokenImage("/images/undecodable.png").then(image => { + return promise_rejects_dom(t, "InvalidStateError", + createImageBitmap(image)); + }); +}, "createImageBitmap with an available but undecodable image source."); + +promise_test( t => { + return makeAvailableButBrokenImage("/images/red-zeroheight.svg").then(image => { + return promise_rejects_dom(t, "InvalidStateError", + createImageBitmap(image)); + }); +}, "createImageBitmap with an available but zero height image source."); + +promise_test( t => { + return makeAvailableButBrokenImage("/images/red-zerowidth.svg").then(image => { + return promise_rejects_dom(t, "InvalidStateError", + createImageBitmap(image)); + }); +}, "createImageBitmap with an available but zero width image source."); + +promise_test( t => { + return makeImageBitmap().then(bitmap => { + bitmap.close() + return promise_rejects_dom(t, "InvalidStateError", + createImageBitmap(bitmap)); + }); +}, "createImageBitmap with a closed ImageBitmap."); +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html new file mode 100644 index 0000000000..ae3d23cfbc --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>createImageBitmap: origin-clean flag</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/common/media.js"></script> +<script src="/html/canvas/resources/canvas-tests.js"></script> +<div id=log></div> +<script> + +function assert_origin_unclean_getImageData(bitmap) { + const context = document.createElement("canvas").getContext("2d"); + context.drawImage(bitmap, 0, 0); + assert_throws_dom("SecurityError", () => { + context.getImageData(0, 0, 1, 1); + }); +} + +function assert_origin_unclean_drawImage(bitmap) { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + ctx.drawImage(bitmap, 0, 0); + assert_throws_dom('SecurityError', () => canvas.toDataURL()); +} + +function assert_origin_unclean_transferFromImageBitmap(bitmap) { + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('bitmaprenderer'); + ctx.transferFromImageBitmap(bitmap); + assert_throws_dom('SecurityError', () => canvas.toDataURL()); +} + +forEachCanvasSource(get_host_info().HTTP_REMOTE_ORIGIN, + get_host_info().HTTP_ORIGIN, + (name, factory) => { + promise_test(function() { + return factory().then(createImageBitmap).then(assert_origin_unclean_getImageData); + }, `${name}: origin unclear getImageData`); + promise_test(function() { + return factory().then(createImageBitmap).then(assert_origin_unclean_drawImage); + }, `${name}: origin unclear 2dContext.drawImage`); + promise_test(function() { + return factory().then(createImageBitmap).then(assert_origin_unclean_transferFromImageBitmap); + }, `${name}: origin unclear bitmaprenderer.transferFromImageBitmap`); +}); +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html new file mode 100644 index 0000000000..c185cd9cbd --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>createImageBitmap serialize test</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/namespaces.js"></script> +<script src="/common/media.js"></script> +<script src="common.sub.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<div id=log></div> +<script> +let worker, continuations = {}; +setup(function() { + worker = new Worker("transfer-worker.js"); + worker.addEventListener("message", function(event) { + let { name, bitmap } = event.data; + if (continuations.hasOwnProperty(name)) { + continuations[name](bitmap); + } + }); +}); + +for (let { name, factory } of imageSourceTypes) { + promise_test(function(t) { + return factory().then(createImageBitmap).then(function(bitmap) { + assert_equals(bitmap.width, 20); + assert_equals(bitmap.height, 20); + + worker.postMessage({ name: t.name, bitmap: bitmap }); + + assert_equals(bitmap.width, 20); + assert_equals(bitmap.height, 20); + + return new Promise(function(resolve) { + continuations[t.name] = resolve; + }); + }).then(function(bitmap) { + assert_class_string(bitmap, "ImageBitmap"); + assert_equals(bitmap.width, 20); + assert_equals(bitmap.height, 20); + }); + }, `Serialize ImageBitmap created from ${name}`); +} + +promise_test(async (t) => { + const url = get_host_info().REMOTE_ORIGIN + '/images/pattern.png'; + const image = await makeMakeHTMLImage(url)(); + const bitmap = await createImageBitmap(image); + + assert_throws_dom('DataCloneError', () => worker.postMessage(bitmap)); +}, 'Serializing a non-origin-clean ImageBitmap throws.'); +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-sizeOverflow.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-sizeOverflow.html new file mode 100644 index 0000000000..25c1fb6885 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-sizeOverflow.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<html> +<title>createImageBitmap with size overflow</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> +promise_test(function() { + var imgData = new ImageData(20, 20); + return createImageBitmap(imgData, 4294967400, 10, 10, 10); +}, "createImageBitmap does not crash or reject the promise when passing very large sx"); + +promise_test(function() { + var imgData = new ImageData(20, 20); + return createImageBitmap(imgData, 10, 4294967400, 10, 10); +}, "createImageBitmap does not crash or reject the promise when passing very large sy"); + +promise_test(function() { + var imgData = new ImageData(20, 20); + return createImageBitmap(imgData, 10, 10, 4294967400, 10); +}, "createImageBitmap does not crash or reject the promise when passing very large sw"); + +promise_test(function() { + var imgData = new ImageData(20, 20); + return createImageBitmap(imgData, 10, 10, 10, 4294967400); +}, "createImageBitmap does not crash or reject the promise when passing very large sh"); + +promise_test(function() { + var imgData = new ImageData(20, 20); + return createImageBitmap(imgData, 4294967400, 4294967400, 4294967400, 4294967400); +}, "createImageBitmap does not crash or reject the promise when passing very large sx, sy, sw and sh"); + +promise_test(function(t) { + var imgData = new ImageData(20, 20); + var imageBitmapOptions = {imageOrientation:'from-image', premultiplyAlpha:'default', + colorSpaceConversion:'none', resizeHeight:2122252543, resizeQuality:'high'}; + return createImageBitmap(imgData, 0, 0, 4294967295, 64) + .then(imageBitmap => promise_rejects_dom(t, "InvalidStateError", + createImageBitmap(imageBitmap, imageBitmapOptions))); +}, "createImageBitmap throws an InvalidStateError error with big imageBitmap scaled up in big height"); + +promise_test(function(t) { + var imgData = new ImageData(20, 20); + var imageBitmapOptions = {imageOrientation:'from-image', premultiplyAlpha:'default', + colorSpaceConversion:'none', resizeWidth:2122252543, resizeQuality:'high'}; + return createImageBitmap(imgData, 0, 0, 4294967295, 64) + .then(imageBitmap => promise_rejects_dom(t, "InvalidStateError", + createImageBitmap(imageBitmap, imageBitmapOptions))); +}, "createImageBitmap throws an InvalidStateError error with big imageBitmap scaled up in big width"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html new file mode 100644 index 0000000000..3ec02fcbf4 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>createImageBitmap transferring test</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/media.js"></script> +<script src="common.sub.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<div id=log></div> +<script> +let worker, continuations = {}; +setup(function() { + worker = new Worker("transfer-worker.js"); + worker.addEventListener("message", function(event) { + let { name, bitmap } = event.data; + if (continuations.hasOwnProperty(name)) { + continuations[name](bitmap); + } + }); +}); + +for (let { name, factory } of imageSourceTypes) { + promise_test(function(t) { + return factory().then(createImageBitmap).then(function(bitmap) { + assert_equals(bitmap.width, 20); + assert_equals(bitmap.height, 20); + + worker.postMessage({ name: t.name, bitmap: bitmap }, [bitmap]); + + assert_equals(bitmap.width, 0); + assert_equals(bitmap.height, 0); + + return new Promise(function(resolve) { + continuations[t.name] = resolve; + }); + }).then(function(bitmap) { + assert_class_string(bitmap, "ImageBitmap"); + assert_equals(bitmap.width, 20); + assert_equals(bitmap.height, 20); + }); + }, `Transfer ImageBitmap created from ${name}`); +} + +promise_test(async (t) => { + const url = get_host_info().REMOTE_ORIGIN + '/images/pattern.png'; + const image = await makeMakeHTMLImage(url)(); + const bitmap = await createImageBitmap(image); + + assert_throws_dom('DataCloneError', + () => worker.postMessage(bitmap, [bitmap])); +}, 'Transferring a non-origin-clean ImageBitmap throws.'); + +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-worker.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-worker.js new file mode 100644 index 0000000000..67a0904e47 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/createImageBitmap-worker.js @@ -0,0 +1,17 @@ +function makeBlob() { + return new Promise(function(resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", '/images/pattern.png'); + xhr.responseType = 'blob'; + xhr.send(); + xhr.onload = function() { + resolve(xhr.response); + }; + }); +} + +addEventListener("message", () => { + makeBlob().then(createImageBitmap).then(bitmap => { + postMessage(bitmap, [bitmap]); + }); +}); diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation-expected.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation-expected.html new file mode 100644 index 0000000000..64b1791afe --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation-expected.html @@ -0,0 +1,18 @@ +<html> +<body> + <canvas id="myCanvas" width="400" height="400"></canvas> +</body>> +<script> +var canvas = document.getElementById('myCanvas'); +ctx = canvas.getContext('2d'); +image = document.createElement("img"); +image.src = "../../../resources/black_white.png" +image.onload = function() { + Promise.all([ + createImageBitmap(image, { imageOrientation: 'flipY' }), + ]).then(function(sprites) { + // Draw image onto the canvas + ctx.drawImage(sprites[0], 0, 0); +}); +} +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation.html new file mode 100644 index 0000000000..e93c17c8e8 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmap-from-imageData-no-image-rotation.html @@ -0,0 +1,26 @@ +<html> + <link rel="match" href="imageBitmap-from-imageData-no-image-rotation-expected.html" /> + <style type="text/css"> + canvas { + image-orientation: none; + } + </style> +<body> + <canvas id="myCanvas" width="400" height="400"></canvas> +</body>> +<script> +var canvas = document.getElementById('myCanvas'); +ctx = canvas.getContext('2d'); +image = document.createElement("img"); +image.src = "../../../resources/black_white.png" +image.onload = function() { + Promise.all([ + // The image should be flipped and ignoring "image-orientation" setting + // in css style. + createImageBitmap(image, { imageOrientation: 'flipY' }), + ]).then(function(sprites) { + // Draw image onto the canvas + ctx.drawImage(sprites[0], 0, 0); +}); +} +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-expected.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-expected.html new file mode 100644 index 0000000000..bababda44c --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-expected.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<body> + <p>Test whether the imageOrientation "from-image" works when creating an ImageBitmap from the ImageData of a canvas, and then transfered to an ImageBitmapRenderingContext.</p> + <canvas id="canvas" width="300" height="300"></canvas> +</body> +<script> + +function drawSquares(ctx) { + ctx.fillStyle = 'red'; + ctx.fillRect(0,0,150,150); + ctx.fillStyle = 'green'; + ctx.fillRect(150,0,300,150); + ctx.fillStyle = 'blue'; + ctx.fillRect(0,150,150,300); +} + +async function runTest() { + const canvas = document.getElementById('canvas'); + canvas.width = 300; + canvas.height = 300; + const ctx = canvas.getContext('2d'); + drawSquares(ctx); +} + +runTest(); + +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped-expected.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped-expected.html new file mode 100644 index 0000000000..5e21671973 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped-expected.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<body> + <p>Test whether the imageOrientation "flipY" works when creating an ImageBitmap from the ImageData of a canvas, and then transfered to an ImageBitmapRenderingContext.</p> + <canvas id="canvas" width="300" height="300"></canvas> +</body> +<script> + +function drawSquares(ctx) { + ctx.fillStyle = 'red'; + ctx.fillRect(0,150,150,150); + ctx.fillStyle = 'green'; + ctx.fillRect(150,150,300,150); + ctx.fillStyle = 'blue'; + ctx.fillRect(0,0,150,150); +} + +async function runTest() { + const canvas = document.getElementById('canvas'); + canvas.width = 300; + canvas.height = 300; + const ctx = canvas.getContext('2d'); + drawSquares(ctx); +} + +runTest(); + +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped.html new file mode 100644 index 0000000000..02e0690876 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-flipped.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<link rel="match" href="imageBitmapRendering-transferFromImageBitmap-flipped-expected.html" /> +<body> + <p>Test whether the imageOrientation "flipY" works when creating an ImageBitmap from the ImageData of a canvas, and then transfered to an ImageBitmapRenderingContext.</p> + <canvas id="canvas" width="300" height="300"></canvas> +</body> +<script> + +function drawSquares(ctx) { + ctx.fillStyle = 'red'; + ctx.fillRect(0,0,150,150); + ctx.fillStyle = 'green'; + ctx.fillRect(150,0,300,150); + ctx.fillStyle = 'blue'; + ctx.fillRect(0,150,150,300); +} + +async function runTest() { + const canvas_temp = document.createElement('canvas'); + canvas_temp.width = 300; + canvas_temp.height = 300; + const ctx_temp = canvas_temp.getContext('2d'); + drawSquares(ctx_temp); + const imageSource = ctx_temp.getImageData(0, 0, 300, 300); + const imageOrientation = 'flipY'; + imageIDFlipped = await createImageBitmap(imageSource, 0, 0, 300, 300, { imageOrientation }); + const canvas = document.getElementById('canvas'); + const ctx = canvas.getContext('bitmaprenderer'); + ctx.transferFromImageBitmap(imageIDFlipped); +} + +runTest(); + +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl-expected.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl-expected.html new file mode 100644 index 0000000000..4f155316f0 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl-expected.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> + +<body> + <p> + Test creating an ImageBitmap from the transferToImageBitmap of a webgl OffscreenCanvas, and then + transferred to an ImageBitmapRenderingContext. + </p> + <canvas id="canvas" width="300" height="300"></canvas> +</body> +<script> + + function drawSquares(ctx) { + ctx.enable(ctx.SCISSOR_TEST); + ctx.scissor(0, 150, 150, 150); + ctx.clearColor(1, 0, 0, 1); + ctx.clear(ctx.COLOR_BUFFER_BIT); + ctx.scissor(150, 150, 300, 150); + ctx.clearColor(0, 1, 0, 1); + ctx.clear(ctx.COLOR_BUFFER_BIT); + ctx.scissor(0, 0, 150, 150); + ctx.clearColor(0, 0, 1, 1); + ctx.clear(ctx.COLOR_BUFFER_BIT); + } + + async function runTest() { + const canvas = document.getElementById('canvas'); + canvas.width = 300; + canvas.height = 300; + const ctx = canvas.getContext('webgl'); + drawSquares(ctx); + } + + runTest(); + +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl.html new file mode 100644 index 0000000000..049a3822cd --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap-webgl.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<link rel="match" href="imageBitmapRendering-transferFromImageBitmap-webgl-expected.html" /> + +<body> + <p> + Test creating an ImageBitmap from the transferToImageBitmap of a webgl OffscreenCanvas, and then + transferred to an ImageBitmapRenderingContext. + </p> + <canvas id="canvas" width="300" height="300"></canvas> +</body> +<script> + + function drawSquares(ctx) { + ctx.enable(ctx.SCISSOR_TEST); + ctx.scissor(0, 150, 150, 150); + ctx.clearColor(1, 0, 0, 1); + ctx.clear(ctx.COLOR_BUFFER_BIT); + ctx.scissor(150, 150, 300, 150); + ctx.clearColor(0, 1, 0, 1); + ctx.clear(ctx.COLOR_BUFFER_BIT); + ctx.scissor(0, 0, 150, 150); + ctx.clearColor(0, 0, 1, 1); + ctx.clear(ctx.COLOR_BUFFER_BIT); + } + + async function runTest() { + const offscreen = new OffscreenCanvas(300, 300); + const ctxOffscreen = offscreen.getContext('webgl'); + drawSquares(ctxOffscreen); + const image = offscreen.transferToImageBitmap(); + const canvas = document.getElementById('canvas'); + const ctx = canvas.getContext('bitmaprenderer'); + ctx.transferFromImageBitmap(image); + } + + runTest(); + +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap.html new file mode 100644 index 0000000000..6d3d886759 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imageBitmapRendering-transferFromImageBitmap.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<link rel="match" href="imageBitmapRendering-transferFromImageBitmap-expected.html" /> +<body> + <p>Test whether the imageOrientation "from-image" works when creating an ImageBitmap from the ImageData of a canvas, and then transfered to an ImageBitmapRenderingContext.</p> + <canvas id="canvas" width="300" height="300"></canvas> +</body> +<script> + +function drawSquares(ctx) { + ctx.fillStyle = 'red'; + ctx.fillRect(0,0,150,150); + ctx.fillStyle = 'green'; + ctx.fillRect(150,0,300,150); + ctx.fillStyle = 'blue'; + ctx.fillRect(0,150,150,300); +} + +async function runTest() { + const canvas_temp = document.createElement('canvas'); + canvas_temp.width = 300; + canvas_temp.height = 300; + const ctx_temp = canvas_temp.getContext('2d'); + drawSquares(ctx_temp); + const imageSource = ctx_temp.getImageData(0, 0, 300, 300); + const imageOrientation = 'from-image'; + imageIDFlipped = await createImageBitmap(imageSource, 0, 0, 300, 300, { imageOrientation }); + const canvas = document.getElementById('canvas'); + const ctx = canvas.getContext('bitmaprenderer'); + ctx.transferFromImageBitmap(imageIDFlipped); +} + +runTest(); + +</script> diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html new file mode 100644 index 0000000000..ab4331adef --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html @@ -0,0 +1,146 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Verify that image orientation is propagated when ImageBitmap objects are replicated.</title> +<link rel="author" title="Justin Novosad" href="mailto:junov@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> + +<body> +<script> +// This test is most relevant for browser implementations that apply EXIF image +// orientation lazily. That is to say that the transform is applied at rasterization +// time rather than at image decode time. This implies that image orientation metadata +// is stored internally in the decoded image's data structure. This test ensures +// that the orientation metadata is correctly carried over when ImageBitmap objects +// are replicated (serialized/deserialized, copied or transferred). + +function checkImageBitmapRotated(bitmap) { + assert_equals(bitmap.width, 320, 'Bitmap width'); + assert_equals(bitmap.height, 160, 'Bitmap height'); + + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + const expected_colors = [ + // row, col, r, g, b, a + [0, 0, 255, 0, 0, 255], + [0, 1, 0, 255, 0, 255], + [0, 2, 0, 0, 255, 255], + [0, 3, 0, 0, 0, 255], + [1, 0, 255, 128, 128, 255], + [1, 1, 128, 255, 128, 255], + [1, 2, 128, 128, 255, 255], + [1, 3, 128, 128, 128, 255], + ]; + + canvas.width = bitmap.width; + canvas.height = bitmap.height; + ctx.drawImage(bitmap, 0, 0); + + let data = ctx.getImageData(0, 0, canvas.width, canvas.height).data; + for (let [row, col, r, g, b, a] of expected_colors) { + let x = col * 80 + 40; + let y = row * 80 + 40; + let i = (x + y * canvas.width) * 4; + let expected = [r, g, b, a]; + let actual = [data[i], data[i + 1], data[i + 2], data[i + 3]]; + assert_array_approx_equals(actual, expected, 1, `Pixel value at (${x},${y}) ${expected} =~ ${actual}.`); + } +} + +promise_test(async t => { + const response = await fetch("resources/squares_6.jpg"); + const blob = await response.blob(); + const image = await createImageBitmap(blob) + const image_copy = structuredClone(image); + checkImageBitmapRotated(image_copy); +}, "ImageBitmap from file with EXIF rotation, duplicated via structuredClone"); + +promise_test(async t => { + const image = new Image(); + image.src = "resources/squares_6.jpg" + await new Promise(resolve => image.onload = resolve); + const image_copy = await createImageBitmap(image); + checkImageBitmapRotated(image_copy); +}, "ImageBitmap from file with EXIF rotation, loaded via <img>"); + +promise_test(async t => { + const image = new Image(); + image.src = "resources/squares_6.jpg" + // The following has no effect because the image's style is not + // processed unless the element is connected to the DOM. + image.style.imageOrientation = "none"; + await new Promise(resolve => image.onload = resolve); + const image_copy = await createImageBitmap(image); + checkImageBitmapRotated(image_copy); +}, "ImageBitmap from file with EXIF rotation, loaded via <img> not in DOM, imageOrientation = none"); + +promise_test(async t => { + const image = new Image(); + document.body.appendChild(image); + image.src = "resources/squares_6.jpg" + // The style is being processed in this case, but the imageOrientation + // CSS property must still have no effect because createImageBitmap + // accesses the element's underlying media directly, without being + // affected by the image's style (unlike drawImage). + image.style.imageOrientation = "none"; + await new Promise(resolve => image.onload = resolve); + const image_copy = await createImageBitmap(image); + checkImageBitmapRotated(image_copy); +}, "ImageBitmap from file with EXIF rotation, loaded via <img> in DOM, imageOrientation = none"); + + +promise_test(async t => { + const response = await fetch("resources/squares_6.jpg"); + const blob = await response.blob(); + const image = await createImageBitmap(blob); + const image_copy = await createImageBitmap(image); + checkImageBitmapRotated(image_copy); +}, "ImageBitmap from file with EXIF rotation, duplicated via createImageBitmap"); + +promise_test(async t => { + const worker = new Worker("serialize-worker.js"); + const response = await fetch("resources/squares_6.jpg"); + const blob = await response.blob() + const image = await createImageBitmap(blob); + worker.postMessage({bitmap: image}); + const bitmap = (await new Promise(resolve => {worker.addEventListener("message", resolve)})).data.bitmap; + checkImageBitmapRotated(bitmap); +}, "ImageBitmap from file with EXIF rotation, duplicated via worker serialization round-trip"); + +promise_test(async t => { + const worker = new Worker("transfer-worker.js"); + let response = await fetch("resources/squares_6.jpg"); + let blob = await response.blob(); + let image = await createImageBitmap(blob); + worker.postMessage({bitmap: image}, [image]); + const bitmap = (await new Promise(resolve => {worker.addEventListener("message", resolve)})).data.bitmap; + checkImageBitmapRotated(bitmap); +}, "ImageBitmap from file with EXIF rotation, duplicated via worker transfer round-trip"); + +promise_test(async t => { + // This test variant ensures additional code coverage. + // By creating a canvas pattern, a reference to the ImageBitmap's + // underlying pixel data is held in the source realm. This forces + // implementations that do lazy copying to duplicate the pixel + // data at transfer time. This test verifies that the lazy + // duplication code path (if applicable) carries over the image + // orientation metadata. + const worker = new Worker("transfer-worker.js"); + let response = await fetch("resources/squares_6.jpg"); + let blob = await response.blob(); + let image = await createImageBitmap(blob); + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + const pattern = ctx.createPattern(image, 'repeat'); + worker.postMessage({bitmap: image}, [image]); + const bitmap = (await new Promise(resolve => {worker.addEventListener("message", resolve)})).data.bitmap; + checkImageBitmapRotated(bitmap); +}, "ImageBitmap from file with EXIF rotation, duplicated via worker transfer round-trip, while referenced by a CanvasPattern"); + + +</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_1.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_1.jpg Binary files differnew file mode 100644 index 0000000000..0f0e8866b4 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_1.jpg diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_2.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_2.jpg Binary files differnew file mode 100644 index 0000000000..526f7a6c82 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_2.jpg diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_3.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_3.jpg Binary files differnew file mode 100644 index 0000000000..a21e521c2d --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_3.jpg diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_4.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_4.jpg Binary files differnew file mode 100644 index 0000000000..c4380b1e67 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_4.jpg diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_5.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_5.jpg Binary files differnew file mode 100644 index 0000000000..0bdd89aa1b --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_5.jpg diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_6.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_6.jpg Binary files differnew file mode 100644 index 0000000000..f197760a11 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_6.jpg diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_7.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_7.jpg Binary files differnew file mode 100644 index 0000000000..9b1a346888 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_7.jpg diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_8.jpg b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_8.jpg Binary files differnew file mode 100644 index 0000000000..41d2fbe7f0 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/resources/squares_8.jpg diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/serialize-worker.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/serialize-worker.js new file mode 100644 index 0000000000..a76537cc9e --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/serialize-worker.js @@ -0,0 +1,3 @@ +addEventListener('message', evt => { + postMessage(evt.data); +}); diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/transfer-worker.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/transfer-worker.js new file mode 100644 index 0000000000..55465a899c --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/transfer-worker.js @@ -0,0 +1,3 @@ +addEventListener('message', evt => { + postMessage(evt.data, [evt.data.bitmap]); +}); diff --git a/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/worker-onmessage-noop.js b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/worker-onmessage-noop.js new file mode 100644 index 0000000000..c0a352b4d9 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/manual/imagebitmap/worker-onmessage-noop.js @@ -0,0 +1,3 @@ +self.onmessage = function(e) { +}; + |