FM/Assets/Scripts/FUJIFILM/UI/Other/PngCompressor.cs

165 lines
6.4 KiB
C#
Raw Normal View History

2025-09-07 11:06:22 +08:00
using System;
using System.Collections;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
2025-09-04 15:34:11 +08:00
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Processing;
using ImageMagick;
using UnityEngine;
namespace HK.FUJIFILM
{
public static class PngCompressor
{
public static void CompressPng(string inputPath, string outputPath)
{
using (Image image = Image.Load(inputPath))
{
// 设置 DPI如果你知道它是 300可以手动设置
image.Metadata.VerticalResolution = 300;
image.Metadata.HorizontalResolution = 300;
// 保存为 PNG并设置压缩选项
var encoder = new PngEncoder
{
CompressionLevel = PngCompressionLevel.Level9, // 最高压缩,无损
FilterMethod = PngFilterMethod.Adaptive, // 通常效果最好
ColorType = PngColorType.Rgb // 保留 RGB避免 Alpha 影响体积
};
image.Save(outputPath, encoder);
}
}
/// <summary>
/// 压缩 PNG 并设置为 300 DPI
/// </summary>
/// <param name="inputPath">原始 PNG 路径</param>
/// <param name="outputPath">压缩后 PNG 输出路径</param>
public static void CompressPngAndSetDpi(string inputPath, string outputPath)
{
using (var image = new MagickImage(inputPath))
{
// 设置 300 DPI
image.Density = new Density(300, 300, DensityUnit.PixelsPerInch);
// 设置压缩参数(对于 PNG 有效)
image.Format = MagickFormat.Png;
image.Quality = 100; // 保证无损
image.Depth = 8; // PNG 8bit通常足够
// 选择压缩过滤器
image.Settings.SetDefine(MagickFormat.Png, "compression-filter", "5");
image.Settings.Compression = CompressionMethod.Zip; // PNG 默认
// 写入输出文件
image.Write(outputPath);
}
Debug.Log($"✅ 压缩成功,输出文件: {outputPath}");
}
2025-09-07 11:06:22 +08:00
public static void CompressPngAndSetDpi(byte[] bytes, string outputPath)
{
using (var image = new MagickImage(bytes))
{
// 设置 300 DPI
image.Density = new Density(300, 300, DensityUnit.PixelsPerInch);
// 设置压缩参数(对于 PNG 有效)
image.Format = MagickFormat.Png;
image.Quality = 100; // 保证无损
image.Depth = 8; // PNG 8bit通常足够
// 选择压缩过滤器
image.Settings.SetDefine(MagickFormat.Png, "compression-filter", "5");
image.Settings.Compression = CompressionMethod.Zip; // PNG 默认
// 写入输出文件
image.Write(outputPath);
}
Debug.Log($"✅ 压缩成功,输出文件: {outputPath}");
}
2025-09-07 11:06:22 +08:00
public static async UniTask CompressPngAndSetDpiAsync(byte[] bytes, string outputPath)
{
// 关键用Task.Run替代RunOnThreadPool在.NET线程池执行后台操作
await Task.Run(() =>
{
using (var image = new MagickImage(bytes))
{
// 设置300 DPI
image.Density = new Density(300, 300, DensityUnit.PixelsPerInch);
// PNG压缩配置
image.Format = MagickFormat.Png;
image.Quality = 100; // 无损压缩
image.Depth = 8;
image.Settings.SetDefine(MagickFormat.Png, "compression-level", "9");
image.Settings.SetDefine(MagickFormat.Png, "compression-filter", "5");
image.Settings.Compression = CompressionMethod.Zip;
image.Write(outputPath); // 同步写入文件(在后台线程)
}
});
Debug.Log($"✅ 压缩成功,输出文件: {outputPath}");
}
public static IEnumerator CompressPngAndSetDpiAsync1(byte[] bytes, string outputPath,
CancellationToken cancellationToken = default)
{
// 验证输入参数
if (bytes == null || bytes.Length == 0)
throw new ArgumentException("图片字节数组不能为空", nameof(bytes));
if (string.IsNullOrEmpty(outputPath))
throw new ArgumentException("输出路径不能为空", nameof(outputPath));
bool isWriteDone = false;
Task.Run(() =>
{
// 检查是否已取消
cancellationToken.ThrowIfCancellationRequested();
// 使用MagickImage处理图片
using (var image = new MagickImage(bytes))
{
// 设置DPI为300
image.Density = new Density(300, 300, DensityUnit.PixelsPerInch);
// 配置PNG压缩参数
image.Format = MagickFormat.Png;
image.Quality = 100; // 无损压缩
image.Depth = 8; // 8位色深
image.Settings.SetDefine(MagickFormat.Png, "compression-level", "9");
image.Settings.SetDefine(MagickFormat.Png, "compression-filter", "5");
image.Settings.Compression = CompressionMethod.Zip;
// 再次检查取消状态
if (cancellationToken.IsCancellationRequested)
cancellationToken.ThrowIfCancellationRequested();
// 写入处理后的图片
image.Write(outputPath);
}
Debug.Log($"✅ 压缩成功,输出文件: {outputPath}");
// 标记完成线程安全因为bool是原子操作
isWriteDone = true;
}, cancellationToken);
// 主线程:等待后台线程完成(用循环+yield return null避免死等
while (!isWriteDone)
{
yield return null; // 每帧检查一次,不阻塞主线程
}
}
2025-09-04 15:34:11 +08:00
}
}