|
2 | 2 | // Licensed under the MIT License. |
3 | 3 |
|
4 | 4 | using Microsoft.Extensions.Logging; |
5 | | -using SkiaSharp; |
| 5 | +using System.Drawing; |
| 6 | +using System.Drawing.Drawing2D; |
| 7 | +using System.Drawing.Imaging; |
6 | 8 | using WinApp.Cli.Helpers; |
7 | 9 |
|
8 | 10 | namespace WinApp.Cli.Services; |
@@ -36,90 +38,94 @@ public async Task GenerateAssetsAsync(FileInfo sourceImagePath, DirectoryInfo ou |
36 | 38 | logger.LogInformation("{UISymbol} Generating MSIX image assets from: {SourceImage}", UiSymbols.Info, sourceImagePath.Name); |
37 | 39 |
|
38 | 40 | // Load the source image |
39 | | - using var sourceImage = SKBitmap.Decode(sourceImagePath.FullName); |
40 | | - if (sourceImage == null) |
| 41 | + Bitmap sourceImage; |
| 42 | + try |
41 | 43 | { |
42 | | - throw new InvalidOperationException($"Failed to decode image: {sourceImagePath.FullName}. Please ensure the file is a valid image format."); |
| 44 | + sourceImage = new Bitmap(sourceImagePath.FullName); |
43 | 45 | } |
44 | | - |
45 | | - logger.LogDebug("Source image size: {Width}x{Height}", sourceImage.Width, sourceImage.Height); |
46 | | - |
47 | | - // Ensure output directory exists |
48 | | - if (!outputDirectory.Exists) |
| 46 | + catch (Exception ex) |
49 | 47 | { |
50 | | - outputDirectory.Create(); |
| 48 | + throw new InvalidOperationException($"Failed to decode image: {sourceImagePath.FullName}. Please ensure the file is a valid image format.", ex); |
51 | 49 | } |
52 | 50 |
|
53 | | - // Generate each required asset |
54 | | - var successCount = 0; |
55 | | - foreach (var (fileName, width, height) in AssetSpecifications) |
| 51 | + using (sourceImage) |
56 | 52 | { |
57 | | - try |
| 53 | + logger.LogDebug("Source image size: {Width}x{Height}", sourceImage.Width, sourceImage.Height); |
| 54 | + |
| 55 | + // Ensure output directory exists |
| 56 | + if (!outputDirectory.Exists) |
58 | 57 | { |
59 | | - var outputPath = Path.Combine(outputDirectory.FullName, fileName); |
60 | | - await GenerateAssetAsync(sourceImage, outputPath, width, height, cancellationToken); |
61 | | - successCount++; |
62 | | - logger.LogDebug(" {UISymbol} Generated: {FileName} ({Width}x{Height})", UiSymbols.Check, fileName, width, height); |
| 58 | + outputDirectory.Create(); |
63 | 59 | } |
64 | | - catch (Exception ex) |
| 60 | + |
| 61 | + // Generate each required asset |
| 62 | + var successCount = 0; |
| 63 | + foreach (var (fileName, width, height) in AssetSpecifications) |
65 | 64 | { |
66 | | - logger.LogWarning("{UISymbol} Failed to generate {FileName}: {ErrorMessage}", UiSymbols.Warning, fileName, ex.Message); |
| 65 | + try |
| 66 | + { |
| 67 | + var outputPath = Path.Combine(outputDirectory.FullName, fileName); |
| 68 | + await GenerateAssetAsync(sourceImage, outputPath, width, height, cancellationToken); |
| 69 | + successCount++; |
| 70 | + logger.LogDebug(" {UISymbol} Generated: {FileName} ({Width}x{Height})", UiSymbols.Check, fileName, width, height); |
| 71 | + } |
| 72 | + catch (Exception ex) |
| 73 | + { |
| 74 | + logger.LogWarning("{UISymbol} Failed to generate {FileName}: {ErrorMessage}", UiSymbols.Warning, fileName, ex.Message); |
| 75 | + } |
67 | 76 | } |
68 | | - } |
69 | 77 |
|
70 | | - logger.LogInformation("{UISymbol} Successfully generated {Count} of {Total} image assets", |
71 | | - UiSymbols.Party, successCount, AssetSpecifications.Length); |
| 78 | + logger.LogInformation("{UISymbol} Successfully generated {Count} of {Total} image assets", |
| 79 | + UiSymbols.Party, successCount, AssetSpecifications.Length); |
| 80 | + } |
72 | 81 | } |
73 | 82 |
|
74 | | - private static async Task GenerateAssetAsync(SKBitmap sourceImage, string outputPath, int targetWidth, int targetHeight, CancellationToken cancellationToken) |
| 83 | + private static async Task GenerateAssetAsync(Bitmap sourceImage, string outputPath, int targetWidth, int targetHeight, CancellationToken cancellationToken) |
75 | 84 | { |
76 | | - // Calculate scaling to fit target dimensions while maintaining aspect ratio |
77 | | - var sourceAspect = (float)sourceImage.Width / sourceImage.Height; |
78 | | - var targetAspect = (float)targetWidth / targetHeight; |
79 | | - |
80 | | - int scaledWidth, scaledHeight; |
81 | | - if (sourceAspect > targetAspect) |
82 | | - { |
83 | | - // Source is wider - fit to width |
84 | | - scaledWidth = targetWidth; |
85 | | - scaledHeight = (int)(targetWidth / sourceAspect); |
86 | | - } |
87 | | - else |
| 85 | + await Task.Run(() => |
88 | 86 | { |
89 | | - // Source is taller - fit to height |
90 | | - scaledHeight = targetHeight; |
91 | | - scaledWidth = (int)(targetHeight * sourceAspect); |
92 | | - } |
| 87 | + // Calculate scaling to fit target dimensions while maintaining aspect ratio |
| 88 | + var sourceAspect = (float)sourceImage.Width / sourceImage.Height; |
| 89 | + var targetAspect = (float)targetWidth / targetHeight; |
93 | 90 |
|
94 | | - // Create the target bitmap with the required dimensions |
95 | | - using var targetBitmap = new SKBitmap(targetWidth, targetHeight); |
96 | | - using var canvas = new SKCanvas(targetBitmap); |
| 91 | + int scaledWidth, scaledHeight; |
| 92 | + if (sourceAspect > targetAspect) |
| 93 | + { |
| 94 | + // Source is wider - fit to width |
| 95 | + scaledWidth = targetWidth; |
| 96 | + scaledHeight = (int)(targetWidth / sourceAspect); |
| 97 | + } |
| 98 | + else |
| 99 | + { |
| 100 | + // Source is taller - fit to height |
| 101 | + scaledHeight = targetHeight; |
| 102 | + scaledWidth = (int)(targetHeight * sourceAspect); |
| 103 | + } |
97 | 104 |
|
98 | | - // Fill with transparent background |
99 | | - canvas.Clear(SKColors.Transparent); |
| 105 | + // Create the target bitmap with the required dimensions |
| 106 | + using var targetBitmap = new Bitmap(targetWidth, targetHeight, PixelFormat.Format32bppArgb); |
| 107 | + using var graphics = Graphics.FromImage(targetBitmap); |
100 | 108 |
|
101 | | - // Calculate position to center the scaled image |
102 | | - var destRect = new SKRect( |
103 | | - (targetWidth - scaledWidth) / 2f, |
104 | | - (targetHeight - scaledHeight) / 2f, |
105 | | - (targetWidth + scaledWidth) / 2f, |
106 | | - (targetHeight + scaledHeight) / 2f |
107 | | - ); |
| 109 | + // Set high-quality rendering options |
| 110 | + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; |
| 111 | + graphics.SmoothingMode = SmoothingMode.HighQuality; |
| 112 | + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; |
| 113 | + graphics.CompositingQuality = CompositingQuality.HighQuality; |
| 114 | + graphics.CompositingMode = CompositingMode.SourceOver; |
108 | 115 |
|
109 | | - // Draw the scaled image |
110 | | - var paint = new SKPaint |
111 | | - { |
112 | | - IsAntialias = true |
113 | | - }; |
114 | | - canvas.DrawBitmap(sourceImage, destRect, paint); |
115 | | - |
116 | | - // Encode and save as PNG |
117 | | - using var image = SKImage.FromBitmap(targetBitmap); |
118 | | - using var data = image.Encode(SKEncodedImageFormat.Png, 100); |
119 | | - |
120 | | - // Write to file asynchronously |
121 | | - await using var stream = File.Create(outputPath); |
122 | | - data.SaveTo(stream); |
123 | | - await stream.FlushAsync(cancellationToken); |
| 116 | + // Fill with transparent background |
| 117 | + graphics.Clear(Color.Transparent); |
| 118 | + |
| 119 | + // Calculate position to center the scaled image |
| 120 | + var x = (targetWidth - scaledWidth) / 2f; |
| 121 | + var y = (targetHeight - scaledHeight) / 2f; |
| 122 | + var destRect = new RectangleF(x, y, scaledWidth, scaledHeight); |
| 123 | + |
| 124 | + // Draw the scaled image |
| 125 | + graphics.DrawImage(sourceImage, destRect); |
| 126 | + |
| 127 | + // Save as PNG |
| 128 | + targetBitmap.Save(outputPath, ImageFormat.Png); |
| 129 | + }, cancellationToken); |
124 | 130 | } |
125 | 131 | } |
0 commit comments