Skip to content

Commit 693db92

Browse files
authored
Merge pull request #58 from dotnet-campus/t/lindexi/SkiaImage
拆开 Skia 到 AOT 的实现
2 parents 81b93c8 + 4276db3 commit 693db92

File tree

165 files changed

+474
-277
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

165 files changed

+474
-277
lines changed

src/MediaConverters/MediaConverters.Lib/Imaging/Optimizations/ImageFileOptimization.cs

Lines changed: 11 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@
1010
using SixLabors.ImageSharp.PixelFormats;
1111
using SixLabors.ImageSharp.Processing;
1212

13-
using SkiaSharp;
14-
15-
using Svg.Skia;
16-
1713
namespace DotNetCampus.MediaConverters.Imaging.Optimizations;
1814

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

6561
if (IsExtension(".svg"))
6662
{
67-
// 如果是 svg 那就直接转换了,因为后续叠加特效等逻辑都不能支持 SVG 格式
68-
try
69-
{
70-
var outputFilePath = ConvertSvgToPngFile(context);
71-
if (outputFilePath is null)
72-
{
73-
return new ImageFileOptimizationResult()
74-
{
75-
OptimizedImageFile = null,
76-
FailureReason = ImageFileOptimizationFailureReason.NotSupported
77-
};
78-
}
79-
else
80-
{
81-
context.LogMessage($"Success ConvertSvgToPngFile. Update current image file to '{outputFilePath.FullName}'");
82-
context = context with
83-
{
84-
ImageFile = outputFilePath
85-
};
86-
}
87-
}
88-
catch (Exception e)
89-
{
90-
context.LogMessage($"Convert SVG to PNG failed: {e}");
91-
92-
return ImageFileOptimizationResult.FailException(e);
93-
}
63+
return ImageFileOptimizationResult.NotSupported();
9464
}
9565
else if (IsExtension(".wmf") ||
9666
IsExtension(".emf"))
9767
{
98-
var result = EnhancedGraphicsMetafileOptimization.ConvertWmfOrEmfToPngFile(context);
99-
if (result.OptimizedImageFile is not null)
100-
{
101-
context.LogMessage($"Success ConvertWmfOrEmfToPngFile. Update current image file to '{result.OptimizedImageFile}'");
102-
context = context with
103-
{
104-
ImageFile = result.OptimizedImageFile
105-
};
106-
}
107-
else
108-
{
109-
return result;
110-
}
68+
return ImageFileOptimizationResult.NotSupported();
11169
}
11270

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

168126
OptimizeImage(image, maxImageWidth, maxImageHeight, useAreaSizeLimit);
169127

170-
// 重新保存即可
171128
var outputImageFilePath = Path.Join(workingFolder.FullName, $"{Path.GetRandomFileName()}.png");
172-
await image.SaveAsPngAsync(outputImageFilePath, new PngEncoder()
129+
if (context.ShouldSaveToPngFile)
173130
{
174-
ColorType = PngColorType.RgbWithAlpha,
175-
BitDepth = PngBitDepth.Bit8,
176-
});
131+
// 重新保存即可,保存就等于解决了各种格式问题,输出为标准的格式
132+
await image.SaveAsPngAsync(outputImageFilePath, new PngEncoder()
133+
{
134+
ColorType = PngColorType.RgbWithAlpha,
135+
BitDepth = PngBitDepth.Bit8,
136+
CompressionLevel = ((PngCompressionLevel?) context.PngCompressionLevel) ?? PngCompressionLevel.DefaultCompression
137+
});
138+
}
177139

