Optimize compressImage method to also use binary search (#7490)
* use binary search for compressImage * use binary search for doResize (mobile version) * use binary search for doResize (web version) * use safeDeleteAsync in compressImage --------- Co-authored-by: nguyen <nguyensvo123@gmail.com>
This commit is contained in:
+20
-7
@@ -178,15 +178,20 @@ async function doResize(localUri: string, opts: DoResizeOpts): Promise<Image> {
|
||||
height: imageRes.height,
|
||||
})
|
||||
|
||||
for (let i = 0; i < 9; i++) {
|
||||
// nearest 10th
|
||||
const quality = Math.round((1 - 0.1 * i) * 10) / 10
|
||||
let minQualityPercentage = 0
|
||||
let maxQualityPercentage = 101 // exclusive
|
||||
let newDataUri
|
||||
|
||||
while (maxQualityPercentage - minQualityPercentage > 1) {
|
||||
const qualityPercentage = Math.round(
|
||||
(maxQualityPercentage + minQualityPercentage) / 2,
|
||||
)
|
||||
const resizeRes = await manipulateAsync(
|
||||
localUri,
|
||||
[{resize: newDimensions}],
|
||||
{
|
||||
format: SaveFormat.JPEG,
|
||||
compress: quality,
|
||||
compress: qualityPercentage / 100,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -198,8 +203,8 @@ async function doResize(localUri: string, opts: DoResizeOpts): Promise<Image> {
|
||||
}
|
||||
|
||||
if (fileInfo.size < opts.maxSize) {
|
||||
safeDeleteAsync(imageRes.uri)
|
||||
return {
|
||||
minQualityPercentage = qualityPercentage
|
||||
newDataUri = {
|
||||
path: normalizePath(resizeRes.uri),
|
||||
mime: 'image/jpeg',
|
||||
size: fileInfo.size,
|
||||
@@ -207,9 +212,17 @@ async function doResize(localUri: string, opts: DoResizeOpts): Promise<Image> {
|
||||
height: resizeRes.height,
|
||||
}
|
||||
} else {
|
||||
safeDeleteAsync(resizeRes.uri)
|
||||
maxQualityPercentage = qualityPercentage
|
||||
}
|
||||
|
||||
safeDeleteAsync(resizeRes.uri)
|
||||
}
|
||||
|
||||
if (newDataUri) {
|
||||
safeDeleteAsync(imageRes.uri)
|
||||
return newDataUri
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`This image is too big! We couldn't compress it down to ${opts.maxSize} bytes`,
|
||||
)
|
||||
|
||||
@@ -72,17 +72,28 @@ interface DoResizeOpts {
|
||||
async function doResize(dataUri: string, opts: DoResizeOpts): Promise<RNImage> {
|
||||
let newDataUri
|
||||
|
||||
for (let i = 0; i <= 10; i++) {
|
||||
newDataUri = await createResizedImage(dataUri, {
|
||||
let minQualityPercentage = 0
|
||||
let maxQualityPercentage = 101 //exclusive
|
||||
|
||||
while (maxQualityPercentage - minQualityPercentage > 1) {
|
||||
const qualityPercentage = Math.round(
|
||||
(maxQualityPercentage + minQualityPercentage) / 2,
|
||||
)
|
||||
const tempDataUri = await createResizedImage(dataUri, {
|
||||
width: opts.width,
|
||||
height: opts.height,
|
||||
quality: 1 - i * 0.1,
|
||||
quality: qualityPercentage / 100,
|
||||
mode: opts.mode,
|
||||
})
|
||||
if (getDataUriSize(newDataUri) < opts.maxSize) {
|
||||
break
|
||||
|
||||
if (getDataUriSize(tempDataUri) < opts.maxSize) {
|
||||
minQualityPercentage = qualityPercentage
|
||||
newDataUri = tempDataUri
|
||||
} else {
|
||||
maxQualityPercentage = qualityPercentage
|
||||
}
|
||||
}
|
||||
|
||||
if (!newDataUri) {
|
||||
throw new Error('Failed to compress image')
|
||||
}
|
||||
|
||||
+20
-9
@@ -13,7 +13,7 @@ import {
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
|
||||
import {POST_IMG_MAX} from '#/lib/constants'
|
||||
import {getImageDim} from '#/lib/media/manip'
|
||||
import {getImageDim, safeDeleteAsync} from '#/lib/media/manip'
|
||||
import {openCropper} from '#/lib/media/picker'
|
||||
import {getDataUriSize} from '#/lib/media/util'
|
||||
import {isIOS, isNative} from '#/platform/detection'
|
||||
@@ -212,15 +212,20 @@ export async function compressImage(img: ComposerImage): Promise<ImageMeta> {
|
||||
const [w, h] = containImageRes(source.width, source.height, POST_IMG_MAX)
|
||||
const cacheDir = isNative && getImageCacheDirectory()
|
||||
|
||||
for (let i = 10; i > 0; i--) {
|
||||
// Float precision
|
||||
const factor = i / 10
|
||||
let minQualityPercentage = 0
|
||||
let maxQualityPercentage = 101 // exclusive
|
||||
let newDataUri
|
||||
|
||||
while (maxQualityPercentage - minQualityPercentage > 1) {
|
||||
const qualityPercentage = Math.round(
|
||||
(maxQualityPercentage + minQualityPercentage) / 2,
|
||||
)
|
||||
|
||||
const res = await manipulateAsync(
|
||||
source.path,
|
||||
[{resize: {width: w, height: h}}],
|
||||
{
|
||||
compress: factor,
|
||||
compress: qualityPercentage / 100,
|
||||
format: SaveFormat.JPEG,
|
||||
base64: true,
|
||||
},
|
||||
@@ -229,17 +234,23 @@ export async function compressImage(img: ComposerImage): Promise<ImageMeta> {
|
||||
const base64 = res.base64
|
||||
|
||||
if (base64 !== undefined && getDataUriSize(base64) <= POST_IMG_MAX.size) {
|
||||
return {
|
||||
minQualityPercentage = qualityPercentage
|
||||
newDataUri = {
|
||||
path: await moveIfNecessary(res.uri),
|
||||
width: res.width,
|
||||
height: res.height,
|
||||
mime: 'image/jpeg',
|
||||
}
|
||||
} else {
|
||||
maxQualityPercentage = qualityPercentage
|
||||
if (cacheDir) {
|
||||
await safeDeleteAsync(res.uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheDir) {
|
||||
await deleteAsync(res.uri)
|
||||
}
|
||||
if (newDataUri) {
|
||||
return newDataUri
|
||||
}
|
||||
|
||||
throw new Error(`Unable to compress image`)
|
||||
|
||||
Reference in New Issue
Block a user