Add resolution downsizing to image compression

This commit is contained in:
Eric Bailey
2026-03-24 13:24:12 -05:00
parent 6a15ca88b6
commit 64bd3d903c
+27 -13
View File
@@ -204,20 +204,17 @@ export async function compressImage(
img: ComposerImage,
options?: {
highResolution?: boolean
attempts?: number
maxDimension?: number
},
): Promise<PickerImage> {
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)
}