On the ImageDecoder interface repetitionCount being 0 mean one total play through, see https://github.com/w3c/webcodecs/issues/932 On the jpeg xl side, num_loops being 0 means to loop infinitely https://github.com/libjxl/libjxl/blob/6553831b39a542d6450e76ca5afb64520649d838/lib/jxl/headers.h#L87 and then I confirmed with Jon Sneyers that num_loops being 1 means to have one total play through. (If num_loops being 1 meant two total play throughs then there would be no way to specify one total play through, so that is good.) There doesn't appear to be any tools that can make finitely looping animated jxl files at this time, so a c file using libjxl was created to create them. Differential Revision: https://phabricator.services.mozilla.com/D295458
71 lines
2.7 KiB
HTML
71 lines
2.7 KiB
HTML
<!DOCTYPE html>
|
|
<title>JPEG XL: animation via ImageDecoder</title>
|
|
<link rel="help" href="https://github.com/web-platform-tests/interop-jpegxl">
|
|
<meta name="assert" content="ImageDecoder reports animation track, decodes multiple frames, and reflects repetition count.">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="resources/helpers.js"></script>
|
|
<script>
|
|
|
|
async function test_animated_jxl(filename, { repetitionCount, frameCount, cycleDurationUs }) {
|
|
assert_implements('ImageDecoder' in self, 'ImageDecoder should exist');
|
|
const supported = await ImageDecoder.isTypeSupported('image/jxl');
|
|
assert_true(supported, 'image/jxl should be supported');
|
|
|
|
const response = await fetch(filename);
|
|
const decoder = new ImageDecoder({data: response.body, type: 'image/jxl'});
|
|
await decoder.tracks.ready;
|
|
|
|
const track = decoder.tracks.selectedTrack;
|
|
assert_true(track.animated, 'track must be animated');
|
|
|
|
if (repetitionCount !== undefined) {
|
|
assert_equals(track.repetitionCount, repetitionCount,
|
|
'repetitionCount must match');
|
|
}
|
|
|
|
await decoder.completed;
|
|
|
|
const frame_count = track.frameCount;
|
|
assert_equals(frame_count, frameCount,
|
|
`must have exactly ${frameCount} frames`);
|
|
|
|
let cycle_duration_us = 0;
|
|
let prev_timestamp = null;
|
|
for (let i = 0; i < frame_count; i++) {
|
|
const result = await decoder.decode({frameIndex: i});
|
|
cycle_duration_us += result.image.duration;
|
|
if (prev_timestamp !== null) {
|
|
assert_less_than(prev_timestamp, result.image.timestamp,
|
|
'timestamps should increase');
|
|
}
|
|
prev_timestamp = result.image.timestamp;
|
|
result.image.close();
|
|
}
|
|
decoder.close();
|
|
|
|
assert_equals(cycle_duration_us, cycleDurationUs, 'cycle duration must match');
|
|
}
|
|
|
|
promise_test(async () => {
|
|
await test_animated_jxl('resources/conformance_animation_spline.jxl',
|
|
{ repetitionCount: Infinity, frameCount: 60, cycleDurationUs: 1200000 });
|
|
}, 'Animated JPEG XL reports multiple frames and decodes frame sequence.');
|
|
|
|
promise_test(async t => {
|
|
await test_animated_jxl('resources/animation-1loop.jxl',
|
|
{ repetitionCount: 0, frameCount: 4, cycleDurationUs: 2000000 });
|
|
}, 'Animated JXL with repetition count 0 plays exactly once.');
|
|
|
|
promise_test(async t => {
|
|
await test_animated_jxl('resources/animation-2loops.jxl',
|
|
{ repetitionCount: 1, frameCount: 4, cycleDurationUs: 2000000 });
|
|
}, 'Animated JXL with repetition count 1 plays exactly twice.');
|
|
|
|
promise_test(async t => {
|
|
await test_animated_jxl('resources/animation-infiniteloop.jxl',
|
|
{ repetitionCount: Infinity, frameCount: 4, cycleDurationUs: 2000000 });
|
|
}, 'Animated JXL with infinite repetition count repeats indefinitely.');
|
|
|
|
</script>
|