From ca56c2e091da8a330511563240fb6cbdf33406ae Mon Sep 17 00:00:00 2001 From: Peter Smit <43498358+Petersmit27@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:32:41 +0200 Subject: [PATCH] Fix some webp images improperly marked as animated (#29713) * Fix some webp images improperly marked as animated * Add unit test for an unanimated webp file in extended file format * Apply linting to webp test --- src/utils/Image.ts | 4 ++-- test/unit-tests/Image-test.ts | 7 +++++++ .../images/static-logo-extended-file-format.webp | Bin 0 -> 734 bytes 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 test/unit-tests/images/static-logo-extended-file-format.webp diff --git a/src/utils/Image.ts b/src/utils/Image.ts index eed70bdf32..ab7c67d477 100644 --- a/src/utils/Image.ts +++ b/src/utils/Image.ts @@ -31,13 +31,13 @@ export async function blobIsAnimated(mimeType: string | undefined, blob: Blob): case "image/webp": { // Only extended file format WEBP images support animation, so grab the expected data range and verify header. // Based on https://developers.google.com/speed/webp/docs/riff_container#extended_file_format - const arr = await blob.slice(0, 17).arrayBuffer(); + const arr = await blob.slice(0, 21).arrayBuffer(); if ( arrayBufferReadStr(arr, 0, 4) === "RIFF" && arrayBufferReadStr(arr, 8, 4) === "WEBP" && arrayBufferReadStr(arr, 12, 4) === "VP8X" ) { - const [flags] = arrayBufferRead(arr, 16, 1); + const [flags] = arrayBufferRead(arr, 20, 1); // Flags: R R I L E X _A_ R (reversed) const animationFlagMask = 1 << 1; return (flags & animationFlagMask) != 0; diff --git a/test/unit-tests/Image-test.ts b/test/unit-tests/Image-test.ts index 149ee0ff26..966236bc8a 100644 --- a/test/unit-tests/Image-test.ts +++ b/test/unit-tests/Image-test.ts @@ -51,6 +51,13 @@ describe("Image", () => { expect(await blobIsAnimated("image/webp", img)).toBeFalsy(); }); + it("Static WEBP in extended file format", async () => { + const img = new Blob([ + fs.readFileSync(path.resolve(__dirname, "images", "static-logo-extended-file-format.webp")), + ]); + expect(await blobIsAnimated("image/webp", img)).toBeFalsy(); + }); + it("Animated PNG", async () => { const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "animated-logo.apng"))]); expect(await blobIsAnimated("image/png", img)).toBeTruthy(); diff --git a/test/unit-tests/images/static-logo-extended-file-format.webp b/test/unit-tests/images/static-logo-extended-file-format.webp new file mode 100644 index 0000000000000000000000000000000000000000..bb4364374b4e2a665986842946b280c6b1b5c6be GIT binary patch literal 734 zcmV<40wMiUNk&H20ssJ4MM6+kP&il$0000G0002T0074T06|PpNQnUe00EE~Ns=N- zDQpU|n#VmfcU8@CL`;Bw{r|;3bDum=5pVZ;F~1PBQ?!)Z2uIbvhp7T(kf;K|fi6lN zTm@4F-W^m3-Wyy7;WF@3A{BxkyS!dciNk=~373KQ2A4s&3_M;y3HY}HfF#lf=)dGo z|0PTOI|X%;A(|6#2aq2?62Oqr8Hx)1e+KT+UW9}j8Z*k2Q7;2HRj8K%qft^rpF|fP zAyaf2WeN@U;Y=|k(J~sxiP10}%rI$Tf<#Lb*+)1UPoTkJCKjWqT!CQl6jn&&#Aq&F zKvI}xBpID09Ev9M56786m?m)pQb4*?1db5Si04VnGx;!D$O85ej)Lo%FmyYjV2wl- zxPc16qhho=O)Mi%K1FyRi8~mrFgnI;#3fk6K|E0kL-=}}B)pMHjoFCY5RFk0NP%z_ zOch9g7D#L)oEf$k@EyVCPf4U-Y|)=J zGoCTpx@b6b>bA_k5^dc$9{u|N`*Z+SP&gna0RRAy8UURED#!rH06sAkh(e+vAs3(I z03ZVdw6}1$n2q`E_yhO{;0Ndj@DJbK{|LIzIJaH7e80Rqg=(4HCpn3S?#N>Wf0HhE zRC9#E2CATk)e*&MG@yH4b|wIDE+9U?+7>^9t3hqW=H&(clnUN|MsGV{z=eZ$vI08|NW`ZZ^=rjU;qBp Q<@fSV6d#j@eo4Rp0BUbTKL7v# literal 0 HcmV?d00001