Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7606420
拆分出一个还不用使用 skia 的版本
lindexi Sep 2, 2025
2b66683
去掉 IPC 实现,因为 AOT 之后居然有 20 MB 大小
lindexi Sep 2, 2025
369e109
Revert "去掉 IPC 实现,因为 AOT 之后居然有 20 MB 大小"
lindexi Sep 2, 2025
8a3923c
完成拆分 Skia 到独立可库引用的项目
lindexi Sep 2, 2025
d09d81d
请了 GitHub Copilot 添加注释
lindexi Sep 2, 2025
cb398ce
完成文件的拆分
lindexi Sep 2, 2025
eb3aad6
提供简便的转换实现逻辑
lindexi Sep 2, 2025
a5455e9
更改资源负载资源所在项目
lindexi Sep 2, 2025
f912526
配置打包输出
lindexi Sep 3, 2025
9d6b21d
加上打包构建逻辑
lindexi Sep 3, 2025
eeffd65
修复路径
lindexi Sep 3, 2025
85eee8e
修复拷贝错内容
lindexi Sep 3, 2025
6e0f53f
修复内容被展开
lindexi Sep 3, 2025
b7f4275
修复打包过程没有将正确平台放入
lindexi Sep 3, 2025
00d4756
修复 Windows 端也被拷贝内容
lindexi Sep 3, 2025
6fc64ce
修复错误的属性
lindexi Sep 3, 2025
881384d
去掉不能支持的单元测试
lindexi Sep 3, 2025
1b860b0
修复忘记标记可空
lindexi Sep 5, 2025
a73432e
添加无需转换的属性
lindexi Sep 5, 2025
04c656a
修复压缩等级没有传递
lindexi Sep 5, 2025
17a9ff4
尝试修复 Linux 上区分大小写找不到文件夹
lindexi Sep 5, 2025
43c4fb2
分开保存逻辑
lindexi Sep 5, 2025
531df06
修复判断可空
lindexi Sep 5, 2025
8ea23b2
升级 IPC 库,修复 AOT 体积
lindexi Sep 5, 2025
aba9cbf
尝试修复构建
lindexi Sep 5, 2025
6d0b95f
默认带测量大小
lindexi Sep 5, 2025
c10c259
Revert "升级 IPC 库,修复 AOT 体积"
lindexi Sep 5, 2025
cc59561
由于 IPC 导致序列化文档,暂时回滚代码,同时添加限定超时时间
lindexi Sep 5, 2025
ab3b351
Reapply "升级 IPC 库,修复 AOT 体积"
lindexi Sep 5, 2025
4379147
尝试修复单元测试等待
lindexi Sep 5, 2025
20b90ee
既然已经没有支持 wmf 格式了,那就不再作为默认调试文件
lindexi Sep 5, 2025
2361f25
升级库,尝试修复 Linux 的路径问题
lindexi Sep 5, 2025
4276db3
明确使用 PowerShell 风格
lindexi Sep 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;

using SkiaSharp;

using Svg.Skia;

namespace DotNetCampus.MediaConverters.Imaging.Optimizations;

