Completely removed GDI
Some checks failed
Build Windows Avalonia / build (push) Failing after 1m47s

This commit is contained in:
Maddo 2026-05-28 20:27:05 +02:00
commit d76e133f18
31 changed files with 236 additions and 2592 deletions

View file

@ -1,7 +1,6 @@
using System;
using System.IO;
using System.Threading.Tasks;
// System.Drawing not required for ImageSharp-based drawing in this class
using Microsoft.Extensions.Logging;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
@ -17,11 +16,8 @@ using SixLabors.ImageSharp.Drawing;
namespace MaddoShared;
/// <summary>
/// Image creator implemented using SixLabors.ImageSharp for core image operations.
/// This implementation focuses on loading, EXIF-orientation, resizing and saving.
/// It intentionally implements a minimal subset of the original functionality to
/// provide a safe and testable replacement. Additional features (text/logo drawing)
/// can be added later using ImageSharp.Drawing.Common and SixLabors.Fonts.
/// Image creator implemented with SixLabors.ImageSharp for loading, EXIF orientation,
/// resizing, text/logo drawing, and saving.
/// </summary>
public class ImageCreatorImageSharp : IImageCreator
{
@ -38,12 +34,11 @@ public class ImageCreatorImageSharp : IImageCreator
{
ArgumentNullException.ThrowIfNull(imgState);
// Minimal preparation of names and settings normally done by ImageCreatorSharp.PrepareVariables
PrepareVariablesMinimal(imgState);
try
{
_logger.LogInformation("[Alternate] Processing {File} -> {Dest}", imgState.WorkFile?.FullName, imgState.DestDir?.FullName);
_logger.LogInformation("[ImageSharp] Processing {File} -> {Dest}", imgState.WorkFile?.FullName, imgState.DestDir?.FullName);
using var fs = File.OpenRead(imgState.WorkFile.FullName);
@ -59,9 +54,6 @@ public class ImageCreatorImageSharp : IImageCreator
// text to draw (horizontal vs vertical).
ApplyExifOrientation(img, imgState);
// Determine output format
var forceJpg = _picSettings.UsaForzaJpg;
// Compute big size
var bigSize = ComputeBigSize(img.Width, img.Height);
@ -71,10 +63,10 @@ public class ImageCreatorImageSharp : IImageCreator
// Ensure destination exists
imgState.DestDir?.Create();
var fileNameBig = System.IO.Path.Combine(imgState.DestDir.FullName, imgState.NomeFileBig);
var fileNameBig = System.IO.Path.Combine(imgState.DestDir.FullName, imgState.NomeFileBig);
// Draw overlays (text/logo) onto big image using ImageSharp and save
await DrawAndSaveWithGdiAsync(imgBig, fileNameBig, imgState, logoData, _picSettings.JpegQuality, isThumbnail: false).ConfigureAwait(false);
await DrawAndSaveAsync(imgBig, fileNameBig, imgState, logoData, _picSettings.JpegQuality, isThumbnail: false).ConfigureAwait(false);
// Create thumbnail if requested
if (_picSettings.CreaMiniature)
@ -85,20 +77,16 @@ public class ImageCreatorImageSharp : IImageCreator
var fileNameSmall = System.IO.Path.Combine(imgState.DestDir.FullName, imgState.NomeFileSmall);
// Draw overlays and save thumbnail via ImageSharp
await DrawAndSaveWithGdiAsync(imgSmall, fileNameSmall, imgState, logoData, _picSettings.JpegQualityMin, isThumbnail: true).ConfigureAwait(false);
await DrawAndSaveAsync(imgSmall, fileNameSmall, imgState, logoData, _picSettings.JpegQualityMin, isThumbnail: true).ConfigureAwait(false);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "[Alternate] Error processing image {File}", imgState.WorkFile?.Name);
_logger.LogError(ex, "[ImageSharp] Error processing image {File}", imgState.WorkFile?.Name);
throw;
}
}
// Thumbnail overlays are rendered by the GDI+ pass in DrawAndSaveWithGdiAsync to match original rendering.
private static SixLabors.ImageSharp.Formats.IImageEncoder GetEncoderForExtension(string ext, long quality)
{
quality = Math.Clamp(quality, 1, 100);
@ -110,7 +98,7 @@ public class ImageCreatorImageSharp : IImageCreator
};
}
private async Task DrawAndSaveWithGdiAsync(Image<Rgba32> imgSharp, string outputPath, ImageState imgState, byte[]? logoData, long quality, bool isThumbnail)
private async Task DrawAndSaveAsync(Image<Rgba32> imgSharp, string outputPath, ImageState imgState, byte[]? logoData, long quality, bool isThumbnail)
{
// Use ImageSharp drawing APIs to render text and logos and save using ImageSharp encoders.
// Clone editable image so we don't mutate the original reference unexpectedly.
@ -125,13 +113,13 @@ public class ImageCreatorImageSharp : IImageCreator
}
catch (Exception ex)
{
_logger?.LogDebug(ex, "[Alternate] Failed to clear EXIF orientation on working image");
_logger?.LogDebug(ex, "[ImageSharp] Failed to clear EXIF orientation on working image");
}
// Ensure DataFoto is set (extracted earlier) so time-based text is available
imgState.DataFoto = imgState.CreationDate ?? DateTime.Now;
// Ensure thumbnail text is prepared similarly to ImageCreatorSharp logic
// Ensure thumbnail text is prepared before drawing.
if (isThumbnail)
{
if (string.IsNullOrEmpty(imgState.TestoFirmaPiccola))
@ -285,8 +273,7 @@ public class ImageCreatorImageSharp : IImageCreator
});
}
// Draw logo if provided. For compatibility with the original GDI implementation,
// do not draw the logo on thumbnails (ImageCreatorSharp only draws logos on big images).
// Draw logos only on full-size images.
if (logoData != null && logoData.Length > 0 && _picSettings.LogoAggiungi && !isThumbnail)
{
try
@ -414,7 +401,7 @@ public class ImageCreatorImageSharp : IImageCreator
}
catch (Exception ex)
{
_logger.LogError(ex, "[Alternate] Invalid transparent color setting {Color}", _picSettings.TransparentColor);
_logger.LogError(ex, "[ImageSharp] Invalid transparent color setting {Color}", _picSettings.TransparentColor);
}
}
@ -429,7 +416,7 @@ public class ImageCreatorImageSharp : IImageCreator
}
catch (Exception ex)
{
_logger.LogError(ex, "[Alternate] Error drawing logo in ImageSharp pass");
_logger.LogError(ex, "[ImageSharp] Error drawing logo");
}
}
@ -444,8 +431,6 @@ public class ImageCreatorImageSharp : IImageCreator
await working.SaveAsync(outStream, encoder).ConfigureAwait(false);
}
// Removed GDI encoder helper; ImageSharp encoders are used instead.
private void PrepareVariablesMinimal(ImageState imgState)
{
imgState.NomeFileBig = imgState.WorkFile.Name;
@ -454,7 +439,6 @@ public class ImageCreatorImageSharp : IImageCreator
imgState.DimensioneStandardMiniatura = _picSettings.DimStandardMiniatura;
// basic text / transparency defaults used by drawing routines
// AlphaScelta mirrors ImageCreatorSharp behavior: compute from PicSettings.Trasparenza (0-100)
imgState.AlphaScelta = Convert.ToInt32((255 * (100 - _picSettings.Trasparenza) / (double)100));
// Set minimal text fields so text drawing has fallback values
@ -517,9 +501,7 @@ public class ImageCreatorImageSharp : IImageCreator
private void ApplyExifOrientation(Image<Rgba32> img, ImageState imgState)
{
// Common EXIF orientations: 1=TopLeft, 3=BottomRight (rotate 180), 6=RightTop (rotate 90 CW), 8=LeftBottom (rotate 270 CW)
// Set rotation flags on the state so other code can pick the correct
// text variant (vertical vs horizontal). Mirror ImageCreatorSharp logic.
// Set rotation flags on the state so other code can pick the correct text variant.
imgState.FotoRuotaADestra = false;
imgState.FotoRuotaASinistra = false;
@ -567,11 +549,11 @@ public class ImageCreatorImageSharp : IImageCreator
catch (Exception ex)
{
// Non-fatal: log and continue
_logger?.LogDebug(ex, "[Alternate] Could not clear EXIF orientation tag");
_logger?.LogDebug(ex, "[ImageSharp] Could not clear EXIF orientation tag");
}
}
private System.Drawing.Size ComputeBigSize(int width, int height)
private Size ComputeBigSize(int width, int height)
{
// If original large size option requested, return original
// otherwise compute based on width/height limits from settings
@ -580,16 +562,14 @@ public class ImageCreatorImageSharp : IImageCreator
: CalculateThumbnailSize(width, height, _picSettings.AltezzaBig, "Altezza");
}
private System.Drawing.Size ComputeSmallSize(int width, int height)
private Size ComputeSmallSize(int width, int height)
{
return width > height
? CalculateThumbnailSize(width, height, _picSettings.LarghezzaSmall, "Larghezza")
: CalculateThumbnailSize(width, height, _picSettings.AltezzaSmall, "Altezza");
}
// Helper to access PicSettings values via instance _picSettings
private static System.Drawing.Size CalculateThumbnailSize(int currentwidth, int currentheight, int maxPixel, string tipoSize)
private static Size CalculateThumbnailSize(int currentwidth, int currentheight, int maxPixel, string tipoSize)
{
double tempMultiplier;
if (string.Equals(tipoSize, "Larghezza", StringComparison.OrdinalIgnoreCase))
@ -601,7 +581,7 @@ public class ImageCreatorImageSharp : IImageCreator
else
tempMultiplier = maxPixel / (double)currentwidth;
var newSize = new System.Drawing.Size(Convert.ToInt32(currentwidth * tempMultiplier), Convert.ToInt32(currentheight * tempMultiplier));
var newSize = new Size(Convert.ToInt32(currentwidth * tempMultiplier), Convert.ToInt32(currentheight * tempMultiplier));
return newSize;
}