feat: prepare GoodBuddy 0.8.0
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
export const MAX_BOUNDED_JPEG_BYTES = 220 * 1024
|
||||
export const BOUNDED_JPEG_QUALITIES = [60, 45, 30, 20, 10] as const
|
||||
|
||||
type JpegImage = {
|
||||
getSize(): { width: number; height: number }
|
||||
resize(options: {
|
||||
width: number
|
||||
quality: 'good'
|
||||
}): JpegImage
|
||||
toJPEG(quality: number): Buffer
|
||||
}
|
||||
|
||||
export function isValidJpeg(data: Buffer): boolean {
|
||||
return (
|
||||
data.byteLength >= 4 &&
|
||||
data[0] === 0xff &&
|
||||
data[1] === 0xd8 &&
|
||||
data.at(-2) === 0xff &&
|
||||
data.at(-1) === 0xd9
|
||||
)
|
||||
}
|
||||
|
||||
export function encodeBoundedJpeg(
|
||||
image: JpegImage,
|
||||
maximumBytes = MAX_BOUNDED_JPEG_BYTES
|
||||
): Buffer {
|
||||
if (!Number.isSafeInteger(maximumBytes) || maximumBytes < 4) {
|
||||
throw new Error('JPEG 大小限制无效')
|
||||
}
|
||||
const initialWidth = Math.max(1, image.getSize().width)
|
||||
const widths = [
|
||||
initialWidth,
|
||||
1_600,
|
||||
1_280,
|
||||
960,
|
||||
720
|
||||
].filter(
|
||||
(width, index, values) =>
|
||||
width <= initialWidth && values.indexOf(width) === index
|
||||
)
|
||||
|
||||
for (const width of widths) {
|
||||
const candidate =
|
||||
width === initialWidth
|
||||
? image
|
||||
: image.resize({ width, quality: 'good' })
|
||||
for (const quality of BOUNDED_JPEG_QUALITIES) {
|
||||
const data = candidate.toJPEG(quality)
|
||||
if (!isValidJpeg(data)) {
|
||||
throw new Error('JPEG 图片内容无效')
|
||||
}
|
||||
if (data.byteLength <= maximumBytes) {
|
||||
return data
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error('JPEG 图片压缩后仍然过大')
|
||||
}
|
||||
Reference in New Issue
Block a user