2025-12-22 16:12:13 +08:00

374 lines
17 KiB
C#
Raw Permalink 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.

using ImageMagick;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Reflection.Emit;
using System.Reflection.Metadata;
using System.Runtime.Intrinsics.Arm;
using System.Text;
using System.Threading.Tasks;
using XPrint.Production.Business.ImageLogic.Enitites;
using XPrint.Production.Business.Tools;
namespace XPrint.Production.Business.ImageLogic
{
public class ProductionImageCreation
{
public ProductionImageCreation() { }
public void Create(List<ProductionInfo> infos, int dpi, string outputPath, float productionHeight, int printIndex = -1)
{
// 初始化Magick旧版本可能需要显式初始化
//MagickNET.Initialize();
CreateHorProductionImage(infos, dpi, outputPath, productionHeight, printIndex);
//// 合并成3000x3000的大图
//using (var combiner = new BatchImageCombiner("final_result.png", 42000, 8000))
//{
// combiner.AppendImage("D:\\test\\bg.jpg", 0, 0, 500, 500, 2000, 2000, 42000, 6000);
// combiner.AppendImage("D:\\test\\dragonball.jpeg", 0, 0, 500, 500, 3000, 3000, 1000, 1000);
// combiner.AppendImage("D:\\test\\head.jpg", 0, 0, 500, 500, 4000, 4000, 500, 500);
// combiner.Complete(600, 600);
// Console.WriteLine("合并完成!");
//}
}
//private void SetImageProperty(MagickImage image)
//{
// image.Settings.SetDefine("magick:thread", "1"); // 启用多线程
// image.Settings.SetDefine("magick:opencl", "1"); // 启用 OpenCL
//}
/// <summary>
/// 横向布局生产图
/// </summary>
/// <param name="info">生产图信息</param>
/// <param name="dpi">打印dpi</param>
public string CreateHorProductionImage(List<ProductionInfo> infos, int dpi, string outputPath, float productionHeight, int printIndex = -1)
{
if (infos.Count > 3)
{
return "最多同时打印三份生产图";
}
var printWidth = ImageTool.MMToPxWithDpi(413.91f, dpi); //打印正反面的宽度
var printHeight = ImageTool.MMToPxWithDpi(productionHeight, dpi); //打印正反面的高度 原来是650
//var printRuleHeight = ImageTool.MMToPxWithDpi(590, dpi); //打印正反面的高度 原来是650
var printSpace = ImageTool.MMToPxWithDpi(185.087f, dpi); //打印水平间距
var productCanvasWidth = (printWidth + printSpace) * 3; //三连张正反面
var productCanvasHeight = printHeight;
//// 3. 启用多线程加速
//var settings = new MagickReadSettings
//{
// // 设置线程数0 表示自动检测 CPU 核心)
// ThreadCount = Environment.ProcessorCount
//};
var horOffset = ImageTool.MMToPxWithDpi(7f, dpi); //打印正反面的宽度
var productCanvas = new MagickImage(MagickColors.Transparent, (uint)productCanvasWidth, (uint)productCanvasHeight);
//productCanvas.SetCompression(CompressionMethod.NoCompression);
//SetImageProperty(productCanvas);
//int currentY = 0;
//uint limitHandleSize = 30000;
//for (int i = 0; i < 3; i++)
//Parallel.For(0, info.CanvasInfos.Length, (idx) =>
Parallel.For(0, infos.Count, (idx) =>
{
var info = infos[idx];
int currentIndx = idx;
float currentX = (printWidth + printSpace) * currentIndx;
if (printIndex != -1)
{
currentX = (printWidth + printSpace) * (currentIndx + printIndex);
}
//var canvasInfo = info.CanvasInfos[idx];
var canvasInfo = info.CanvasInfos[0];//正面测试
//float scaleX1 = canvasInfo.MaskWidth / canvasInfo.MaskProductWidth;
//float scaleY1 = canvasInfo.MaskHeight / canvasInfo.MaskProductHeight;
//float scaleX = printWidth / canvasInfo.MaskProductWidth;
float heightScale1 = printHeight / canvasInfo.MaskProductHeight;
float heightScale2 = printHeight / canvasInfo.MaskHeight;
float widthScale1 = printWidth / canvasInfo.MaskProductWidth;
float widthScale2 = printWidth / canvasInfo.MaskWidth;
//float offsetY = (canvasInfo.MaskProductHeight - canvasInfo.MaskHeight) * scaleY2;
float destOffsetX1 = (canvasInfo.MaskWidth - canvasInfo.CanvasWidth) * widthScale2 * 0.5f;
float destOffsetY1 = (canvasInfo.MaskHeight - canvasInfo.CanvasHeight) * heightScale2 * 0.5f;
//float destOffsetX2 = (canvasInfo.MaskProductWidth - canvasInfo.CanvasWidth) * heightScale2 * 0.5f;
//float destOffsetY2 = (canvasInfo.MaskProductHeight - canvasInfo.CanvasHeight) * heightScale2 * 0.5f;
int cropX = (int)((canvasInfo.MaskProductX - canvasInfo.MaskX) * widthScale2);
int cropY = (int)((canvasInfo.MaskProductY - canvasInfo.MaskY) * heightScale2);// (int)((canvasInfo.MaskProductY - canvasInfo.MaskY) * heightScale2);
uint cropWidth = (uint)(canvasInfo.MaskProductWidth * widthScale2);
uint cropHeight = (uint)(canvasInfo.MaskProductHeight * heightScale2);
using var drawCanvas = new MagickImage(MagickColors.Transparent, (uint)printWidth, (uint)printHeight);
//SetImageProperty(drawCanvas);
//MagickImage? maskImage = null;
//if (canvasInfo.MaskBuffer != null)
//{
// maskImage = new MagickImage(canvasInfo.MaskBuffer);
//}
for (int j = 0; j < canvasInfo.Images.Length; j++)
{
var imgInfo = canvasInfo.Images[j];
//uint resizeWidth = (uint)imgInfo.OriginWidth;
//uint resizeHeight = (uint)imgInfo.OriginHeight;
//if (resizeWidth > limitHandleSize || resizeHeight > limitHandleSize)
//{
// if (resizeWidth > resizeHeight)
// {
// resizeWidth = limitHandleSize;
// resizeHeight = (uint)(imgInfo.OriginHeight * (resizeWidth / (float)imgInfo.OriginWidth));
// }
// else
// {
// resizeHeight = limitHandleSize;
// resizeWidth = (uint)(imgInfo.OriginWidth * (resizeHeight / (float)imgInfo.OriginHeight));
// }
//}
//var settings = new MagickReadSettings
//{
// Width = resizeWidth, // 目标宽度
// Height = resizeHeight, // 目标高度
//};
//using var image = new MagickImage(imgInfo.DataBuffer, settings);
using var image = new MagickImage(imgInfo.DataBuffer);
image.AutoOrient();//修复手机拍的图片的方向问题
// 方法1alpha通道阈值处理消除半透明
//SetImageProperty(image);
image.BackgroundColor = MagickColors.Transparent;
//int srcX = 0,
// srcY = 0;
//uint srcWidth = (uint)imgInfo.OriginWidth,
// srcHeight = (uint)imgInfo.OriginHeight;
//if (imgInfo.X < canvasInfo.MaskX)
//{
// srcX = (int)((canvasInfo.MaskX - imgInfo.X) * (imgInfo.OriginWidth / imgInfo.Width));
//}
//if (imgInfo.Y < canvasInfo.MaskY)
//{
// srcY = (int)((canvasInfo.MaskY - imgInfo.Y) * (imgInfo.OriginHeight / imgInfo.Height));
//}
//if (imgInfo.X + imgInfo.Width > canvasInfo.MaskX + canvasInfo.MaskWidth)
//{
// srcWidth = (uint)((canvasInfo.MaskX + canvasInfo.MaskWidth - imgInfo.X) * (imgInfo.OriginWidth / imgInfo.Width));
// srcWidth = Math.Min(image.Width, srcWidth);
//}
//if (imgInfo.Y + imgInfo.Height > canvasInfo.MaskY + canvasInfo.MaskHeight)
//{
// srcHeight = (uint)((canvasInfo.MaskY + canvasInfo.MaskHeight - imgInfo.Y) * (imgInfo.OriginHeight / imgInfo.Height));
// srcHeight = Math.Min(image.Height, srcHeight);
//}
if (imgInfo.Angle != 0)
{
image.Rotate(imgInfo.Angle);
}
//if (imgInfo.ScaleX < 0 && imgInfo.ScaleY == 1)
//{
// image.Flop();
//}
//else if (imgInfo.ScaleX == 1 && imgInfo.ScaleY < 0)
//{
// image.Flip();
//}
//else if (imgInfo.ScaleX < 0 && imgInfo.ScaleY < 0)
//{
// image.Flop();
// image.Flip();
//}
if (imgInfo.FlipX)
{
image.Flop();
}
if (imgInfo.FlipY)
{
image.Flip();
}
float destX = imgInfo.X * heightScale2;
float destY = imgInfo.Y * heightScale2;
float destWidth = imgInfo.Width * heightScale2;
float destHeight = imgInfo.Height * heightScale2;
//float cropedWidth = destWidth;
//float cropedHeight = destHeight;
//if (destX + destWidth > currentStartX + printWidth)
//{
// cropedWidth = currentStartX + printWidth - destX;
//}
//if (destY + destHeight > currentY + printHeight)
//{
// cropedHeight = currentY + printHeight - destY;
//}
BatchImageCombiner.CopyRegion(image, drawCanvas, 0, 0, image.Width, image.Height,
(int)(destOffsetX1 + destX), (int)(destOffsetY1 + destY), (uint)destWidth, (uint)destHeight);
}
uint orginWidth = drawCanvas.Width;
uint orginHeight = drawCanvas.Height;
drawCanvas.Crop(new MagickGeometry(cropX, cropY, cropWidth, cropHeight));
//drawCanvas.Resize(orginWidth, orginHeight);
int offset = 0;
//if (printHeight != printRuleHeight)
//{
// drawCanvas.Scale((uint)(drawCanvas.Width * (printHeight / printRuleHeight)), (uint)printHeight);
// //计算偏移,以便居中
// offset = (int)orginWidth - (int)drawCanvas.Width;
//}
//if (maskImage != null)
//{
// //maskImage.AutoOrient();
// //if (maskProductWidth != 0 && maskProductHeight != 0)
// //{
// // drawCanvas.Crop((uint)(currentStartX + maskProductWidth), (uint)maskProductHeight);
// //}
// float destWidth = maskImage.Width * scale;
// float destHeight = maskImage.Height * scale;
// BatchImageCombiner.CopyRegion(maskImage, drawCanvas, 0, 0, maskImage.Width, maskImage.Height,
// (int)currentX, 0, (uint)destWidth, (uint)destHeight);
// maskImage.Dispose();
//}
BatchImageCombiner.CopyRegion(drawCanvas, productCanvas, 0, 0, drawCanvas.Width, drawCanvas.Height,
(int)(currentX + offset - horOffset), 0, (uint)printWidth, (uint)printHeight, true);
//}
});
// 2. 根据格式设置特定优化参数
//if (format == MagickFormat.Jpeg)
//{
// image.SetOption("jpeg:optimize-coding", "true");
// image.SetOption("jpeg:progressive", "false"); // 非渐进式编码更快
//}
//productCanvas.SetOption("png:compression-level", "3"); // 降低压缩级别0-9
//productCanvas.SetOption("png:exclude-chunk", "all"); // 排除元数据块
//else if (format == MagickFormat.Webp)
//{
// image.SetOption("webp:lossless", "false"); // 有损模式更快
//}
productCanvas.Density = new Density(dpi, dpi);
//// 降低 PNG 压缩级别0-9值越小越快
//productCanvas.SetAttribute("png:compression-level", "0"); // 无压缩
//productCanvas.SetAttribute("png:compression-strategy", "0");
// 1. 降低质量和压缩级别(最直接提升速度)
//productCanvas.Quality = 85; // 85以上质量提升不明显但耗时增加
//productCanvas.ColorSpace = ColorSpace.sRGB; // 统一色彩空间,避免转换开销
//OptimizeQualityForFormat(productCanvas, MagickFormat.Tif, productCanvas.Quality);
// 2. 使用流式写入(避免中间文件)
//using (var stream = new MemoryStream())
//{
//productCanvas.Settings.SetDefine("tiff:tile", "True"); // 启用分块
//productCanvas.Settings.SetDefine("tiff:tile-width", "256"); // 设置分块宽度
//productCanvas.Settings.SetDefine("tiff:tile-height", "256"); // 设置分块高度
//productCanvas.SetCompression(CompressionMethod.LZW); // 分块 JPEG 压缩
//productCanvas.Quality = 90;
productCanvas.Settings.Compression = CompressionMethod.JPEG;
productCanvas.Quality = 100;
using var stream = new MemoryStream();
productCanvas.Write(stream, MagickFormat.Tif);
stream.Position = 0;
productCanvas.Dispose();
using var productFixedImage = new MagickImage(stream);
productFixedImage.Settings.Compression = CompressionMethod.LZW;
productFixedImage.Write(outputPath, MagickFormat.Tif);
//File.WriteAllBytes(@"D:\final_result.png", stream.ToArray());
//}
GC.Collect(); // 强制回收内存
return string.Empty;
//productCanvas.Write(@"D:\final_result.png"); // 覆盖临时文件(磁盘操作)
}
///// <summary>
///// 针对不同格式优化质量参数
///// </summary>
//private static void OptimizeQualityForFormat(MagickImage image, MagickFormat format, uint quality)
//{
// switch (format)
// {
// case MagickFormat.Jpeg:
// // JPEG质量 80-90 平衡最佳
// // 配合渐进式参数(质量高时启用)
// image.SetAttribute("jpeg:progressive", quality >= 80 ? "true" : "false");
// // 质量低时启用快速编码
// if (quality < 60)
// image.SetAttribute("jpeg:dct-method", "fast");
// break;
// case MagickFormat.Png:
// // PNG质量值映射为压缩级别0-9
// // 质量越高 = 压缩率越高(文件越小但写入越慢)
// int compressionLevel = Math.Min(9, (int)Math.Ceiling(quality / 11.1));
// image.SetAttribute("png:compression-level", compressionLevel.ToString());
// // 高质量时启用无损压缩
// if (quality >= 90)
// image.SetAttribute("png:compression-strategy", "1");
// break;
// case MagickFormat.WebP:
// // WebP质量 0-10075 为默认推荐值
// image.SetAttribute("webp:lossless", quality == 100 ? "true" : "false");
// if (quality < 50)
// image.SetAttribute("webp:method", "1"); // 低质量时用快速模式
// break;
// case MagickFormat.Tiff | MagickFormat.Tif:
// // TIFF质量值影响压缩率
// image.SetCompression(CompressionMethod.LZW);
// break;
// }
//}
}
}