178140
return new ImageFileOptimizationResult()
179141
{
@@ -303,96 +265,4 @@ public static void LimitImageSize(Image<Rgba32> image, int? maxImageWidth, int?
303265
/// 图片压缩的最大高度
304266
/// </summary>
305267
private const int MaxHeight = 2160;
306-
307-
/// <summary>
308-
/// 转换 svg 文件为 png 文件
309-
/// </summary>
310-
/// <returns></returns>
311-
public static FileInfo? ConvertSvgToPngFile(ImageFileOptimizationContext context)
312-
{
313-
var imageFile = context.ImageFile;
314-
var workingFolder = context.WorkingFolder;
315-
316-
using var skSvg = new SKSvg();
317-
using var skPicture = skSvg.Load(imageFile.FullName);
318-
var outputFile = Path.Join(workingFolder.FullName,
319-
$"SVG_{Path.GetRandomFileName()}.png");
320-
var canSave = skSvg.Save(outputFile, SKColors.Transparent);
321-
if (canSave && File.Exists(outputFile))
322-
{
323-
return new FileInfo(outputFile);
324-
}
325-
326-
// 转换失败
327-
return null;
328-
}
329-
330-
public static async Task<FileInfo> FixSvgInvalidCharacterAsync(FileInfo svgFile,
331-
DirectoryInfo workingFolder)
332-
{
333-
using var fileStream = svgFile.OpenRead();
334-
using var streamReader = new StreamReader(fileStream);
335-
336-
var xDocument = await XDocument.LoadAsync(streamReader, LoadOptions.SetLineInfo, CancellationToken.None);
337-
bool anyUpdate = false;
338-
339-
foreach (var xElement in xDocument.Descendants("text"))
340-
{
341-
var value = xElement.Value;
342-
if (!string.IsNullOrEmpty(value) && value.Length > 0 && value[0] is var c && c == 0xFFFD)
343-
{
344-
// 0xFFFFD 是 utf8 特殊字符
345-
// 画出来就是�符号,不如删掉
346-
xElement.Value = string.Empty;
347-
348-
anyUpdate = true;
349-
}
350-
}
351-
352-
if (anyUpdate)
353-
{
354-
var convertedFile = Path.Join(workingFolder.FullName, $"FixSVG_{Path.GetRandomFileName()}.svg");
355-
using var stream = File.Create(convertedFile);
356-
await xDocument.SaveAsync(stream, SaveOptions.None, CancellationToken.None);
357-
return new FileInfo(convertedFile);
358-
}
359-
360-
// 啥都不用改,返回原图
361-
return svgFile;
362-
}
363-
364-
public static FileInfo FixSvgInvalidCharacter(ImageFileOptimizationContext context)
365-
{
366-
FileInfo svgFile = context.ImageFile;
367-
DirectoryInfo workingFolder = context.WorkingFolder;
368-
369-
using var fileStream = svgFile.OpenRead();
370-
using var streamReader = new StreamReader(fileStream);
371-
372-
var xDocument = XDocument.Load(streamReader, LoadOptions.SetLineInfo);
373-
bool anyUpdate = false;
374-
375-
foreach (var xElement in xDocument.Descendants("text"))
376-
{
377-
var value = xElement.Value;
378-
if (!string.IsNullOrEmpty(value) && value.Length > 0 && value[0] is var c && c == 0xFFFD)
379-
{
380-
// 0xFFFFD 是 utf8 特殊字符
381-
// 画出来就是�符号,不如删掉
382-
xElement.Value = string.Empty;
383-
384-
anyUpdate = true;
385-
}
386-
}
387-
388-
if (anyUpdate)
389-
{
390-
var convertedFile = Path.Join(workingFolder.FullName, $"FixSVG_{Path.GetRandomFileName()}.svg");
391-
xDocument.Save(convertedFile);
392-
return new FileInfo(convertedFile);
393-
}
394-
395-
// 啥都不用改,返回原图
396-
return svgFile;
397-
}
398268
}

src/MediaConverters/MediaConverters.Lib/Imaging/Optimizations/ImageFileOptimizationContext.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@ public readonly record struct ImageFileOptimizationContext(FileInfo ImageFile,
1919

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

22+
/// <summary>
23+
/// 是否应该保存为 PNG 文件。为 false 则只存放到内存,不存放到文件
24+
/// </summary>
25+
public bool ShouldSaveToPngFile { get; init; } = true;
26+
2227
public string? LogFileName { get; init; }
28+
public int? PngCompressionLevel { get; init; }
2329

2430
public void LogMessage(string message)
2531
{

src/MediaConverters/MediaConverters.Lib/Imaging/Optimizations/ImageFileOptimizationResult.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ public readonly record struct ImageFileOptimizationResult() : IDisposable
2020
public Exception? Exception { get; init; }
2121
public ImageFileOptimizationFailureReason FailureReason { get; init; } = ImageFileOptimizationFailureReason.Ok;
2222

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

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

@@ -39,4 +38,13 @@ public static ImageFileOptimizationResult FailException(Exception e)
3938
FailureReason = ImageFileOptimizationFailureReason.Exception
4039
};
4140
}
41+
42+
public static ImageFileOptimizationResult NotSupported()
43+
{
44+
return new ImageFileOptimizationResult()
45+
{
46+
OptimizedImageFile = null,
47+
FailureReason = ImageFileOptimizationFailureReason.NotSupported
48+
};
49+
}
4250
}

src/MediaConverters/MediaConverters.Lib/MediaConverters.Lib.csproj

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,6 @@
1111

1212
<ItemGroup>
1313
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
14-
<PackageReference Include="SkiaSharp" Version="3.119.0" />
15-
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.119.0" />
16-
<PackageReference Include="Svg.Skia" Version="3.0.4" />
17-
<PackageReference Include="System.Drawing.Common" Version="9.0.7" />
18-
</ItemGroup>
19-
20-
<ItemGroup>
21-
<ProjectReference Include="..\SkiaWmfRenderer\src\SkiaWmfRenderer\SkiaWmfRenderer.csproj" />
2214
</ItemGroup>
2315

2416
<PropertyGroup>

src/MediaConverters/MediaConverters.Tests/Imaging/Optimizations/ImageFileOptimizationTests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,14 @@ public async Task OptimizeImageFileAsyncTest_FormatTga()
8989
TestHelper.OpenFileInExplorer(imageFileOptimizationResult.OptimizedImageFile!);
9090
}
9191

