Files
afilmory/packages/builder/src/image/processor.ts
Innei 7e216d90d7 feat: introduce @afilmory/typing package and update imports across the codebase
- Added a new package `@afilmory/typing` to centralize type definitions.
- Updated various files to import types from `@afilmory/typing` instead of individual modules.
- Adjusted package dependencies in `package.json` files for affected packages.
- Modified `pnpm-lock.yaml` to include the new package and its dependencies.

Signed-off-by: Innei <tukon479@gmail.com>
2026-01-10 23:31:41 +08:00

134 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import path from 'node:path'
import type { ImageMetadata } from '@afilmory/typing'
import * as bmp from '@vingle/bmp-js'
import heicConvert from 'heic-convert'
import sharp from 'sharp'
import { HEIC_FORMATS } from '../constants/index.js'
import { getGlobalLoggers } from '../photo/logger-adapter.js'
// 获取图片元数据(复用 Sharp 实例)
export async function getImageMetadataWithSharp(sharpInstance: sharp.Sharp): Promise<ImageMetadata | null> {
const log = getGlobalLoggers().image
try {
const metadata = await sharpInstance.metadata()
if (!metadata.width || !metadata.height || !metadata.format) {
log.error('图片元数据不完整')
return null
}
let { width } = metadata
let { height } = metadata
// 根据 EXIF Orientation 信息调整宽高
const { orientation } = metadata
if (orientation === 5 || orientation === 6 || orientation === 7 || orientation === 8) {
// 对于需要旋转 90°的图片需要交换宽高
;[width, height] = [height, width]
log.info(`检测到需要旋转 90°的图片 (orientation: ${orientation}),交换宽高:${width}x${height}`)
}
return {
width,
height,
format: metadata.format,
}
} catch (error) {
log.error('获取图片元数据失败:', error)
return null
}
}
// 转换 HEIC/HEIF 格式到 JPEG
export async function convertHeicToJpeg(heicBuffer: Buffer): Promise<Buffer> {
const log = getGlobalLoggers().image
try {
log.info(`开始 HEIC/HEIF → JPEG 转换 (${Math.round(heicBuffer.length / 1024)}KB)`)
const startTime = Date.now()
const jpegBuffer = await heicConvert({
buffer: heicBuffer,
format: 'JPEG',
quality: 0.95, // 高质量转换
})
const duration = Date.now() - startTime
const outputSizeKB = Math.round(jpegBuffer.byteLength / 1024)
log.success(`HEIC/HEIF 转换完成 (${outputSizeKB}KB, ${duration}ms)`)
return Buffer.from(jpegBuffer)
} catch (error) {
log.error('HEIC/HEIF 转换失败:', error)
throw error
}
}
// 预处理图片 Buffer处理 HEIC/HEIF 格式)
export async function preprocessImageBuffer(buffer: Buffer, key: string): Promise<Buffer> {
const log = getGlobalLoggers().image
const ext = path.extname(key).toLowerCase()
// 如果是 HEIC/HEIF 格式,先转换为 JPEG
if (HEIC_FORMATS.has(ext)) {
log.info(`检测到 HEIC/HEIF 格式:${key}`)
return await convertHeicToJpeg(buffer)
}
// 其他格式直接返回原始 buffer
return buffer
}
// BMP 格式
const BUF_BMP = Buffer.from([0x42, 0x4d])
export function isBitmap(buf: Buffer): boolean {
if (buf.length < 2) {
return false
}
return Buffer.compare(BUF_BMP, buf.slice(0, 2)) === 0
}
/**
* 将 BMP Buffer 转换为 Sharp 实例
* @param bmpBuffer Buffer
* @returns Sharp 实例
*/
export async function convertBmpToJpegSharpInstance(bmpBuffer: Buffer): Promise<sharp.Sharp> {
const log = getGlobalLoggers().image
try {
log.info(`开始 BMP → JPEG 转换 (${Math.round(bmpBuffer.length / 1024)}KB)`)
const startTime = Date.now()
// 使用 @vingle/bmp-js 解析 BMP
const bmpImage = bmp.decode(bmpBuffer, true)
if (!bmpImage) {
throw new Error('BMP 解码失败')
}
// 创建 Sharp 实例
// Calculate the number of channels in the BMP image
const channels = bmpImage.data.length / (bmpImage.width * bmpImage.height)
if (channels !== 3 && channels !== 4) {
throw new Error(`Unsupported BMP channel count: ${channels}`)
}
// Create Sharp instance with the correct channel count
const sharpInstance = sharp(bmpImage.data, {
raw: { width: bmpImage.width, height: bmpImage.height, channels },
}).jpeg()
const duration = Date.now() - startTime
log.success(`BMP 转换完成 (${duration}ms)`)
return sharpInstance
} catch (error) {
log.error('BMP 转换失败:', error)
throw error
}
}