Skip to content

Commit

Permalink
CopyEI2T: Cases To Cover Rotated Images (#4111)
Browse files Browse the repository at this point in the history
Co-authored-by: Kai Ninomiya <kainino@chromium.org>
  • Loading branch information
shaoboyan091 and kainino0x authored Jan 7, 2025
1 parent e99550b commit 10b66cd
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/resources/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,25 @@ ffmpeg -display_vflip -i temp.mp4 -c copy four-colors-vp9-bt601-vflip.mp4
rm temp.mp4
```

The test jpg files were generated with by exiftool cmds and other image tools in below steps:
```
// Generate four-colors.jpg with no orientation metadata
Use a image tool (e.g. "Paint" app on Windows) to create four-colors.jpg from four-colors.png and check with exiftool to ensure no orientation metadata been set.
// Generate jpg picture with 90 cw rotation metadata
Use a image tool (e.g. "Paint" app on Windows) to create four-colors-hard-rotate-90-ccw.jpg and check with exiftool to ensure no orientation metadata been set.
exiftool -Orientation#=6 four-colors-hard-rotate-90-ccw.jpg -o four-colors-rotate-90-cw.jpg
rm four-colors-hard-rotate-90-ccw.jpg
// Generate jpg picture with 180 cw rotation metadata
Use a image tool (e.g. "Paint" app on Windows) to create four-colors-hard-rotate-180-ccw.jpg and check with exiftool to ensure no orientation metadata been set.
exiftool -Orientation#=3 four-colors-hard-rotate-180-ccw.jpg -o four-colors-rotate-180-cw.jpg
rm four-colors-hard-rotate-180-ccw.jpg
// Generate jpg picture with 270 cw rotation metadata
Use a image tool (e.g. "Paint" app on Windows) to create four-colors-hard-rotate-270-ccw.jpg and check with exiftool to ensure no orientation metadata been set.
exiftool -Orientation#=8 four-colors-hard-rotate-270-ccw.jpg -o four-colors-rotate-270-cw.jpg
rm four-colors-hard-rotate-270-ccw.jpg
```
Binary file added src/resources/four-colors-rotate-180-cw.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/resources/four-colors-rotate-270-cw.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/resources/four-colors-rotate-90-cw.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/resources/four-colors.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
125 changes: 125 additions & 0 deletions src/webgpu/web_platform/copyToTexture/image_file.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
export const description = `
copyExternalImageToTexture from ImageFiles like *.png, *.jpg source.
`;

import { makeTestGroup } from '../../../common/framework/test_group.js';
import { TextureUploadingUtils } from '../../util/copy_to_texture.js';
import {
convertToUnorm8,
GetSourceFromImageFile,
kImageNames,
kImageInfo,
kImageExpectedColors,
kObjectTypeFromFiles,
} from '../util.js';

export const g = makeTestGroup(TextureUploadingUtils);

g.test('from_orientation_metadata_file')
.desc(
`
Test HTMLImageElements with rotation metadata can be copied to WebGPU texture correctly.
It creates an ImageBitmap or HTMLImageElement using images in the 'resources' folder.
Then call copyExternalImageToTexture() to do a full copy to the 0 mipLevel
of dst texture, and read one pixel out to compare with the manually documented expected color.
If 'flipY' in 'GPUCopyExternalImageSourceInfo' is set to 'true', copy will ensure the result
is flipped.
The tests covers:
- Image with rotation metadata
- Valid 'flipY' config in 'GPUCopyExternalImageSourceInfo' (named 'srcDoFlipYDuringCopy' in cases)
- TODO: partial copy tests should be added
- TODO: all valid dstColorFormat tests should be added.
- TODO(#4108): Make this work in service workers (see GetSourceFromImageFile)
`
)
.params(u =>
u //
.combine('imageName', kImageNames)
.combine('objectTypeFromFile', kObjectTypeFromFiles)
.combine('srcDoFlipYDuringCopy', [true, false])
)
.fn(async t => {
const { imageName, objectTypeFromFile, srcDoFlipYDuringCopy } = t.params;
const kColorFormat = 'rgba8unorm';

// Load image file.
const source = await GetSourceFromImageFile(t, imageName, objectTypeFromFile);
const width = source.width;
const height = source.height;

const dstTexture = t.createTextureTracked({
size: { width, height },
format: kColorFormat,
usage:
GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
});

t.device.queue.copyExternalImageToTexture(
{
source,
flipY: srcDoFlipYDuringCopy,
},
{
texture: dstTexture,
},
{
width,
height,
}
);

const expect = kImageInfo[imageName].display;
const presentColors = kImageExpectedColors.srgb;

if (srcDoFlipYDuringCopy) {
t.expectSinglePixelComparisonsAreOkInTexture({ texture: dstTexture }, [
// Flipped top-left.
{
coord: { x: width * 0.25, y: height * 0.25 },
exp: convertToUnorm8(presentColors[expect.bottomLeftColor]),
},
// Flipped top-right.
{
coord: { x: width * 0.75, y: height * 0.25 },
exp: convertToUnorm8(presentColors[expect.bottomRightColor]),
},
// Flipped bottom-left.
{
coord: { x: width * 0.25, y: height * 0.75 },
exp: convertToUnorm8(presentColors[expect.topLeftColor]),
},
// Flipped bottom-right.
{
coord: { x: width * 0.75, y: height * 0.75 },
exp: convertToUnorm8(presentColors[expect.topRightColor]),
},
]);
} else {
t.expectSinglePixelComparisonsAreOkInTexture({ texture: dstTexture }, [
// Top-left.
{
coord: { x: width * 0.25, y: height * 0.25 },
exp: convertToUnorm8(presentColors[expect.topLeftColor]),
},
// Top-right.
{
coord: { x: width * 0.75, y: height * 0.25 },
exp: convertToUnorm8(presentColors[expect.topRightColor]),
},
// Bottom-left.
{
coord: { x: width * 0.25, y: height * 0.75 },
exp: convertToUnorm8(presentColors[expect.bottomLeftColor]),
},
// Bottom-right.
{
coord: { x: width * 0.75, y: height * 0.75 },
exp: convertToUnorm8(presentColors[expect.bottomRightColor]),
},
]);
}
});
83 changes: 83 additions & 0 deletions src/webgpu/web_platform/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ export const kVideoExpectedColors = makeTable({
},
} as const);

export const kImageExpectedColors = {
srgb: {
red: { R: 1.0, G: 0.0, B: 0.0, A: 1.0 },
green: { R: 0.0, G: 1.0, B: 0.0, A: 1.0 },
blue: { R: 0.0, G: 0.0, B: 1.0, A: 1.0 },
yellow: { R: 1.0, G: 1.0, B: 0.0, A: 1.0 },
},
} as const;

// MAINTENANCE_TODO: Add BT.2020 video in table.
// Video container and codec defines several transform ops to apply to raw decoded frame to display.
// Our test cases covers 'visible rect' and 'rotation'.
Expand Down Expand Up @@ -350,6 +359,7 @@ type VideoName = keyof typeof kVideoInfo;
export const kVideoNames: readonly VideoName[] = keysOf(kVideoInfo);

export const kPredefinedColorSpace = ['display-p3', 'srgb'] as const;

/**
* Starts playing a video and waits for it to be consumable.
* Returns a promise which resolves after `callback` (which may be async) completes.
Expand Down Expand Up @@ -606,3 +616,76 @@ export async function captureCameraFrame(test: GPUTest): Promise<VideoFrame> {

return frame;
}

const kFourColorsInfo = {
display: {
topLeftColor: 'yellow',
topRightColor: 'red',
bottomLeftColor: 'blue',
bottomRightColor: 'green',
},
} as const;

export const kImageInfo = makeTable({
table: {
'four-colors.jpg': kFourColorsInfo,
'four-colors-rotate-90-cw.jpg': kFourColorsInfo,
'four-colors-rotate-180-cw.jpg': kFourColorsInfo,
'four-colors-rotate-270-cw.jpg': kFourColorsInfo,
},
} as const);

type ImageName = keyof typeof kImageInfo;
export const kImageNames: readonly ImageName[] = keysOf(kImageInfo);

type ObjectTypeFromFile = (typeof kObjectTypeFromFiles)[number];
export const kObjectTypeFromFiles = [
'ImageBitmap-from-Blob',
'ImageBitmap-from-Image',
'Image',
] as const;

/**
* Load image file(e.g. *.jpg) from ImageBitmap, blob or HTMLImageElement. And
* convert the result to valid source that GPUCopyExternalImageSource supported.
*/
export async function GetSourceFromImageFile(
test: GPUTest,
imageName: ImageName,
objectTypeFromFile: ObjectTypeFromFile
): Promise<ImageBitmap | HTMLImageElement> {
const imageUrl = getResourcePath(imageName);

switch (objectTypeFromFile) {
case 'ImageBitmap-from-Blob': {
// MAINTENANCE_TODO: resource folder path when using service worker is not correct. Return
// the correct path to load resource in correct place.
// The wrong path: /out/webgpu/webworker/web_platform/copyToTexture/resources
if (globalThis.constructor.name === 'ServiceWorkerGlobalScope') {
test.skip('Try to load image resource from serivce worker but the path is not correct.');
}
// Load image file through fetch.
const response = await fetch(imageUrl);
return createImageBitmap(await response.blob());
}
case 'ImageBitmap-from-Image':
case 'Image': {
// Skip test if HTMLImageElement is not available, e.g. in worker.
if (typeof HTMLImageElement === 'undefined') {
test.skip(
'Try to use HTMLImage do image file decoding but HTMLImageElement not available.'
);
}

// Load image file through HTMLImageElement.
const image = new Image();
image.src = imageUrl;
await raceWithRejectOnTimeout(image.decode(), 5000, 'decode image timeout');
if (objectTypeFromFile === 'Image') {
return image;
}

return createImageBitmap(image);
}
}
}

0 comments on commit 10b66cd

Please sign in to comment.