Commit c55b2d32 by linjinhong

修改裁剪方法

parent 867cb6f6
...@@ -187,8 +187,6 @@ app.on("activate", async () => { ...@@ -187,8 +187,6 @@ app.on("activate", async () => {
app.on("ready", async () => { app.on("ready", async () => {
await createWindow(); await createWindow();
checkMemorySetting();
// 获取当前窗口的尺寸 // 获取当前窗口的尺寸
const { width, height } = win.getBounds(); const { width, height } = win.getBounds();
win.webContents.send("window-size", { width, height }); win.webContents.send("window-size", { width, height });
...@@ -205,23 +203,6 @@ app.on("will-quit", () => { ...@@ -205,23 +203,6 @@ app.on("will-quit", () => {
globalShortcut.unregister("CommandOrControl+R"); globalShortcut.unregister("CommandOrControl+R");
}); });
function checkMemorySetting() {
try {
const v8 = require("v8");
const heapStats = v8.getHeapStatistics();
const currentLimitMB = Math.floor(
heapStats.heap_size_limit / (1024 * 1024)
);
console.log(`当前堆内存限制: ${currentLimitMB}MB`);
if (currentLimitMB < 4096) {
console.warn("内存设置不足,建议重启应用");
}
} catch (error) {
console.error("无法检查内存设置", error);
}
}
if (isDevelopment) { if (isDevelopment) {
if (process.platform === "win32") { if (process.platform === "win32") {
process.on("message", (data) => { process.on("message", (data) => {
......
...@@ -124,11 +124,7 @@ async function cropTransparentEdges(inputDir, outputDir, options = {}) { ...@@ -124,11 +124,7 @@ async function cropTransparentEdges(inputDir, outputDir, options = {}) {
const inputPath = path.join(inputDir, file); const inputPath = path.join(inputDir, file);
const outputPath = path.join(outputDir, file); const outputPath = path.join(outputDir, file);
const result = await cropImageTransparentEdges( const result = await cropImage(inputPath, outputPath, options);
inputPath,
outputPath,
options
);
results.push({ file, ...result }); results.push({ file, ...result });
} }
...@@ -162,11 +158,7 @@ async function processImages(inputPath, outputPath, options = {}) { ...@@ -162,11 +158,7 @@ async function processImages(inputPath, outputPath, options = {}) {
results = await cropTransparentEdges(inputPath, outputPath, options); results = await cropTransparentEdges(inputPath, outputPath, options);
} else { } else {
// 处理单个图片 // 处理单个图片
const result = await cropImageTransparentEdges( const result = await cropImage(inputPath, outputPath, options);
inputPath,
outputPath,
options
);
results = [{ file: path.basename(inputPath), ...result }]; results = [{ file: path.basename(inputPath), ...result }];
} }
...@@ -190,10 +182,246 @@ async function processImages(inputPath, outputPath, options = {}) { ...@@ -190,10 +182,246 @@ async function processImages(inputPath, outputPath, options = {}) {
} }
} }
/**
* 裁切方法优化
*/
async function cropImage(inputPath, outputPath, options = {}) {
const {
threshold = 10, // 透明度阈值
stripHeight = 64, // 流式模式条带高度
maxDimension = 2500, // 小图/大图分界点
padding = 0, // 裁剪后留白
debug = false, // 调试模式
forceStreamMode = false, // 强制使用流式模式
} = options;
// 确保输出目录存在
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
try {
// 获取图像元数据
const metadata = await sharp(inputPath).metadata();
const { width, height } = metadata;
const channels = metadata.channels || 4;
const alphaIndex = channels - 1;
// 根据尺寸选择处理模式
const useStreamMode =
forceStreamMode || width > maxDimension || height > maxDimension;
let bounds = {
top: height,
bottom: 0,
left: width,
right: 0,
foundPixels: false,
};
if (debug) {
console.log(`处理图像: ${width}x${height}`);
console.log(`模式: ${useStreamMode ? "流式处理" : "快速处理"}`);
}
if (useStreamMode) {
// 流式安全模式(处理大图)
await processInStreamMode(inputPath, bounds, {
width,
height,
channels,
alphaIndex,
threshold,
stripHeight,
debug,
});
} else {
// 快速模式(处理小图)
await processInFastMode(inputPath, bounds, {
width,
height,
channels,
alphaIndex,
threshold,
});
}
// 检查是否找到有效像素
if (!bounds.foundPixels) {
// 全透明图像直接复制
await fs.promises.copyFile(inputPath, outputPath);
return {
status: "unchanged",
reason: "全透明图片",
originalSize: { width, height },
};
}
// 应用边界留白
applyPadding(bounds, padding, width, height);
// 计算裁剪区域
const cropWidth = Math.min(
width - bounds.left,
bounds.right - bounds.left + 1
);
const cropHeight = Math.min(
height - bounds.top,
bounds.bottom - bounds.top + 1
);
if (cropWidth <= 0 || cropHeight <= 0) {
throw new Error(`无效的裁剪尺寸: ${cropWidth}x${cropHeight}`);
}
// 如果裁剪区域和原图一样则跳过
if (cropWidth === width && cropHeight === height) {
await fs.promises.copyFile(inputPath, outputPath);
return {
status: "unchanged",
reason: "裁剪尺寸与原图相同",
originalSize: { width, height },
};
}
// 执行裁剪
await sharp(inputPath)
.extract({
left: bounds.left,
top: bounds.top,
width: cropWidth,
height: cropHeight,
})
.toFile(outputPath);
return {
status: "cropped",
outputPath,
originalSize: { width, height },
newSize: { width: cropWidth, height: cropHeight },
cropArea: {
x: bounds.left,
y: bounds.top,
width: cropWidth,
height: cropHeight,
},
};
} catch (error) {
return {
status: "error",
error: error.message,
stack: error.stack,
};
}
}
async function processInStreamMode(inputPath, bounds, options) {
const {
width,
height,
channels,
alphaIndex,
threshold,
stripHeight,
debug,
} = options;
// 垂直分条带处理(从0到height)
for (let y = 0; y < height; y += stripHeight) {
const currentStripHeight = Math.min(stripHeight, height - y);
// 读取当前条带
const stripData = await sharp(inputPath)
.extract({
left: 0,
top: y,
width,
height: currentStripHeight,
})
.raw()
.toBuffer();
// 处理当前条带内的像素
for (let row = 0; row < currentStripHeight; row++) {
const rowStartIdx = row * width * channels;
let rowHasPixel = false;
// 扫描当前行寻找像素
for (let x = 0; x < width; x++) {
const idx = rowStartIdx + x * channels + alphaIndex;
if (stripData[idx] > threshold) {
rowHasPixel = true;
const absY = y + row;
// 更新垂直边界
if (absY < bounds.top) bounds.top = absY;
if (absY > bounds.bottom) bounds.bottom = absY;
// 更新水平边界
if (x < bounds.left) bounds.left = x;
if (x > bounds.right) bounds.right = x;
}
}
// 标记找到像素
if (rowHasPixel) bounds.foundPixels = true;
}
// 调试信息
if (debug && y % (stripHeight * 10) === 0) {
console.log(
`流式处理进度: ${y}/${height} (${Math.round((y / height) * 100)}%)`
);
}
}
}
// 快速模式(高效处理小图)
async function processInFastMode(inputPath, bounds, options) {
const { width, height, channels, alphaIndex, threshold } = options;
// 一次性读取整个图像
const imageBuffer = await sharp(inputPath)
.raw()
.toBuffer();
// 遍历整张图像
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const idx = (y * width + x) * channels + alphaIndex;
if (imageBuffer[idx] > threshold) {
bounds.foundPixels = true;
// 更新所有边界
if (y < bounds.top) bounds.top = y;
if (y > bounds.bottom) bounds.bottom = y;
if (x < bounds.left) bounds.left = x;
if (x > bounds.right) bounds.right = x;
}
}
}
}
// ================= 辅助函数 =================
// 应用边界留白
function applyPadding(bounds, padding, imgWidth, imgHeight) {
if (padding > 0) {
bounds.top = Math.max(0, bounds.top - padding);
bounds.bottom = Math.min(imgHeight - 1, bounds.bottom + padding);
bounds.left = Math.max(0, bounds.left - padding);
bounds.right = Math.min(imgWidth - 1, bounds.right + padding);
}
}
// 分区边界检测实现 // 分区边界检测实现
module.exports = { module.exports = {
cropTransparentEdges, cropTransparentEdges,
cropImageTransparentEdges, cropImageTransparentEdges,
processImages, processImages,
cropImage,
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment