From 64bd3d903c23b760925a060aa5f772176bd497bd Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 24 Mar 2026 13:24:12 -0500 Subject: [PATCH] Add resolution downsizing to image compression --- src/state/gallery.ts | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/state/gallery.ts b/src/state/gallery.ts index d6977817a2..4ff3f1f44a 100644 --- a/src/state/gallery.ts +++ b/src/state/gallery.ts @@ -204,20 +204,17 @@ export async function compressImage( img: ComposerImage, options?: { highResolution?: boolean + attempts?: number + maxDimension?: number }, ): Promise { const source = img.transformed || img.source + const highResolution = options?.highResolution ?? false + const attempts = options?.attempts || 0 + const maxDimension = + options?.maxDimension || (highResolution ? 4000 : POST_IMG_MAX.width) - const [w, h] = containImageRes( - source.width, - source.height, - options?.highResolution - ? { - width: 4000, - height: 4000, - } - : POST_IMG_MAX, - ) + const [w, h] = containImageRes(source.width, source.height, maxDimension) let minQualityPercentage = 0 let maxQualityPercentage = 101 // exclusive @@ -228,6 +225,15 @@ export async function compressImage( (maxQualityPercentage + minQualityPercentage) / 2, ) + /* + * In the event the image doesn't compress well, we want to avoid + * unecessary iterations. In this case, binary search will check 51, 26, + * 13(rounded). We don't want to go below 25, hence the break here at 13, + * which will result in a subsequent attempt with a smaller maxDimension + * and reset qualityPercentage to 100. + */ + if (qualityPercentage <= 13) break + const res = await manipulateAsync( source.path, [{resize: {width: w, height: h}}], @@ -258,6 +264,14 @@ export async function compressImage( return newDataUri } + if (attempts < 4) { + return compressImage(img, { + highResolution, + attempts: attempts + 1, + maxDimension: maxDimension * 0.8, + }) + } + throw new Error(`Unable to compress image`) } @@ -366,12 +380,12 @@ function joinPath(a: string, b: string) { function containImageRes( w: number, h: number, - {width: maxW, height: maxH}: {width: number; height: number}, + max: number, ): [width: number, height: number] { let scale = 1 - if (w > maxW || h > maxH) { - scale = w > h ? maxW / w : maxH / h + if (w > max || h > max) { + scale = w > h ? max / w : max / h w = Math.floor(w * scale) h = Math.floor(h * scale) }