From f135486200d1dbf55eb3399bcde2e14ff0dea312 Mon Sep 17 00:00:00 2001 From: Echo Date: Tue, 21 Apr 2026 13:40:09 +0200 Subject: [PATCH] [Glitch] Remove animation detection in favour of never cropping GIFs Port 57c5d1c8dd50a02bc3a11af30b29fa8163fcdb73 to glitch-soc Signed-off-by: Claire --- .../account_edit/modals/image_upload.tsx | 73 ++++--------------- 1 file changed, 16 insertions(+), 57 deletions(-) diff --git a/app/javascript/flavours/glitch/features/account_edit/modals/image_upload.tsx b/app/javascript/flavours/glitch/features/account_edit/modals/image_upload.tsx index b8413eb0d9..3158ccc22f 100644 --- a/app/javascript/flavours/glitch/features/account_edit/modals/image_upload.tsx +++ b/app/javascript/flavours/glitch/features/account_edit/modals/image_upload.tsx @@ -65,16 +65,23 @@ export const ImageUploadModal: FC< const handleFile = useCallback((file: File) => { try { - parseImageFile(file, (result, isAnimated) => { - if (isAnimated) { - // If the image is animated, skip cropping and go straight to alt text. - setImageBlob(file); - setStep('alt'); - } else { - setImageSrc(result); - setStep('crop'); + // If the image is animated, skip cropping and go straight to alt text. + if (file.type === 'image/gif') { + setImageBlob(file); + setStep('alt'); + return; + } + + const reader = new FileReader(); + reader.onload = () => { + const dataUri = reader.result; + if (typeof dataUri !== 'string') { + throw new Error('Expected a string'); } - }); + setImageSrc(dataUri); + setStep('crop'); + }; + reader.readAsDataURL(file); } catch (error) { console.warn('Error with image parsing:', error); setStep('select'); @@ -408,54 +415,6 @@ const StepAlt: FC<{ ); }; -/** - * Parses an image file and determines if it's an animated GIF and returns a data URI for cropping. - * Based on https://gist.github.com/zakirt/faa4a58cec5a7505b10e3686a226f285. - */ -function parseImageFile( - file: File, - cb: (buffer: string, isAnimated: boolean) => void, -): void { - const reader = new FileReader(); - reader.onload = () => { - const buffer = reader.result; - if (!(buffer instanceof ArrayBuffer)) { - throw new Error('Expected an ArrayBuffer'); - } - - // Convert the ArrayBuffer to a base64 data URI. - const bytes = new Uint8Array(buffer); - const base64 = btoa(String.fromCharCode(...bytes)); - const dataUri = `data:${file.type};base64,${base64}`; - - // If the file type is not a GIF, then it's not animated as we don't support animated WebP or PNG. - if (file.type !== 'image/gif') { - cb(dataUri, false); - } - - const view = new DataView(buffer, 10); // Start from the last 4 bytes of the Logical Screen Descriptor. - let offset = 3; - - // Check the first bit for the global color table flag. - const globalColorTable = view.getInt8(0); - if (globalColorTable & 0x08) { - // Grab last three bits to calculate the global color table size, and skip it. - offset += 3 * Math.pow(2, (globalColorTable & 0x7) + 1); - } - - // Check Graphics Control Extension and Graphics Control Label to access animated data. - let delayTime = 0; - if (view.getUint8(offset) & 0x21 && view.getUint8(offset + 1) & 0xf9) { - // Skip to the delay time, which is stored in the next two bytes. - delayTime = view.getUint16(offset + 4); - } - - // If there is a delay time, the GIF is animated. - cb(dataUri, delayTime > 0); - }; - reader.readAsArrayBuffer(file); -} - async function calculateCroppedImage( imageSrc: string, crop: Area,