374 lines
17 KiB
C#
374 lines
17 KiB
C#
|
||
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();//修复手机拍的图片的方向问题
|
||
// 方法1:alpha通道阈值处理(消除半透明)
|
||
|
||
//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-100,75 为默认推荐值
|
||
// 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;
|
||
// }
|
||
//}
|
||
}
|
||
}
|