fune/testing/web-platform/tests/webcodecs/image-decoder-utils.js
Dale Curtis 8e6db535b2 Bug 1778685 [wpt PR 34737] - [WebCodecs] Ensure orientation is set on YUV image decodes., a=testonly
Automatic update from web-platform-tests
[WebCodecs] Ensure orientation is set on YUV image decodes.

While the RGB decoding path was always setting the orientation, it
was overlooked on the YUV decoding path. As such, this change adds
code to set it and tests to ensure it doesn't regress.

Fixed: 1342672
Change-Id: I7b8f7f05502a040525cc84fc60f2dd12a42211c0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3751369
Reviewed-by: Thomas Guilbert <tguilbert@chromium.org>
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Auto-Submit: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1021792}

--

wpt-commits: 8016ebe321801a0a80e948b8dbb75b9cc0109ac5
wpt-pr: 34737
2022-07-13 15:05:51 +00:00

206 lines
7.2 KiB
JavaScript

const kYellow = 0xFFFF00FF;
const kRed = 0xFF0000FF;
const kBlue = 0x0000FFFF;
const kGreen = 0x00FF00FF;
function getColorName(color) {
switch (color) {
case kYellow:
return "Yellow";
case kRed:
return "Red";
case kBlue:
return "Blue";
case kGreen:
return "Green";
}
return "#" + color.toString(16);
}
function toUInt32(pixelArray, roundForYuv) {
let p = pixelArray.data;
// YUV to RGB conversion introduces some loss, so provide some leeway.
if (roundForYuv) {
const tolerance = 3;
for (var i = 0; i < p.length; ++i) {
if (p[i] >= 0xFF - tolerance)
p[i] = 0xFF;
if (p[i] <= 0x00 + tolerance)
p[i] = 0x00;
}
}
return ((p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]) >>> 0;
}
function flipMatrix(m) {
return m.map(row => row.reverse());
}
function rotateMatrix(m, count) {
for (var i = 0; i < count; ++i)
m = m[0].map((val, index) => m.map(row => row[index]).reverse());
return m;
}
function testFourColorsDecodeBuffer(buffer, mimeType, options = {}) {
var decoder = new ImageDecoder(
{data: buffer, type: mimeType, preferAnimation: options.preferAnimation});
return decoder.decode().then(result => {
assert_equals(result.image.displayWidth, 320);
assert_equals(result.image.displayHeight, 240);
if (options.preferAnimation !== undefined) {
assert_greater_than(decoder.tracks.length, 1);
assert_equals(
options.preferAnimation, decoder.tracks.selectedTrack.animated);
}
if (options.yuvFormat !== undefined)
assert_equals(result.image.format, options.yuvFormat);
if (options.tolerance === undefined)
options.tolerance = 0;
let canvas = new OffscreenCanvas(
result.image.displayWidth, result.image.displayHeight);
let ctx = canvas.getContext('2d');
ctx.drawImage(result.image, 0, 0);
let top_left = ctx.getImageData(0, 0, 1, 1);
let top_right = ctx.getImageData(result.image.displayWidth - 1, 0, 1, 1);
let bottom_left = ctx.getImageData(0, result.image.displayHeight - 1, 1, 1);
let left_corner = ctx.getImageData(
result.image.displayWidth - 1, result.image.displayHeight - 1, 1, 1);
assert_array_approx_equals(
top_left.data, [0xFF, 0xFF, 0x00, 0xFF], options.tolerance,
'top left corner is yellow');
assert_array_approx_equals(
top_right.data, [0xFF, 0x00, 0x00, 0xFF], options.tolerance,
'top right corner is red');
assert_array_approx_equals(
bottom_left.data, [0x00, 0x00, 0xFF, 0xFF], options.tolerance,
'bottom left corner is blue');
assert_array_approx_equals(
left_corner.data, [0x00, 0xFF, 0x00, 0xFF], options.tolerance,
'bottom right corner is green');
});
}
function testFourColorDecodeWithExifOrientation(orientation, canvas, useYuv) {
return ImageDecoder.isTypeSupported('image/jpeg').then(support => {
assert_implements_optional(
support, 'Optional codec image/jpeg not supported.');
const testFile =
useYuv ? 'four-colors-limited-range-420-8bpc.jpg' : 'four-colors.jpg';
return fetch(testFile)
.then(response => {
return response.arrayBuffer();
})
.then(buffer => {
let u8buffer = new Uint8Array(buffer);
u8buffer[useYuv ? 0x31 : 0x1F] =
orientation; // Location derived via diff.
let decoder = new ImageDecoder({data: u8buffer, type: 'image/jpeg'});
return decoder.decode();
})
.then(result => {
let respectOrientation = true;
if (canvas)
respectOrientation = canvas.style.imageOrientation != 'none';
let expectedWidth = 320;
let expectedHeight = 240;
if (orientation > 4 && respectOrientation)
[expectedWidth, expectedHeight] = [expectedHeight, expectedWidth];
if (respectOrientation) {
assert_equals(result.image.displayWidth, expectedWidth);
assert_equals(result.image.displayHeight, expectedHeight);
} else if (orientation > 4) {
assert_equals(result.image.displayHeight, expectedWidth);
assert_equals(result.image.displayWidth, expectedHeight);
}
if (!canvas) {
canvas = new OffscreenCanvas(
result.image.displayWidth, result.image.displayHeight);
} else {
canvas.width = expectedWidth;
canvas.height = expectedHeight;
}
let ctx = canvas.getContext('2d');
ctx.drawImage(result.image, 0, 0);
let matrix = [
[kYellow, kRed],
[kBlue, kGreen],
];
if (respectOrientation) {
switch (orientation) {
case 1: // kOriginTopLeft, default
break;
case 2: // kOriginTopRight, mirror along y-axis
matrix = flipMatrix(matrix);
break;
case 3: // kOriginBottomRight, 180 degree rotation
matrix = rotateMatrix(matrix, 2);
break;
case 4: // kOriginBottomLeft, mirror along the x-axis
matrix = flipMatrix(rotateMatrix(matrix, 2));
break;
case 5: // kOriginLeftTop, mirror along x-axis + 270 degree CW
// rotation
matrix = flipMatrix(rotateMatrix(matrix, 1));
break;
case 6: // kOriginRightTop, 90 degree CW rotation
matrix = rotateMatrix(matrix, 1);
break;
case 7: // kOriginRightBottom, mirror along x-axis + 90 degree CW
// rotation
matrix = flipMatrix(rotateMatrix(matrix, 3));
break;
case 8: // kOriginLeftBottom, 270 degree CW rotation
matrix = rotateMatrix(matrix, 3);
break;
default:
assert_between_inclusive(
orientation, 1, 8, 'unknown image orientation');
break;
};
}
verifyFourColorsImage(
expectedWidth, expectedHeight, ctx, matrix, useYuv);
});
});
}
function verifyFourColorsImage(width, height, ctx, matrix, isYuv) {
if (!matrix) {
matrix = [
[kYellow, kRed],
[kBlue, kGreen],
];
}
let expectedTopLeft = matrix[0][0];
let expectedTopRight = matrix[0][1];
let expectedBottomLeft = matrix[1][0];
let expectedBottomRight = matrix[1][1];
let topLeft = toUInt32(ctx.getImageData(0, 0, 1, 1), isYuv);
let topRight = toUInt32(ctx.getImageData(width - 1, 0, 1, 1), isYuv);
let bottomLeft = toUInt32(ctx.getImageData(0, height - 1, 1, 1), isYuv);
let bottomRight =
toUInt32(ctx.getImageData(width - 1, height - 1, 1, 1), isYuv);
assert_equals(getColorName(topLeft), getColorName(expectedTopLeft),
'top left corner');
assert_equals(getColorName(topRight), getColorName(expectedTopRight),
'top right corner');
assert_equals(getColorName(bottomLeft), getColorName(expectedBottomLeft),
'bottom left corner');
assert_equals(getColorName(bottomRight), getColorName(expectedBottomRight),
'bottom right corner');
}