mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-19 13:59:27 +02:00
feat: 图片压缩算法调整
This commit is contained in:
parent
485099ebfe
commit
effdfd7f73
@ -147,37 +147,143 @@ if (!isMainThread) {
|
|||||||
const sharpOptions = {
|
const sharpOptions = {
|
||||||
failOnError: false,
|
failOnError: false,
|
||||||
limitInputPixels: false,
|
limitInputPixels: false,
|
||||||
sequentialRead: !isRamDisk, // 如果不是RAM disk,使用顺序读取
|
sequentialRead: !isRamDisk,
|
||||||
};
|
};
|
||||||
|
|
||||||
let sharpInstance = sharp(inputPath, sharpOptions);
|
let sharpInstance = sharp(inputPath, sharpOptions);
|
||||||
|
|
||||||
|
// 获取图像信息
|
||||||
|
const metadata = await sharpInstance.metadata();
|
||||||
|
const isTransparent = metadata.hasAlpha;
|
||||||
|
const { width, height } = metadata;
|
||||||
|
const isLargeImage = width > 1000 || height > 1000;
|
||||||
|
|
||||||
|
// 智能压缩策略
|
||||||
if (isSmallFile) {
|
if (isSmallFile) {
|
||||||
|
// 小文件使用相对保守的压缩
|
||||||
await sharpInstance
|
await sharpInstance
|
||||||
.png({
|
.png({
|
||||||
compressionLevel: 9,
|
compressionLevel: 9,
|
||||||
effort: 10,
|
effort: 10,
|
||||||
palette: true,
|
palette: true,
|
||||||
colors: 128,
|
colors: 256,
|
||||||
dither: 0.4
|
quality: 90,
|
||||||
|
dither: 0.6
|
||||||
})
|
})
|
||||||
.toFile(tempPath);
|
.toFile(tempPath);
|
||||||
|
} else if (isTransparent) {
|
||||||
|
// 包含透明通道的图片
|
||||||
|
const optimizedPng = sharpInstance.clone()
|
||||||
|
.png({
|
||||||
|
quality: 75,
|
||||||
|
compressionLevel: 9,
|
||||||
|
effort: 10,
|
||||||
|
palette: true,
|
||||||
|
colors: isLargeImage ? 196 : 256,
|
||||||
|
dither: 0.8
|
||||||
|
});
|
||||||
|
|
||||||
|
// 对于透明图片也尝试使用带Alpha通道的WebP
|
||||||
|
const webpVersion = sharpInstance.clone()
|
||||||
|
.webp({
|
||||||
|
quality: 80,
|
||||||
|
alphaQuality: 85,
|
||||||
|
effort: 6,
|
||||||
|
lossless: false,
|
||||||
|
nearLossless: false,
|
||||||
|
smartSubsample: true,
|
||||||
|
reductionEffort: 6
|
||||||
|
});
|
||||||
|
|
||||||
|
const [pngBuffer, webpBuffer] = await Promise.all([
|
||||||
|
optimizedPng.toBuffer(),
|
||||||
|
webpVersion.toBuffer()
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 选择更小的格式
|
||||||
|
if (webpBuffer.length < pngBuffer.length && webpBuffer.length < inputStats.size) {
|
||||||
|
await fs.writeFile(tempPath, webpBuffer);
|
||||||
|
} else {
|
||||||
|
await fs.writeFile(tempPath, pngBuffer);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await sharpInstance
|
// 不透明图片,使用更激进的压缩
|
||||||
|
const optimizedPng = sharpInstance.clone()
|
||||||
.png({
|
.png({
|
||||||
quality: 70,
|
quality: 70,
|
||||||
compressionLevel: 9,
|
compressionLevel: 9,
|
||||||
|
effort: 10,
|
||||||
palette: true,
|
palette: true,
|
||||||
colors: 128,
|
colors: isLargeImage ? 128 : 196,
|
||||||
dither: 0.4,
|
dither: 0.6
|
||||||
effort: 10
|
});
|
||||||
})
|
|
||||||
.toFile(tempPath);
|
// 对于不透明图片尝试多种格式
|
||||||
|
const webpVersion = sharpInstance.clone()
|
||||||
|
.webp({
|
||||||
|
quality: 75,
|
||||||
|
effort: 6,
|
||||||
|
lossless: false,
|
||||||
|
nearLossless: false,
|
||||||
|
smartSubsample: true,
|
||||||
|
reductionEffort: 6
|
||||||
|
});
|
||||||
|
|
||||||
|
// 对于照片类型的图片,也尝试JPEG格式
|
||||||
|
const jpegVersion = isLargeImage ?
|
||||||
|
sharpInstance.clone()
|
||||||
|
.jpeg({
|
||||||
|
quality: 82,
|
||||||
|
progressive: true,
|
||||||
|
mozjpeg: true,
|
||||||
|
chromaSubsampling: '4:2:0'
|
||||||
|
}) : null;
|
||||||
|
|
||||||
|
const bufferPromises = [
|
||||||
|
optimizedPng.toBuffer(),
|
||||||
|
webpVersion.toBuffer()
|
||||||
|
];
|
||||||
|
|
||||||
|
if (jpegVersion) {
|
||||||
|
bufferPromises.push(jpegVersion.toBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffers = await Promise.all(bufferPromises);
|
||||||
|
const [pngBuffer, webpBuffer, jpegBuffer] = buffers;
|
||||||
|
|
||||||
|
// 选择最小的格式
|
||||||
|
let smallestBuffer = pngBuffer;
|
||||||
|
let smallestSize = pngBuffer.length;
|
||||||
|
|
||||||
|
if (webpBuffer.length < smallestSize) {
|
||||||
|
smallestBuffer = webpBuffer;
|
||||||
|
smallestSize = webpBuffer.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jpegBuffer && jpegBuffer.length < smallestSize) {
|
||||||
|
smallestBuffer = jpegBuffer;
|
||||||
|
smallestSize = jpegBuffer.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smallestSize < inputStats.size) {
|
||||||
|
await fs.writeFile(tempPath, smallestBuffer);
|
||||||
|
} else {
|
||||||
|
// 如果所有格式都没有达到更好的压缩效果,尝试最后的优化
|
||||||
|
await sharpInstance
|
||||||
|
.png({
|
||||||
|
quality: 65,
|
||||||
|
compressionLevel: 9,
|
||||||
|
effort: 10,
|
||||||
|
palette: true,
|
||||||
|
colors: 128,
|
||||||
|
dither: 0.5
|
||||||
|
})
|
||||||
|
.toFile(tempPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputStats = await fs.stat(tempPath);
|
const outputStats = await fs.stat(tempPath);
|
||||||
if (outputStats.size < inputStats.size) {
|
if (outputStats.size < inputStats.size) {
|
||||||
// 如果优化后的文件更小,则替换原文件
|
|
||||||
await fs.rename(tempPath, inputPath);
|
await fs.rename(tempPath, inputPath);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@ -186,7 +292,6 @@ if (!isMainThread) {
|
|||||||
path: relativePath
|
path: relativePath
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// 如果优化后的文件更大,则删除临时文件
|
|
||||||
await fs.unlink(tempPath);
|
await fs.unlink(tempPath);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@ -197,7 +302,6 @@ if (!isMainThread) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 清理临时文件
|
|
||||||
try {
|
try {
|
||||||
await fs.unlink(tempPath);
|
await fs.unlink(tempPath);
|
||||||
} catch {}
|
} catch {}
|
||||||
|
Loading…
Reference in New Issue
Block a user