using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Globalization; using System.IO; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using SixLabors.ImageSharp.Metadata.Profiles.Exif; // Imports System.Threading namespace MaddoShared; [SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")] public class ImageCreatorSharp(PicSettings picSettings, ILogger logger) { public async Task CreateImageAsync(ImageState imgState, Image logo) { try { await Task.Run(() => { logger.LogInformation("File: {FileInfo} Dest: {DirectoryInfo}", imgState.WorkFile, imgState.DestDir); PrepareVariables(imgState); ExtractExif(imgState); using var g = Image.FromFile(imgState.WorkFile.FullName); // Set extra text SetExtraText(g, imgState); // Rotate image according to EXIF ApplyRotation(g, imgState); // Force jpeg if option selected var thisFormat = g.RawFormat; if (picSettings.UsaForzaJpg) thisFormat = ImageFormat.Jpeg; PrepareThumbnailSize(g, imgState); using var imgOutputBig = new Bitmap(g, imgState.ThumbSizeBig.Width, imgState.ThumbSizeBig.Height); imgOutputBig.SetResolution(g.HorizontalResolution, g.VerticalResolution); // Create thumbnails CreateThumbnails(g, imgState, imgOutputBig, thisFormat); AddText(g, imgState, imgOutputBig); AddLogo(imgOutputBig, logo); SavePhoto(imgOutputBig, imgState, thisFormat); }).ConfigureAwait(false); } catch (Exception ex) { var e = ex.Demystify(); logger.LogError(e, "Error in processing photo {WorkFileName}", imgState.WorkFile.Name); } } private void ExtractExif(ImageState imgState) { using var img = SixLabors.ImageSharp.Image.Load(imgState.WorkFile.FullName); imgState.Orientation = Orientations.TopLeft; IExifValue rotation = null; var exifProfile = img.Metadata?.ExifProfile; var found = exifProfile != null && exifProfile.TryGetValue(ExifTag.Orientation, out rotation); if (found) { var intOrientation = rotation.Value.ToInt32(); imgState.Orientation = (Orientations)intOrientation; } IExifValue date = null; var creationFound = exifProfile != null && exifProfile.TryGetValue(ExifTag.DateTimeOriginal, out date); if (creationFound) { var succ = DateTime.TryParseExact(date.Value, "yyyy:MM:dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var crDate); if (succ) { imgState.CreationDate = crDate; } else { imgState.CreationDate = null; } } else { imgState.CreationDate = null; } } private void ApplyRotation(Image g, ImageState imgState) { imgState.FotoRuotaADestra = false; imgState.FotoRuotaASinistra = false; if (picSettings.UsaRotazioneAutomatica && g.PropertyIdList.Length > 0) { switch (imgState.Orientation) { case Orientations.BottomLeft: case Orientations.BottomRight: case Orientations.LeftTop: case Orientations.LftBottom: imgState.FotoRuotaASinistra = true; break; case Orientations.RightBottom: case Orientations.RightTop: case Orientations.TopLeft: case Orientations.TopRight: break; } } if (imgState.FotoRuotaASinistra) g.RotateFlip(RotateFlipType.Rotate270FlipNone); if (imgState.FotoRuotaADestra) g.RotateFlip(RotateFlipType.Rotate90FlipNone); } /// /// ''' Aggiunge Orario, tempo gara e altri /// ''' /// ''' Image /// /// ''' private void SetExtraText(Image g, ImageState imgState) { if (picSettings.UsaOrarioTestoApplicare || picSettings.UsaTempoGaraTestoApplicare || picSettings.UsaOrarioMiniatura || picSettings.TestoMin || picSettings.AggTempoGaraMin || picSettings.AggNumTempMin) { if (g.PropertyIdList.Length <= 0) return; imgState.DataFoto = imgState.CreationDate ?? DateTime.Now; imgState.TestoFirma = picSettings.TestoFirmaStart; imgState.TestoFirmaV = picSettings.TestoFirmaStartV; if (imgState.DataFoto.Year == 1) return; imgState.TestoFirmaPiccola = imgState.DataFoto.ToShortTimeString(); if (picSettings.UsaOrarioTestoApplicare) { imgState.TestoFirma += $" {imgState.DataFoto.ToShortDateString()} {imgState.DataFoto.ToLongTimeString()}"; imgState.TestoFirmaV += $" {imgState.DataFoto.ToShortDateString()} {imgState.DataFoto.ToLongTimeString()}"; } if (!picSettings.UsaTempoGaraTestoApplicare) return; var diff = imgState.DataFoto - imgState.DataPartenzaI; imgState.TestoFirma += $" {imgState.TestoOrario}{diff.Hours:00}:{diff.Minutes:00}:{diff.Seconds:00}"; imgState.TestoFirmaV += $" {imgState.TestoOrario}{diff.Hours:00}:{diff.Minutes:00}:{diff.Seconds:00}"; } else { imgState.TestoFirma = picSettings.TestoFirmaStart; imgState.TestoFirmaV = picSettings.TestoFirmaStartV; } } /// /// ''' Prepara diverse variabili azzerandole, elaborandole e prendendole dalle impostazioni /// ''' /// ''' private void PrepareVariables(ImageState imgState) { imgState.AlphaScelta = System.Convert.ToInt32((255 * (100 - picSettings.Trasparenza) / (double)100)); imgState.TestoFirma = ""; imgState.TestoFirmaV = ""; imgState.DataPartenzaI = picSettings.DataPartenza; imgState.TestoOrario = picSettings.TestoOrario; if (imgState.TestoOrario.Length > 0) imgState.TestoOrario += " "; imgState.TestoFirmaPiccola = ""; imgState.ThumbSizeSmall = new Size(); imgState.ThumbSizeBig = new Size(); imgState.NomeFileSmall = ""; imgState.NomeFileBig2 = ""; imgState.NomeFileBig = ""; imgState.DimensioneStandard = picSettings.DimStandard; imgState.DimensioneStandardMiniatura = picSettings.DimStandardMiniatura; // nomeFileSmall = Suffisso & NomeFileChild // nomeFileBig = NomeFileChild imgState.NomeFileSmall = picSettings.Suffisso + imgState.WorkFile.Name; imgState.NomeFileBig = imgState.WorkFile.Name; } private void PrepareThumbnailSize(Image g, ImageState imgState) { if (g.Width > g.Height) { imgState.ThumbSizeSmall = CalculateThumbnailSize(g.Width, g.Height, picSettings.LarghezzaSmall, "Larghezza"); var sizeOrig = new Size(g.Width, g.Height); imgState.ThumbSizeBig = sizeOrig; } else { imgState.ThumbSizeSmall = CalculateThumbnailSize(g.Width, g.Height, picSettings.AltezzaSmall, "Altezza"); var sizeOrig = new Size(g.Width, g.Height); imgState.ThumbSizeBig = sizeOrig; } } private void CreateThumbnails(Image sourceImage, ImageState imgState, Bitmap imgOutputBig, ImageFormat format) { if (!picSettings.CreaMiniature || picSettings.AggiungiScritteMiniature) return; PrepareSignatureText(imgState); if (IsSameDirectory(picSettings.DirectorySorgente, picSettings.DirectoryDestinazione)) UpdateFilenameWithCode(imgState); if (ShouldRenderText()) CreateThumbnailWithText(sourceImage, imgState, imgOutputBig, format); else CreateSimpleThumbnail(sourceImage, imgState, format); } private void PrepareSignatureText(ImageState imgState) { if (picSettings.TestoMin) imgState.TestoFirmaPiccola = imgState.NomeFileBig; else if (picSettings.AggNumTempMin) imgState.TestoFirmaPiccola = imgState.NomeFileBig + " "; } private bool IsSameDirectory(string dir1, string dir2) => string.Equals(dir1, dir2, StringComparison.OrdinalIgnoreCase); private void UpdateFilenameWithCode(ImageState imgState) { var name = imgState.NomeFileSmall; imgState.NomeFileSmall = name[..^4] + picSettings.Codice + name[^4..]; } private bool ShouldRenderText() => picSettings.UsaOrarioMiniatura || picSettings.TestoMin || picSettings.AggTempoGaraMin || picSettings.AggNumTempMin; private void CreateSimpleThumbnail(Image image, ImageState imgState, ImageFormat format) { using var thumbnail = new Bitmap(image, imgState.ThumbSizeSmall.Width, imgState.ThumbSizeSmall.Height); thumbnail.Save(Path.Combine(imgState.DestDir.FullName, imgState.NomeFileSmall), format); } private void CreateThumbnailWithText(Image image, ImageState imgState, Bitmap sourceBitmap, ImageFormat format) { if (imgState.TestoFirmaPiccola.Length == 0) { CreateSimpleThumbnail(image, imgState, format); return; } using var imgOutputSmall = (Bitmap)sourceBitmap.Clone(); using var graphics = Graphics.FromImage(imgOutputSmall); graphics.SmoothingMode = SmoothingMode.AntiAlias; // 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); // 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); } // 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); 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)); DrawText(graphics, imgState, xCenter, stringFormat, shadowBrush, textBrush, finalFont); using var finalThumb = new Bitmap(imgOutputSmall, imgState.ThumbSizeSmall.Width, imgState.ThumbSizeSmall.Height); finalThumb.Save(Path.Combine(imgState.DestDir.FullName, imgState.NomeFileSmall), format); } private Font CreateFont(string fontName, int size, bool bold) => new Font(fontName, size, bold ? FontStyle.Bold : FontStyle.Regular); private int FindBestFontSize(Graphics g, string text, string fontName, int maxSize, bool bold, int maxWidth, int minSize = 5) { if (maxSize <= minSize) return Math.Max(minSize, maxSize); int low = minSize; int high = Math.Max(minSize, maxSize); int best = minSize; while (low <= high) { int mid = (low + high) / 2; using var testFont = CreateFont(fontName, mid, bold); var measured = g.MeasureString(text, testFont); if (measured.Width <= maxWidth) { best = mid; low = mid + 1; // try larger } else { high = mid - 1; // too big } } return best; } private void AddText(Image g, ImageState imgState, Bitmap imgOutputBig) { using var grPhoto = Graphics.FromImage(imgOutputBig); grPhoto.SmoothingMode = SmoothingMode.AntiAlias; // Determine best base font size using a binary search (faster than decremental loop) int availableWidth = (int)g.Width; int targetBaseSize = imgState.DimensioneStandard > 0 ? imgState.DimensioneStandard : picSettings.DimStandard; int bestBaseSize = FindBestFontSize(grPhoto, imgState.TestoFirma ?? string.Empty, picSettings.IlFont, targetBaseSize, picSettings.Grassetto, availableWidth); imgState.DimensioneStandard = bestBaseSize; // Decide final drawing size (use DimVert if rotated) int drawSize = (imgState.FotoRuotaADestra || imgState.FotoRuotaASinistra) ? picSettings.DimVert : imgState.DimensioneStandard; using var drawFont = CreateFont(picSettings.IlFont, drawSize, picSettings.Grassetto); var crSize = grPhoto.MeasureString(imgState.TestoFirma ?? string.Empty, drawFont); var larghezzaStandard = Convert.ToInt32(crSize.Width); // Vertical positions switch (picSettings.Posizione.ToUpper()) { case "ALTO": { imgState.YPosFromBottom = picSettings.Margine; imgState.YPosFromBottom3 = picSettings.MargVert; break; } case "BASSO": { imgState.YPosFromBottom = Convert.ToSingle((g.Height - crSize.Height - (g.Height * picSettings.Margine / 100.0))); imgState.YPosFromBottom3 = Convert.ToSingle((g.Height - crSize.Height - (g.Height * picSettings.MargVert / 100.0))); break; } } float xCenterOfImg = 0; using var strFormat = new StringFormat(); switch (picSettings.Allineamento.ToUpper()) { case "SINISTRA": { xCenterOfImg = Convert.ToSingle((picSettings.Margine + (larghezzaStandard / (double)2))); if ((larghezzaStandard / (double)2) > (g.Width / (double)2) - picSettings.Margine) xCenterOfImg = Convert.ToSingle((g.Width / (double)2)); break; } case "CENTRO": { xCenterOfImg = Convert.ToSingle((g.Width / (double)2)); break; } case "DESTRA": { xCenterOfImg = Convert.ToSingle((g.Width - picSettings.Margine - (larghezzaStandard / (double)2))); if ((larghezzaStandard / (double)2) > (g.Width / (double)2) - picSettings.Margine) xCenterOfImg = Convert.ToSingle((g.Width / (double)2)); break; } } strFormat.Alignment = StringAlignment.Center; using var semiTransBrush2 = new SolidBrush(Color.FromArgb(imgState.AlphaScelta, 0, 0, 0)); using var semiTransBrush = new SolidBrush(Color.FromArgb(imgState.AlphaScelta, picSettings.FontColoreRGB)); // write text (NomeFileBig) if (picSettings.TestoNome) { if (picSettings.NomeData && g.PropertyIdList.Length > 0) { imgState.DataFoto = imgState.CreationDate ?? DateTime.Now; grPhoto.DrawString((imgState.NomeFileBig + " " + imgState.DataFoto.ToShortDateString()), drawFont, semiTransBrush2, new PointF(xCenterOfImg + 1, imgState.YPosFromBottom + 1), strFormat); grPhoto.DrawString((imgState.NomeFileBig + " " + imgState.DataFoto.ToShortDateString()), drawFont, semiTransBrush, new PointF(xCenterOfImg, imgState.YPosFromBottom), strFormat); } else { grPhoto.DrawString(imgState.NomeFileBig, drawFont, semiTransBrush2, new PointF(xCenterOfImg + 1, imgState.YPosFromBottom + 1), strFormat); grPhoto.DrawString(imgState.NomeFileBig, drawFont, semiTransBrush, new PointF(xCenterOfImg, imgState.YPosFromBottom), strFormat); } } else { if (imgState.FotoRuotaADestra || imgState.FotoRuotaASinistra) { if (!picSettings.TestoMin) { grPhoto.DrawString(imgState.TestoFirmaV, drawFont, semiTransBrush2, new PointF(xCenterOfImg + 1, imgState.YPosFromBottom3 + 1), strFormat); grPhoto.DrawString(imgState.TestoFirmaV, drawFont, semiTransBrush, new PointF(xCenterOfImg, imgState.YPosFromBottom3), strFormat); } if (picSettings.TestoMin) { grPhoto.DrawString(imgState.TestoFirmaV, drawFont, semiTransBrush2, new PointF(xCenterOfImg + 1, imgState.YPosFromBottom4 + 1), strFormat); grPhoto.DrawString(imgState.TestoFirmaV, drawFont, semiTransBrush, new PointF(xCenterOfImg, imgState.YPosFromBottom4), strFormat); } } else { grPhoto.DrawString(imgState.TestoFirma, drawFont, semiTransBrush2, new PointF(xCenterOfImg + 1, imgState.YPosFromBottom + 1), strFormat); grPhoto.DrawString(imgState.TestoFirma, drawFont, semiTransBrush, new PointF(xCenterOfImg, imgState.YPosFromBottom), strFormat); } } if (string.Equals(picSettings.DirectorySorgente, picSettings.DirectoryDestinazione, StringComparison.OrdinalIgnoreCase)) { imgState.NomeFileBig2 = imgState.NomeFileBig; imgState.NomeFileBig = $"{imgState.NomeFileBig[..^4]}{picSettings.Codice}{imgState.NomeFileBig[^4..]}"; } } private void AddLogo(Bitmap imgOutputBig, Image logo) { // Skip if no logo provided if (logo is null) return; // Load check (use short-circuit &&) if (!(picSettings.LogoAggiungi && File.Exists(picSettings.LogoNomeFile))) return; var logoColoreTrasparente = Color.White; // * Load this Bitmap into a new Graphic Object using var grWatermark = Graphics.FromImage(imgOutputBig); using ImageAttributes imageAttributes = new ImageAttributes(); // * The first step replace the background color with one that is transparent (Alpha=0, R=0, G=0, B=0) var colorMap = new ColorMap { // * background this will be the color we search for and replace with transparency OldColor = logoColoreTrasparente, NewColor = Color.FromArgb(0, 0, 0, 0) }; var remapTable = new[] { colorMap }; 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(logoTransparencyValue) / 100F, 0.0F }, new[] { 0.0F, 0.0F, 0.0F, 0.0F, 1.0F } }; var wmColorMatrix = new ColorMatrix(colorMatrixElements); imageAttributes.SetColorMatrix(wmColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); var fotoLogoH = picSettings.LogoAltezza; var fotoLogoW = picSettings.LogoLarghezza; var fattoreAlt = logo.Height / (double)fotoLogoH; var fattoreLarg = logo.Width / (double)fotoLogoW; var nuovaSize = fattoreLarg > fattoreAlt ? CalculateThumbnailSize(logo.Width, logo.Height, fotoLogoW, "Larghezza") : CalculateThumbnailSize(logo.Width, logo.Height, fotoLogoH, "Altezza"); // 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; var logoH = (picSettings.LogoPosizioneH ?? "NESSUNA").ToUpperInvariant(); var logoV = (picSettings.LogoPosizioneV ?? "NESSUNA").ToUpperInvariant(); switch (logoH) { case "SINISTRA": case "NESSUNA": { xPosOfWm = margineUsato; break; } case "CENTRO": { xPosOfWm = System.Convert.ToInt32((imgOutputBig.Width - nuovaSize.Width) / (double)2); break; } case "DESTRA": { xPosOfWm = ((imgOutputBig.Width - nuovaSize.Width) - margineUsato); break; } } switch (logoV) { case "ALTO": case "NESSUNA": { yPosOfWm = margineUsato; break; } case "CENTRO": { yPosOfWm = System.Convert.ToInt32((imgOutputBig.Height - nuovaSize.Height) / (double)2); break; } case "BASSO": { yPosOfWm = ((imgOutputBig.Height - nuovaSize.Height) - margineUsato); break; } } grWatermark.DrawImage(logo, new Rectangle(xPosOfWm, yPosOfWm, nuovaSize.Width, nuovaSize.Height), 0, 0, logo.Width, logo.Height, GraphicsUnit.Pixel, imageAttributes); //grWatermark.Dispose(); } private void SavePhoto(Bitmap imgOutputBig, ImageState imgState, ImageFormat thisFormat) { var fileName = Path.Combine(imgState.DestDir.FullName, imgState.NomeFileBig); using var image1Stream = new MemoryStream(); if (picSettings.FotoGrandeDimOrigina == false) { // attenzione non controlla se è png // imgOutputBig.Save(Path.Combine(_DestDir.FullName, "Temp_" & NomeFileBig), thisFormat) if (thisFormat.Equals(ImageFormat.Jpeg)) { MakeImageCustomQuality(imgOutputBig, image1Stream, picSettings.JpegQuality); } //SalvaImmagineCustomQuality(imgOutputBig, Path.Combine(DestDir.FullName, "Temp_" + NomeFileBig), _picSettings.jpegQuality); else { imgOutputBig.Save(image1Stream, thisFormat); } //imgOutputBig.Save(Path.Combine(DestDir.FullName, "Temp_" + NomeFileBig), thisFormat); image1Stream.Seek(0, SeekOrigin.Begin); using var g2 = Image.FromStream(image1Stream); imgState.ThumbSizeBig = g2.Width > g2.Height ? CalculateThumbnailSize(g2.Width, g2.Height, picSettings.LarghezzaBig, "Larghezza") : CalculateThumbnailSize(g2.Width, g2.Height, picSettings.AltezzaBig, "Altezza"); using var imgOutputBig2 = new Bitmap(g2, imgState.ThumbSizeBig.Width, imgState.ThumbSizeBig.Height); if (!picSettings.OverwriteFiles && File.Exists(fileName)) { logger.LogInformation("Saltata foto {FileName}, esiste", fileName); } else { if (thisFormat.Equals(ImageFormat.Jpeg)) SaveImageCustomQuality(imgOutputBig2, fileName, picSettings.JpegQuality); else imgOutputBig2.Save(fileName, thisFormat); } } else { if (!picSettings.OverwriteFiles && File.Exists(fileName)) { logger.LogInformation("Saltata foto {FileName}, esiste", fileName); } else { if (thisFormat.Equals(ImageFormat.Jpeg)) SaveImageCustomQuality(imgOutputBig, fileName, picSettings.JpegQuality); else imgOutputBig.Save(fileName, thisFormat); } } image1Stream.Seek(0, SeekOrigin.Begin); if (!picSettings.CreaMiniature) return; if (!picSettings.AggiungiScritteMiniature) return; using var g1 = picSettings.FotoGrandeDimOrigina ? (Image)imgOutputBig.Clone() : Image.FromStream(image1Stream); using var imgOutputSmall = new Bitmap(g1, imgState.ThumbSizeSmall.Width, imgState.ThumbSizeSmall.Height); if (string.Equals(picSettings.DirectorySorgente, picSettings.DirectoryDestinazione, StringComparison.OrdinalIgnoreCase)) imgState.NomeFileSmall = imgState.NomeFileSmall.Substring(0, imgState.NomeFileSmall.Length - 4) + picSettings.Codice + imgState.NomeFileSmall.Substring(imgState.NomeFileSmall.Length - 4); var tnFileName = Path.Combine(imgState.DestDir.FullName, imgState.NomeFileSmall); if (!picSettings.OverwriteFiles && File.Exists(tnFileName)) { logger.LogInformation("Saltata miniatura foto {TnFileName}, esiste", tnFileName); } else { if (thisFormat.Equals(ImageFormat.Jpeg)) SaveImageCustomQuality(imgOutputSmall, tnFileName, picSettings.JpegQualityMin); else imgOutputSmall.Save(tnFileName, thisFormat); } } private void SaveImageCustomQuality(Bitmap imageToSave, string nomeFileFinale, long quality) { var jgpEncoder = GetEncoder(ImageFormat.Jpeg); var myEncoder = System.Drawing.Imaging.Encoder.Quality; using var myEncoderParameters = new EncoderParameters(1); var myEncoderParameter = new EncoderParameter(myEncoder, quality); myEncoderParameters.Param[0] = myEncoderParameter; imageToSave.Save(nomeFileFinale, jgpEncoder, myEncoderParameters); //imageToSave.Dispose(); } private void MakeImageCustomQuality(Bitmap imageToSave, Stream destinationStream, long quality) { var jgpEncoder = GetEncoder(ImageFormat.Jpeg); var myEncoder = System.Drawing.Imaging.Encoder.Quality; using var myEncoderParameters = new EncoderParameters(1); var myEncoderParameter = new EncoderParameter(myEncoder, quality); myEncoderParameters.Param[0] = myEncoderParameter; destinationStream.Seek(0, SeekOrigin.Begin); imageToSave.Save(destinationStream, jgpEncoder, myEncoderParameters); //imageToSave.Dispose(); } private ImageCodecInfo GetEncoder(ImageFormat format) { var codecs = ImageCodecInfo.GetImageDecoders(); foreach (var codec in codecs) { if (codec.FormatID == format.Guid) return codec; } return null /* TODO Change to default(_) if this is not a reference type */; } /// /// ''' Calculate the Size of the New image /// ''' /// ''' Larghezza /// ''' Altezza /// ''' /// ''' /// ''' /// ''' private Size CalculateThumbnailSize(int currentwidth, int currentheight, int maxPixel, string tipoSize) { // e // *** Larghezza, Altezza, Auto double tempMultiplier; if (tipoSize.ToUpper() == "Larghezza".ToUpper()) tempMultiplier = maxPixel / (double)currentwidth; else if (tipoSize.ToUpper() == "Altezza".ToUpper()) tempMultiplier = maxPixel / (double)currentheight; else if (currentheight > currentwidth) tempMultiplier = maxPixel / (double)currentheight; else tempMultiplier = maxPixel / (double)currentwidth; var newSize = new Size(System.Convert.ToInt32(currentwidth * tempMultiplier), System.Convert.ToInt32(currentheight * tempMultiplier)); return newSize; } 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 = Math.Max(minMargin, picSettings.Margine); imgState.YPosFromBottom4 = Math.Max(minMargin, picSettings.MargVert); break; case "BASSO": 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; } } private float CalculateHorizontalAlignment(int imgWidth, float textWidth) { double halfWidth = textWidth / 2.0; return picSettings.Allineamento.ToUpper() switch { "SINISTRA" => (float)Math.Min(picSettings.Margine + halfWidth, imgWidth / 2.0), "DESTRA" => (float)Math.Max(imgWidth - picSettings.Margine - halfWidth, imgWidth / 2.0), _ => imgWidth / 2.0f, // CENTRO or default }; } private void DrawText(Graphics g, ImageState imgState, float x, StringFormat format, Brush shadowBrush, Brush textBrush, Font font) { string content = imgState.TestoFirmaPiccola; if (picSettings.TestoMin) { content = imgState.NomeFileBig; } else if (picSettings.AggTempoGaraMin && picSettings.UsaTempoGaraTestoApplicare) { content = FormatTimeText(imgState, includeFileName: false); } else if (picSettings.AggNumTempMin) { content = FormatTimeText(imgState, includeFileName: true); } var offset = new PointF(x + 1, imgState.YPosFromBottom1 + 1); var actual = new PointF(x, imgState.YPosFromBottom1); g.DrawString(content, font, shadowBrush, offset, format); g.DrawString(content, font, textBrush, actual, format); } private string FormatTimeText(ImageState imgState, bool includeFileName) { var diff = imgState.DataPartenzaI - imgState.DataFoto; var ticks = (long)(diff.TotalSeconds * 10000000); var time = new TimeSpan(ticks); var formatted = $"{imgState.TestoOrario}{time:hh\\:mm\\:ss}"; return includeFileName ? $"{imgState.NomeFileBig}{Environment.NewLine}{formatted}" : Environment.NewLine + formatted; } }