92-
[TestMethod()]
93-
public async Task OptimizeImageFileAsyncTest_FormatWmf()
94-
{
95-
var file = TestFileProvider.GetTestFile("sample.wmf");
96-
var imageFileOptimizationResult = await ImageFileOptimization.OptimizeImageFileAsync(new ImageFileOptimizationContext(file, TestHelper.WorkingDirectory));
97-
Assert.AreEqual(true, imageFileOptimizationResult.IsSuccess);
98-
Assert.AreEqual(ImageFileOptimizationFailureReason.Ok, imageFileOptimizationResult.FailureReason);
99-
}
92+
//[TestMethod()]
93+
//public async Task OptimizeImageFileAsyncTest_FormatWmf()
94+
//{
95+
// var file = TestFileProvider.GetTestFile("sample.wmf");
96+
// var imageFileOptimizationResult = await ImageFileOptimization.OptimizeImageFileAsync(new ImageFileOptimizationContext(file, TestHelper.WorkingDirectory));
97+
// Assert.AreEqual(true, imageFileOptimizationResult.IsSuccess);
98+
// Assert.AreEqual(ImageFileOptimizationFailureReason.Ok, imageFileOptimizationResult.FailureReason);
99+
//}
100100

101101
[TestMethod()]
102102
public async Task OptimizeImageFileAsyncTest_Orientation()

src/MediaConverters/MediaConverters.Tests/Tool/MediaConverterIpcTests.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Text.Json;
22
using System.Text.Json.Serialization;
3+
using dotnetCampus.Ipc.Context;
34
using dotnetCampus.Ipc.IpcRouteds.DirectRouteds;
45
using DotNetCampus.MediaConverters.CommandLineHandlers;
56
using DotNetCampus.MediaConverters.Contexts;
@@ -26,7 +27,8 @@ public async Task TestBatchImage()
2627

2728
var task = Task.Run(async () =>
2829
{
29-
var provider = new JsonIpcDirectRoutedProvider();
30+
var provider = new JsonIpcDirectRoutedProvider(ipcConfiguration: new IpcConfiguration()
31+
.UseSystemTextJsonIpcObjectSerializer(MediaConverterJsonSerializerSourceGenerationContext.Default));
3032
var clientProxy = await provider.GetAndConnectClientAsync(ipcHandler.IpcName);
3133

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

98-
await ipcHandler.RunAsync();
100+
await ipcHandler.RunAsync().WaitAsync(TimeSpan.FromMinutes(15));
99101

100-
await task;
102+
await task.WaitAsync(TimeSpan.FromMinutes(15));
101103
}
102104

103105
private static string ToConfigurationFile(ImageConvertContext imageConvertContext,string testFolder)

src/MediaConverters/MediaConverters.Tool/CommandLineHandlers/IpcHandlers/IpcHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public async Task<int> RunAsync()
4545
SharedArrayPool = new SharedArrayPool(ArrayPool<byte>.Shared),
4646
IpcTaskScheduling = IpcTaskScheduling.GlobalConcurrent,
4747
};
48-
ipcConfiguration.UseSystemJsonIpcObjectSerializer(MediaConverterJsonSerializerSourceGenerationContext.Default);
48+
ipcConfiguration.UseSystemTextJsonIpcObjectSerializer(MediaConverterJsonSerializerSourceGenerationContext.Default);
4949

5050
var ipcProvider = new IpcProvider(IpcName, ipcConfiguration);
5151

src/MediaConverters/MediaConverters.Tool/MediaConverters.Tool.csproj

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,14 @@
1010
<PublishAot>true</PublishAot>
1111
<IsAotCompatible>true</IsAotCompatible>
1212
<IsPackable>false</IsPackable>
13+
<IlcGenerateMstatFile>true</IlcGenerateMstatFile>
14+
<IlcGenerateDgmlFile>true</IlcGenerateDgmlFile>
1315
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
1416
</PropertyGroup>
1517

1618
<ItemGroup>
17-
<None Include="Assets\$(RuntimeIdentifier)\**" CopyToOutputDirectory="PreserveNewest" />
18-
<None Include="Assets\gsfonts\**" CopyToOutputDirectory="PreserveNewest" />
19-
<None Include="Assets\Fonts\**" CopyToOutputDirectory="PreserveNewest" />
20-
</ItemGroup>
21-
22-
<ItemGroup>
23-
<PackageReference Include="DotNetCampus.CommandLine" Version="4.0.0-alpha05" />
24-
<PackageReference Include="dotnetCampus.Ipc" Version="2.0.0-alpha422" />
19+
<PackageReference Include="DotNetCampus.CommandLine" Version="4.0.1-alpha.1" />
20+
<PackageReference Include="dotnetCampus.Ipc" Version="2.0.0-alpha423" />
2521
<PackageReference Include="VC-LTL" Version="5.2.2" />
2622
</ItemGroup>
2723

0 commit comments

Comments
 (0)