Enhance image processing performance and flexibility by introducing atomic counters, improving file pattern matching, and refining logo positioning logic.

This commit is contained in:
MaddoScientisto 2026-02-10 21:18:46 +01:00
commit 68c1106f65
8 changed files with 134 additions and 68 deletions

View file

@ -88,7 +88,28 @@ namespace MaddoShared
DirectoryInfo destDir;
destDir = new DirectoryInfo(Path.Combine(dirDest.FullName));
foreach (FileInfo file in dir.GetFiles(filter))
// Support multiple patterns separated by ';' or ','
var patterns = (filter ?? "*").Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()).ToArray();
// Collect matching files for all patterns and avoid duplicates
var matchedFiles = new List<FileInfo>();
foreach (var pat in patterns)
{
try
{
matchedFiles.AddRange(dir.GetFiles(pat));
}
catch
{
// Ignore pattern errors and continue
}
}
// Remove duplicates (by FullName)
var distinctFiles = matchedFiles.GroupBy(f => f.FullName, StringComparer.OrdinalIgnoreCase).Select(g => g.First()).OrderBy(f => f.Name).ToList();
foreach (FileInfo file in distinctFiles)
{
contaFilePerDir += 1;

View file

@ -142,13 +142,17 @@ namespace MaddoShared
private List<FileData> GetFilesToProcess(Options options)
{
// Support multiple common JPEG patterns so files named .jpeg, .jpe, etc. are included
var jpgPatterns = new[] { "*.jpg", "*.jpeg", "*.jpe", "*.jfif", "*.pjpeg", "*.pjp" };
if (options.AggiornaSottodirectory && options.CreaSottocartelle)
{
var helper = new FileHelperSharp();
// Pass patterns joined by ';' - FileHelperSharp will split and handle multiple patterns
return helper.GetFilesRecursive(
new DirectoryInfo(options.SourcePath),
new DirectoryInfo(options.DestinationPath),
"*.jpg",
string.Join(";", jpgPatterns),
new FileHelperOptions
{
FilesPerFolder = options.FilePerCartella,
@ -158,10 +162,13 @@ namespace MaddoShared
});
}
var files = Directory.EnumerateFiles(
options.SourcePath,
"*.jpg",
options.AggiornaSottodirectory ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
// For non-recursive or recursive enumeration without using the helper, enumerate for each pattern
var searchOption = options.AggiornaSottodirectory ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
var files = jpgPatterns
.SelectMany(p => Directory.EnumerateFiles(options.SourcePath, p, searchOption))
.Distinct()
.ToList();
return files.Select(x =>
{

View file

@ -267,26 +267,36 @@ public class ImageCreatorSharp(PicSettings picSettings, ILogger<ImageCreatorShar
using var graphics = Graphics.FromImage(imgOutputSmall);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
imgState.DimensioneStandardMiniatura = 50;
// Use the user's configured font size directly
using var font1 = CreateFont(picSettings.IlFont, imgState.DimensioneStandardMiniatura, picSettings.Grassetto);
var textSize = graphics.MeasureString(imgState.TestoFirmaPiccola, font1);
AdjustFontToFitWidth(graphics, image.Width, imgState, ref textSize);
// Adjust font if it's too large for the image dimensions
// Keep text height under 15% of image height to ensure proper spacing when resized
// This leaves room for margins and prevents clipping
int tempFontSize = imgState.DimensioneStandardMiniatura;
float maxTextHeight = image.Height * 0.15f;
while ((textSize.Width > image.Width * 0.95f || textSize.Height > maxTextHeight) && tempFontSize > 5)
{
tempFontSize = (tempFontSize > 20) ? tempFontSize - 5 : tempFontSize - 1;
using var tempFont = CreateFont(picSettings.IlFont, tempFontSize, picSettings.Grassetto);
textSize = graphics.MeasureString(imgState.TestoFirmaPiccola, tempFont);
}
SetVerticalPosition(image.Height, textSize.Height, imgState);
// Re-measure text with the final font size for accurate positioning
using var finalFont = CreateFont(picSettings.IlFont, tempFontSize, picSettings.Grassetto);
var finalTextSize = graphics.MeasureString(imgState.TestoFirmaPiccola, finalFont);
float xCenter = CalculateHorizontalAlignment(image.Width, textSize.Width);
SetVerticalPosition(image.Height, finalTextSize.Height, imgState);
float xCenter = CalculateHorizontalAlignment(image.Width, finalTextSize.Width);
using var stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
using var shadowBrush = new SolidBrush(Color.FromArgb(imgState.AlphaScelta, 0, 0, 0));
using var textBrush = new SolidBrush(Color.FromArgb(imgState.AlphaScelta, picSettings.FontColoreRGB));
imgState.DimensioneStandardMiniatura = picSettings.DimMin;
using var finalFont =
CreateFont(picSettings.IlFont, imgState.DimensioneStandardMiniatura, picSettings.Grassetto);
DrawText(graphics, imgState, xCenter, stringFormat, shadowBrush, textBrush, finalFont);
using var finalThumb =
@ -324,20 +334,6 @@ public class ImageCreatorSharp(PicSettings picSettings, ILogger<ImageCreatorShar
return best;
}
private void AdjustFontToFitWidth(Graphics g, int maxWidth, ImageState imgState, ref SizeF size)
{
int currentSize = imgState.DimensioneStandardMiniatura;
while (size.Width > maxWidth && currentSize > 5)
{
currentSize = (currentSize > 20) ? currentSize - 5 : currentSize - 1;
using var tempFont = CreateFont(picSettings.IlFont, currentSize, picSettings.Grassetto);
size = g.MeasureString(imgState.TestoFirmaPiccola, tempFont);
//tempFont.Dispose();
}
imgState.DimensioneStandardMiniatura = currentSize;
}
private void AddText(Image g, ImageState imgState, Bitmap imgOutputBig)
{
@ -493,11 +489,17 @@ public class ImageCreatorSharp(PicSettings picSettings, ILogger<ImageCreatorShar
imageAttributes.SetRemapTable(remapTable, ColorAdjustType.Bitmap);
// * The second color manipulation is used to change the opacity by setting the 3rd row and 3rd column to 0.3f
// Parse transparency safely (default to 100 if parsing fails)
if (!int.TryParse(picSettings.LogoTrasparenza, out var logoTransparencyValue))
{
logoTransparencyValue = 100;
}
var colorMatrixElements = new[]
{
new[] { 1.0F, 0.0F, 0.0F, 0.0F, 0.0F }, new[] { 0.0F, 1.0F, 0.0F, 0.0F, 0.0F },
new[] { 0.0F, 0.0F, 1.0F, 0.0F, 0.0F },
new[] { 0.0F, 0.0F, 0.0F, System.Convert.ToSingle(picSettings.LogoTrasparenza) / 100F, 0.0F },
new[] { 0.0F, 0.0F, 0.0F, System.Convert.ToSingle(logoTransparencyValue) / 100F, 0.0F },
new[] { 0.0F, 0.0F, 0.0F, 0.0F, 1.0F }
};
var wmColorMatrix = new ColorMatrix(colorMatrixElements);
@ -511,14 +513,27 @@ public class ImageCreatorSharp(PicSettings picSettings, ILogger<ImageCreatorShar
? CalculateThumbnailSize(logo.Width, logo.Height, fotoLogoW, "Larghezza")
: CalculateThumbnailSize(logo.Width, logo.Height, fotoLogoH, "Altezza");
var inPercentualeL = picSettings.LogoMargine.EndsWith('%');
var margineL = System.Convert.ToInt32(picSettings.LogoMargine);
// Guard against null/empty LogoMargine and percentage parsing
var logoMargineStr = picSettings.LogoMargine ?? string.Empty;
var inPercentualeL = logoMargineStr.EndsWith('%');
var margineL = 0;
if (inPercentualeL)
{
var trimmed = logoMargineStr.TrimEnd('%');
if (!int.TryParse(trimmed, out margineL)) margineL = 0;
}
else
{
if (!int.TryParse(logoMargineStr, out margineL)) margineL = 0;
}
var margineUsato =
inPercentualeL ? System.Convert.ToInt32(imgOutputBig.Height * margineL / (double)100) : margineL;
int xPosOfWm = 0;
int yPosOfWm = 0;
switch (picSettings.LogoPosizioneH.ToUpper())
var logoH = (picSettings.LogoPosizioneH ?? "NESSUNA").ToUpperInvariant();
var logoV = (picSettings.LogoPosizioneV ?? "NESSUNA").ToUpperInvariant();
switch (logoH)
{
case "SINISTRA":
case "NESSUNA":
@ -540,7 +555,7 @@ public class ImageCreatorSharp(PicSettings picSettings, ILogger<ImageCreatorShar
}
}
switch (picSettings.LogoPosizioneV.ToUpper())
switch (logoV)
{
case "ALTO":
case "NESSUNA":
@ -725,16 +740,42 @@ public class ImageCreatorSharp(PicSettings picSettings, ILogger<ImageCreatorShar
private void SetVerticalPosition(int imgHeight, float textHeight, ImageState imgState)
{
// Use 1% of image height as minimum margin, or 10px, whichever is larger
float minMargin = Math.Max(10f, imgHeight * 0.01f);
switch (picSettings.Posizione.ToUpper())
{
case "ALTO":
imgState.YPosFromBottom1 = picSettings.Margine;
imgState.YPosFromBottom4 = picSettings.MargVert;
imgState.YPosFromBottom1 = Math.Max(minMargin, picSettings.Margine);
imgState.YPosFromBottom4 = Math.Max(minMargin, picSettings.MargVert);
break;
case "BASSO":
imgState.YPosFromBottom1 = (float)(imgHeight - textHeight - (imgHeight * picSettings.Margine / 100.0));
imgState.YPosFromBottom4 = (float)(imgHeight - textHeight - (imgHeight * picSettings.MargVert / 100.0));
var bottomMargin1 = (float)(imgHeight * picSettings.Margine / 100.0);
var bottomMargin4 = (float)(imgHeight * picSettings.MargVert / 100.0);
// Position from bottom: bottom edge of text at desired margin from bottom
// Y = imageHeight - textHeight - bottomMargin
var desiredY1 = imgHeight - textHeight - bottomMargin1;
var desiredY4 = imgHeight - textHeight - bottomMargin4;
// Ensure text stays completely within bounds:
// - Top edge must be >= minMargin (not clipped at top)
// - Bottom edge must be <= imgHeight - minMargin (not clipped at bottom)
var maxAllowedY1 = imgHeight - textHeight - minMargin; // Maximum Y to keep bottom margin
var maxAllowedY4 = imgHeight - textHeight - minMargin;
imgState.YPosFromBottom1 = Math.Max(minMargin, Math.Min(desiredY1, maxAllowedY1));
imgState.YPosFromBottom4 = Math.Max(minMargin, Math.Min(desiredY4, maxAllowedY4));
break;
case "CENTRO":
default:
// Center the text vertically
var centeredY = (imgHeight - textHeight) / 2f;
// Clamp to ensure margins are respected
imgState.YPosFromBottom1 = Math.Max(minMargin, Math.Min(centeredY, imgHeight - textHeight - minMargin));
imgState.YPosFromBottom4 = imgState.YPosFromBottom1;
break;
}
}

View file

@ -9,7 +9,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AsyncEnumerator" Version="4.0.2" />
<PackageReference Include="AutoMapper" Version="16.0.0" />
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
<PackageReference Include="IDisposableAnalyzers" Version="4.0.8">
<PrivateAssets>all</PrivateAssets>
@ -20,7 +19,7 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.2" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
<PackageReference Include="System.Buffers" Version="4.6.1" />
<PackageReference Include="System.Collections.Immutable" Version="10.0.2" />
<PackageReference Include="System.Collections.Immutable" Version="9.0.7" />
<PackageReference Include="System.Memory" Version="4.6.3" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.2" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
@ -28,6 +27,6 @@
<PackageReference Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.4.421302">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Windows.Compatibility" Version="10.0.2" />
<PackageReference Include="Microsoft.Windows.Compatibility" Version="9.0.7" />
</ItemGroup>
</Project>

View file

@ -38,9 +38,10 @@ public class PicSettings
public int LogoLarghezza { get; set; }
public Color FontColoreRGB { get; set; }
public bool LogoAggiungi { get; set; }
public string LogoNomeFile { get; set; }
public string LogoTrasparenza { get; set; }
public string LogoMargine { get; set; }
// Initialize logo-related strings to safe defaults to avoid null reference issues
public string LogoNomeFile { get; set; } = string.Empty;
public string LogoTrasparenza { get; set; } = "100";
public string LogoMargine { get; set; } = "0";
public string LogoPosizioneH { get; set; }
public string LogoPosizioneV { get; set; }
public bool FotoGrandeDimOrigina { get; set; }