public static class ImageFileOptimization
Expand Down Expand Up @@ -64,50 +60,12 @@ public static async Task<ImageFileOptimizationResult> OptimizeImageFileAsync(Ima

if (IsExtension(".svg"))
{
// 如果是 svg 那就直接转换了,因为后续叠加特效等逻辑都不能支持 SVG 格式
try
{
var outputFilePath = ConvertSvgToPngFile(context);
if (outputFilePath is null)
{
return new ImageFileOptimizationResult()
{
OptimizedImageFile = null,
FailureReason = ImageFileOptimizationFailureReason.NotSupported
};
}
else
{
context.LogMessage($"Success ConvertSvgToPngFile. Update current image file to '{outputFilePath.FullName}'");
context = context with
{
ImageFile = outputFilePath
};
}
}
catch (Exception e)
{
context.LogMessage($"Convert SVG to PNG failed: {e}");

return ImageFileOptimizationResult.FailException(e);
}
return ImageFileOptimizationResult.NotSupported();
}
else if (IsExtension(".wmf") ||
IsExtension(".emf"))
{
var result = EnhancedGraphicsMetafileOptimization.ConvertWmfOrEmfToPngFile(context);
if (result.OptimizedImageFile is not null)
{
context.LogMessage($"Success ConvertWmfOrEmfToPngFile. Update current image file to '{result.OptimizedImageFile}'");
context = context with
{
ImageFile = result.OptimizedImageFile
};
}
else
{
return result;
}
return ImageFileOptimizationResult.NotSupported();
}

context.LogMessage($"Start optimize image with ImageSharp. ImageFile: '{context.ImageFile.FullName}'");
Expand Down Expand Up @@ -167,13 +125,17 @@ public static async Task<ImageFileOptimizationResult> OptimizeImageFileAsync(Ima

OptimizeImage(image, maxImageWidth, maxImageHeight, useAreaSizeLimit);

// 重新保存即可
var outputImageFilePath = Path.Join(workingFolder.FullName, $"{Path.GetRandomFileName()}.png");
await image.SaveAsPngAsync(outputImageFilePath, new PngEncoder()
if (context.ShouldSaveToPngFile)
{
ColorType = PngColorType.RgbWithAlpha,
BitDepth = PngBitDepth.Bit8,
});
// 重新保存即可,保存就等于解决了各种格式问题,输出为标准的格式
await image.SaveAsPngAsync(outputImageFilePath, new PngEncoder()
{
ColorType = PngColorType.RgbWithAlpha,
BitDepth = PngBitDepth.Bit8,
CompressionLevel = ((PngCompressionLevel?) context.PngCompressionLevel) ?? PngCompressionLevel.DefaultCompression
});
}

return new ImageFileOptimizationResult()
{
Expand Down Expand Up @@ -303,96 +265,4 @@ public static void LimitImageSize(Image<Rgba32> image, int? maxImageWidth, int?
/// 图片压缩的最大高度
/// </summary>
private const int MaxHeight = 2160;

/// <summary>
/// 转换 svg 文件为 png 文件
/// </summary>
/// <returns></returns>
public static FileInfo? ConvertSvgToPngFile(ImageFileOptimizationContext context)
{
var imageFile = context.ImageFile;
var workingFolder = context.WorkingFolder;

using var skSvg = new SKSvg();
using var skPicture = skSvg.Load(imageFile.FullName);
var outputFile = Path.Join(workingFolder.FullName,
$"SVG_{Path.GetRandomFileName()}.png");
var canSave = skSvg.Save(outputFile, SKColors.Transparent);
if (canSave && File.Exists(outputFile))
{
return new FileInfo(outputFile);
}

// 转换失败
return null;
}

public static async Task<FileInfo> FixSvgInvalidCharacterAsync(FileInfo svgFile,
DirectoryInfo workingFolder)
{
using var fileStream = svgFile.OpenRead();
using var streamReader = new StreamReader(fileStream);

var xDocument = await XDocument.LoadAsync(streamReader, LoadOptions.SetLineInfo, CancellationToken.None);
bool anyUpdate = false;

foreach (var xElement in xDocument.Descendants("text"))
{
var value = xElement.Value;
if (!string.IsNullOrEmpty(value) && value.Length > 0 && value[0] is var c && c == 0xFFFD)
{
// 0xFFFFD 是 utf8 特殊字符
// 画出来就是�符号,不如删掉
xElement.Value = string.Empty;

anyUpdate = true;
}
}

if (anyUpdate)
{
var convertedFile = Path.Join(workingFolder.FullName, $"FixSVG_{Path.GetRandomFileName()}.svg");
using var stream = File.Create(convertedFile);
await xDocument.SaveAsync(stream, SaveOptions.None, CancellationToken.None);
return new FileInfo(convertedFile);
}

// 啥都不用改,返回原图
return svgFile;
}

public static FileInfo FixSvgInvalidCharacter(ImageFileOptimizationContext context)
{
FileInfo svgFile = context.ImageFile;
DirectoryInfo workingFolder = context.WorkingFolder;

using var fileStream = svgFile.OpenRead();
using var streamReader = new StreamReader(fileStream);

var xDocument = XDocument.Load(streamReader, LoadOptions.SetLineInfo);
bool anyUpdate = false;

foreach (var xElement in xDocument.Descendants("text"))
{
var value = xElement.Value;
if (!string.IsNullOrEmpty(value) && value.Length > 0 && value[0] is var c && c == 0xFFFD)
{
// 0xFFFFD 是 utf8 特殊字符
// 画出来就是�符号,不如删掉
xElement.Value = string.Empty;

anyUpdate = true;
}
}

if (anyUpdate)
{
var convertedFile = Path.Join(workingFolder.FullName, $"FixSVG_{Path.GetRandomFileName()}.svg");
xDocument.Save(convertedFile);
return new FileInfo(convertedFile);
}

// 啥都不用改,返回原图
return svgFile;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ public readonly record struct ImageFileOptimizationContext(FileInfo ImageFile,

public bool ShouldLogToFile { get; init; } = false;

/// <summary>
/// 是否应该保存为 PNG 文件。为 false 则只存放到内存,不存放到文件
/// </summary>
public bool ShouldSaveToPngFile { get; init; } = true;

public string? LogFileName { get; init; }
public int? PngCompressionLevel { get; init; }

public void LogMessage(string message)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ public readonly record struct ImageFileOptimizationResult() : IDisposable
public Exception? Exception { get; init; }
public ImageFileOptimizationFailureReason FailureReason { get; init; } = ImageFileOptimizationFailureReason.Ok;

[MemberNotNullWhen(true, nameof(OptimizedImageFile))]
public bool IsSuccess => OptimizedImageFile is not null;
public bool IsSuccess => OptimizedImageFile is not null || Image is not null;

public Image<Rgba32>? Image { get; init; }

Expand All @@ -39,4 +38,13 @@ public static ImageFileOptimizationResult FailException(Exception e)
FailureReason = ImageFileOptimizationFailureReason.Exception
};
}

public static ImageFileOptimizationResult NotSupported()
{
return new ImageFileOptimizationResult()
{
OptimizedImageFile = null,
FailureReason = ImageFileOptimizationFailureReason.NotSupported
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,6 @@

<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
<PackageReference Include="SkiaSharp" Version="3.119.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.119.0" />
<PackageReference Include="Svg.Skia" Version="3.0.4" />
<PackageReference Include="System.Drawing.Common" Version="9.0.7" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\SkiaWmfRenderer\src\SkiaWmfRenderer\SkiaWmfRenderer.csproj" />
</ItemGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,14 @@ public async Task OptimizeImageFileAsyncTest_FormatTga()
TestHelper.OpenFileInExplorer(imageFileOptimizationResult.OptimizedImageFile!);
}

[TestMethod()]
public async Task OptimizeImageFileAsyncTest_FormatWmf()
{
var file = TestFileProvider.GetTestFile("sample.wmf");
var imageFileOptimizationResult = await ImageFileOptimization.OptimizeImageFileAsync(new ImageFileOptimizationContext(file, TestHelper.WorkingDirectory));
Assert.AreEqual(true, imageFileOptimizationResult.IsSuccess);
Assert.AreEqual(ImageFileOptimizationFailureReason.Ok, imageFileOptimizationResult.FailureReason);
}
//[TestMethod()]
//public async Task OptimizeImageFileAsyncTest_FormatWmf()
//{
// var file = TestFileProvider.GetTestFile("sample.wmf");
// var imageFileOptimizationResult = await ImageFileOptimization.OptimizeImageFileAsync(new ImageFileOptimizationContext(file, TestHelper.WorkingDirectory));
// Assert.AreEqual(true, imageFileOptimizationResult.IsSuccess);
// Assert.AreEqual(ImageFileOptimizationFailureReason.Ok, imageFileOptimizationResult.FailureReason);
//}

[TestMethod()]
public async Task OptimizeImageFileAsyncTest_Orientation()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using dotnetCampus.Ipc.Context;
using dotnetCampus.Ipc.IpcRouteds.DirectRouteds;
using DotNetCampus.MediaConverters.CommandLineHandlers;
using DotNetCampus.MediaConverters.Contexts;
Expand All @@ -26,7 +27,8 @@ public async Task TestBatchImage()

var task = Task.Run(async () =>
{
var provider = new JsonIpcDirectRoutedProvider();
var provider = new JsonIpcDirectRoutedProvider(ipcConfiguration: new IpcConfiguration()
.UseSystemTextJsonIpcObjectSerializer(MediaConverterJsonSerializerSourceGenerationContext.Default));
var clientProxy = await provider.GetAndConnectClientAsync(ipcHandler.IpcName);

var response = await clientProxy.GetResponseAsync<IpcConvertImageResponse>(IpcPaths.RequestConvertImage, new IpcConvertImageRequest()
Expand Down Expand Up @@ -95,9 +97,9 @@ public async Task TestBatchImage()
Assert.AreEqual(MediaConverterErrorCode.Success.Code, response.Code);
});

await ipcHandler.RunAsync();
await ipcHandler.RunAsync().WaitAsync(TimeSpan.FromMinutes(15));

await task;
await task.WaitAsync(TimeSpan.FromMinutes(15));
}

private static string ToConfigurationFile(ImageConvertContext imageConvertContext,string testFolder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public async Task<int> RunAsync()
SharedArrayPool = new SharedArrayPool(ArrayPool<byte>.Shared),
IpcTaskScheduling = IpcTaskScheduling.GlobalConcurrent,
};
ipcConfiguration.UseSystemJsonIpcObjectSerializer(MediaConverterJsonSerializerSourceGenerationContext.Default);
ipcConfiguration.UseSystemTextJsonIpcObjectSerializer(MediaConverterJsonSerializerSourceGenerationContext.Default);

var ipcProvider = new IpcProvider(IpcName, ipcConfiguration);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,14 @@
<PublishAot>true</PublishAot>
<IsAotCompatible>true</IsAotCompatible>
<IsPackable>false</IsPackable>
<IlcGenerateMstatFile>true</IlcGenerateMstatFile>
<IlcGenerateDgmlFile>true</IlcGenerateDgmlFile>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>

<ItemGroup>
<None Include="Assets\$(RuntimeIdentifier)\**" CopyToOutputDirectory="PreserveNewest" />
<None Include="Assets\gsfonts\**" CopyToOutputDirectory="PreserveNewest" />
<None Include="Assets\Fonts\**" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="DotNetCampus.CommandLine" Version="4.0.0-alpha05" />
<PackageReference Include="dotnetCampus.Ipc" Version="2.0.0-alpha422" />
<PackageReference Include="DotNetCampus.CommandLine" Version="4.0.1-alpha.1" />
<PackageReference Include="dotnetCampus.Ipc" Version="2.0.0-alpha423" />
<PackageReference Include="VC-LTL" Version="5.2.2" />
</ItemGroup>

Expand Down
Loading
Loading