1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 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>
|