Skip to content

Commit

Permalink
Add more image files with multiple format for copyToTexture,image_fil…
Browse files Browse the repository at this point in the history
…e cases (#4135)

This PR added multiple format image file to test CopyEI2T handles
HTMLImageElements loaded from them.

The format including coded files: *.png, *.jpg, *.bmp, *.ico, *.gif
*.webp, and *.avif
and svg file: *.svg
  • Loading branch information
shaoboyan091 authored Jan 15, 2025
1 parent 8c3c0ff commit 62838f3
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 8 deletions.
Binary file added src/resources/four-colors.avif
Binary file not shown.
Binary file added src/resources/four-colors.bmp
Binary file not shown.
Binary file added src/resources/four-colors.gif
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.ico
Binary file not shown.
8 changes: 8 additions & 0 deletions src/resources/four-colors.svg
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.webp
Binary file not shown.
119 changes: 115 additions & 4 deletions src/webgpu/web_platform/copyToTexture/image_file.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import { makeTestGroup } from '../../../common/framework/test_group.js';
import { TextureUploadingUtils } from '../../util/copy_to_texture.js';
import {
convertToUnorm8,
GetSourceFromImageFile,
GetSourceFromEXIFImageFile,
kImageNames,
kImageInfo,
kImageExpectedColors,
kObjectTypeFromFiles,
kEXIFImageNames,
kEXIFImageInfo,
loadImageFileAndRun,
} from '../util.js';

export const g = makeTestGroup(TextureUploadingUtils);
Expand Down Expand Up @@ -38,7 +41,7 @@ g.test('from_orientation_metadata_file')
)
.params(u =>
u //
.combine('imageName', kImageNames)
.combine('imageName', kEXIFImageNames)
.combine('objectTypeFromFile', kObjectTypeFromFiles)
.combine('srcDoFlipYDuringCopy', [true, false])
)
Expand All @@ -47,7 +50,7 @@ g.test('from_orientation_metadata_file')
const kColorFormat = 'rgba8unorm';

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

Expand All @@ -72,7 +75,7 @@ g.test('from_orientation_metadata_file')
}
);

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

if (srcDoFlipYDuringCopy) {
Expand Down Expand Up @@ -123,3 +126,111 @@ g.test('from_orientation_metadata_file')
]);
}
});

g.test('from_multiple_formats')
.desc(
`
Test HTMLImageElements which loaded multiple image file formats. Including
*.jpg, *.png, *.bmp, *.webp, *.avif, *.svg, *.ico and *.gif.
It creates an 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 multiple image file format
- 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('srcDoFlipYDuringCopy', [true, false])
)
.fn(async t => {
const { imageName, srcDoFlipYDuringCopy } = t.params;
const kColorFormat = 'rgba8unorm';
await loadImageFileAndRun(t, imageName, (source: HTMLImageElement) => {
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]),
},
]);
}
});
});
65 changes: 61 additions & 4 deletions src/webgpu/web_platform/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ const kFourColorsInfo = {
},
} as const;

export const kImageInfo = makeTable({
export const kEXIFImageInfo = makeTable({
table: {
'four-colors.jpg': kFourColorsInfo,
'four-colors-rotate-90-cw.jpg': kFourColorsInfo,
Expand All @@ -635,9 +635,25 @@ export const kImageInfo = makeTable({
},
} as const);

export const kImageInfo = makeTable({
table: {
'four-colors.jpg': kFourColorsInfo,
'four-colors.png': kFourColorsInfo,
'four-colors.bmp': kFourColorsInfo,
'four-colors.webp': kFourColorsInfo,
'four-colors.gif': kFourColorsInfo,
'four-colors.avif': kFourColorsInfo,
'four-colors.ico': kFourColorsInfo,
'four-colors.svg': kFourColorsInfo,
},
} as const);

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

type EXIFImageName = keyof typeof kEXIFImageInfo;
export const kEXIFImageNames: readonly EXIFImageName[] = keysOf(kEXIFImageInfo);

type ObjectTypeFromFile = (typeof kObjectTypeFromFiles)[number];
export const kObjectTypeFromFiles = [
'ImageBitmap-from-Blob',
Expand All @@ -649,12 +665,12 @@ export const kObjectTypeFromFiles = [
* 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(
export async function GetSourceFromEXIFImageFile(
test: GPUTest,
imageName: ImageName,
exifImageName: EXIFImageName,
objectTypeFromFile: ObjectTypeFromFile
): Promise<ImageBitmap | HTMLImageElement> {
const imageUrl = getResourcePath(imageName);
const imageUrl = getResourcePath(exifImageName);

switch (objectTypeFromFile) {
case 'ImageBitmap-from-Blob': {
Expand Down Expand Up @@ -689,3 +705,44 @@ export async function GetSourceFromImageFile(
}
}
}

/**
* Create HTMLImageElement and load image file and waits for it to be loaded.
* Returns a promise which resolves after `callback` (which may be async) completes.
*
* @param imageName An valid imageName in kkImageInfo table .
* @param callback Function to call when HTMLImageElement is loaded.
*
*/
export function loadImageFileAndRun(
test: GPUTest,
imageName: ImageName,
callback: (image: HTMLImageElement) => unknown | Promise<unknown>
): Promise<void> {
return raceWithRejectOnTimeout(
new Promise((resolve, reject) => {
const callbackAndResolve = (image: HTMLImageElement) =>
void (async () => {
try {
await callback(image);
resolve();
} catch (ex) {
reject(ex);
}
})();
// 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.'
);
}
const image = new Image();
image.src = getResourcePath(imageName);
image.onload = () => {
callbackAndResolve(image);
};
}),
2000,
'Video never became ready'
);
}

0 comments on commit 62838f3

Please sign in to comment.