Add color-key transparency support for logo overlays

- Allow users to select a transparent color for logo images (watermarks) via UI (checkbox, color picker, preview).
- Apply color-key transparency in both System.Drawing and ImageSharp backends: specified color in logo is made fully transparent.
- Persist transparency settings in PicSettings and SettingsDto; bind to DataModel and UI controls.
- Update logo preview to reflect transparency in real time.
- Add option to select image processing library (System.Drawing or ImageSharp) in UI and settings.
- Fix bug in SettingsService parameter loading for int/double/DateTime.
- Fully integrate color-key transparency into image processing and settings serialization.
This commit is contained in:
MaddoScientisto 2026-02-15 11:13:23 +01:00
commit 8872080741
9 changed files with 732 additions and 168 deletions

View file

@ -30,6 +30,7 @@ namespace ImageCatalog_2
public ICommand SaveSettingsCommand { get; }
public ICommand LoadSettingsCommand { get; }
public ICommand SelectColorCommand { get; }
public ICommand SelectTransparentColorCommand { get; }
private readonly ITestService _service;
private readonly ILogger<DataModel> _logger;
@ -67,6 +68,7 @@ namespace ImageCatalog_2
SaveSettingsCommand = new RelayCommand(SaveSettings);
LoadSettingsCommand = new RelayCommand(LoadSettings);
SelectColorCommand = new RelayCommand(SelectColor);
SelectTransparentColorCommand = new RelayCommand(SelectTransparentColor);
// Load available fonts
AvailableFonts = LoadAvailableFonts();
@ -341,6 +343,28 @@ namespace ImageCatalog_2
}
}
private string _transparentColor = "#FFFFFF";
public string TransparentColor
{
get => _transparentColor;
set
{
_transparentColor = value;
NotifyPropertyChanged();
}
}
private bool _useTransparentColor;
public bool UseTransparentColor
{
get => _useTransparentColor;
set
{
_useTransparentColor = value;
NotifyPropertyChanged();
}
}
// Logo/Watermark settings
private string _logoFile = "";
public string LogoFile
@ -1129,6 +1153,7 @@ namespace ImageCatalog_2
public event EventHandler SelectColorRequested;
// Request that the View shows a message to the user (message, caption, icon)
public event EventHandler<Tuple<string, string, MessageBoxIcon>> ShowMessageRequested;
public event EventHandler SelectTransparentColorRequested;
private void SelectSourceFolder(object parameter)
{
@ -1160,6 +1185,11 @@ namespace ImageCatalog_2
SelectColorRequested?.Invoke(this, EventArgs.Empty);
}
private void SelectTransparentColor(object parameter)
{
SelectTransparentColorRequested?.Invoke(this, EventArgs.Empty);
}
public async Task SaveSettingsToFileAsync(string filePath)
{
await _settingsService.SaveSettingsAsync(filePath, this);

View file

@ -45,6 +45,9 @@ namespace ImageCatalog
Label43 = new Label();
TabControl1 = new TabControl();
TabPage5 = new TabPage();
groupBox12 = new GroupBox();
rdbLibrary2 = new RadioButton();
rdbLibrary1 = new RadioButton();
GroupBox11 = new GroupBox();
numericUpDown2 = new NumericUpDown();
numericUpDown1 = new NumericUpDown();
@ -150,7 +153,6 @@ namespace ImageCatalog
CheckBox1 = new CheckBox();
TabPage4 = new TabPage();
GroupBox6 = new GroupBox();
PictureBox2 = new PictureBox();
_PictureBox1 = new PictureBox();
ComboBox5 = new ComboBox();
ComboBox4 = new ComboBox();
@ -167,8 +169,6 @@ namespace ImageCatalog
_Button4 = new Button();
TextBox10 = new TextBox();
Label29 = new Label();
Label30 = new Label();
PictureBox3 = new PictureBox();
versionLabel = new Label();
_Button7 = new Button();
_Button5 = new Button();
@ -181,13 +181,15 @@ namespace ImageCatalog
_btnCreaCatalogoAsync = new Button();
timer1 = new System.Windows.Forms.Timer(components);
dataModelBindingSource1 = new BindingSource(components);
groupBox12 = new GroupBox();
rdbLibrary1 = new RadioButton();
rdbLibrary2 = new RadioButton();
PictureBox3 = new PictureBox();
colorDialog1 = new ColorDialog();
btnSetTransparency = new Button();
chkUseTransparentColor = new CheckBox();
((System.ComponentModel.ISupportInitialize)bindingSource1).BeginInit();
((System.ComponentModel.ISupportInitialize)dataModelBindingSource).BeginInit();
TabControl1.SuspendLayout();
TabPage5.SuspendLayout();
groupBox12.SuspendLayout();
GroupBox11.SuspendLayout();
((System.ComponentModel.ISupportInitialize)numericUpDown2).BeginInit();
((System.ComponentModel.ISupportInitialize)numericUpDown1).BeginInit();
@ -208,11 +210,9 @@ namespace ImageCatalog
Panel2.SuspendLayout();
TabPage4.SuspendLayout();
GroupBox6.SuspendLayout();
((System.ComponentModel.ISupportInitialize)PictureBox2).BeginInit();
((System.ComponentModel.ISupportInitialize)_PictureBox1).BeginInit();
((System.ComponentModel.ISupportInitialize)PictureBox3).BeginInit();
((System.ComponentModel.ISupportInitialize)dataModelBindingSource1).BeginInit();
groupBox12.SuspendLayout();
((System.ComponentModel.ISupportInitialize)PictureBox3).BeginInit();
SuspendLayout();
//
// ProgressBar1
@ -286,6 +286,41 @@ namespace ImageCatalog
TabPage5.Text = "Generale";
TabPage5.UseVisualStyleBackColor = true;
//
// groupBox12
//
groupBox12.Controls.Add(rdbLibrary2);
groupBox12.Controls.Add(rdbLibrary1);
groupBox12.Location = new Point(405, 625);
groupBox12.Name = "groupBox12";
groupBox12.Size = new Size(350, 175);
groupBox12.TabIndex = 49;
groupBox12.TabStop = false;
groupBox12.Text = "Libreria Manipolazione Grafica";
//
// rdbLibrary2
//
rdbLibrary2.AutoSize = true;
rdbLibrary2.DataBindings.Add(new Binding("Checked", bindingSource1, "UseImageSharp", true, DataSourceUpdateMode.OnPropertyChanged));
rdbLibrary2.Location = new Point(12, 77);
rdbLibrary2.Name = "rdbLibrary2";
rdbLibrary2.Size = new Size(149, 34);
rdbLibrary2.TabIndex = 1;
rdbLibrary2.TabStop = true;
rdbLibrary2.Text = "ImageSharp";
rdbLibrary2.UseVisualStyleBackColor = true;
//
// rdbLibrary1
//
rdbLibrary1.AutoSize = true;
rdbLibrary1.DataBindings.Add(new Binding("Checked", bindingSource1, "UseSystemGraphics", true, DataSourceUpdateMode.OnPropertyChanged));
rdbLibrary1.Location = new Point(12, 37);
rdbLibrary1.Name = "rdbLibrary1";
rdbLibrary1.Size = new Size(188, 34);
rdbLibrary1.TabIndex = 0;
rdbLibrary1.TabStop = true;
rdbLibrary1.Text = "System.Graphics";
rdbLibrary1.UseVisualStyleBackColor = true;
//
// GroupBox11
//
GroupBox11.Controls.Add(numericUpDown2);
@ -1491,7 +1526,8 @@ namespace ImageCatalog
//
// GroupBox6
//
GroupBox6.Controls.Add(PictureBox2);
GroupBox6.Controls.Add(chkUseTransparentColor);
GroupBox6.Controls.Add(btnSetTransparency);
GroupBox6.Controls.Add(_PictureBox1);
GroupBox6.Controls.Add(ComboBox5);
GroupBox6.Controls.Add(ComboBox4);
@ -1508,29 +1544,17 @@ namespace ImageCatalog
GroupBox6.Controls.Add(_Button4);
GroupBox6.Controls.Add(TextBox10);
GroupBox6.Controls.Add(Label29);
GroupBox6.Controls.Add(Label30);
GroupBox6.Controls.Add(PictureBox3);
GroupBox6.ForeColor = Color.FromArgb(0, 0, 192);
GroupBox6.Location = new Point(12, 13);
GroupBox6.Margin = new Padding(6, 8, 6, 8);
GroupBox6.Name = "GroupBox6";
GroupBox6.Padding = new Padding(6, 8, 6, 8);
GroupBox6.Size = new Size(991, 518);
GroupBox6.Size = new Size(991, 593);
GroupBox6.TabIndex = 42;
GroupBox6.TabStop = false;
GroupBox6.Text = "Logo";
//
// PictureBox2
//
PictureBox2.BorderStyle = BorderStyle.FixedSingle;
PictureBox2.Location = new Point(288, 442);
PictureBox2.Margin = new Padding(6, 8, 6, 8);
PictureBox2.Name = "PictureBox2";
PictureBox2.Size = new Size(45, 53);
PictureBox2.TabIndex = 44;
PictureBox2.TabStop = false;
PictureBox2.Visible = false;
//
// _PictureBox1
//
_PictureBox1.Cursor = Cursors.Cross;
@ -1575,7 +1599,7 @@ namespace ImageCatalog
// Label28
//
Label28.ForeColor = Color.Black;
Label28.Location = new Point(34, 223);
Label28.Location = new Point(38, 222);
Label28.Margin = new Padding(6, 0, 6, 0);
Label28.Name = "Label28";
Label28.Size = new Size(240, 38);
@ -1587,7 +1611,7 @@ namespace ImageCatalog
//
CheckBox5.DataBindings.Add(new Binding("Checked", bindingSource1, "AddLogo", true, DataSourceUpdateMode.OnPropertyChanged));
CheckBox5.ForeColor = Color.Black;
CheckBox5.Location = new Point(0, 60);
CheckBox5.Location = new Point(12, 56);
CheckBox5.Margin = new Padding(6, 8, 6, 8);
CheckBox5.Name = "CheckBox5";
CheckBox5.Size = new Size(144, 47);
@ -1618,7 +1642,7 @@ namespace ImageCatalog
//
Label25.AutoSize = true;
Label25.ForeColor = Color.Black;
Label25.Location = new Point(34, 387);
Label25.Location = new Point(39, 390);
Label25.Margin = new Padding(6, 0, 6, 0);
Label25.Name = "Label25";
Label25.Size = new Size(183, 30);
@ -1695,7 +1719,7 @@ namespace ImageCatalog
//
Label29.AutoSize = true;
Label29.ForeColor = Color.Black;
Label29.Location = new Point(34, 339);
Label29.Location = new Point(38, 341);
Label29.Margin = new Padding(6, 0, 6, 0);
Label29.Name = "Label29";
Label29.Size = new Size(208, 30);
@ -1703,30 +1727,6 @@ namespace ImageCatalog
Label29.Text = "Posizione orizzontale";
Label29.TextAlign = ContentAlignment.MiddleLeft;
//
// Label30
//
Label30.AutoSize = true;
Label30.ForeColor = Color.Black;
Label30.Location = new Point(38, 468);
Label30.Margin = new Padding(6, 0, 6, 0);
Label30.Name = "Label30";
Label30.Size = new Size(184, 30);
Label30.TabIndex = 36;
Label30.Text = "Colore trasparente";
Label30.TextAlign = ContentAlignment.MiddleLeft;
Label30.Visible = false;
//
// PictureBox3
//
PictureBox3.BorderStyle = BorderStyle.FixedSingle;
PictureBox3.Location = new Point(432, 442);
PictureBox3.Margin = new Padding(6, 8, 6, 8);
PictureBox3.Name = "PictureBox3";
PictureBox3.Size = new Size(45, 53);
PictureBox3.TabIndex = 44;
PictureBox3.TabStop = false;
PictureBox3.Visible = false;
//
// versionLabel
//
versionLabel.DataBindings.Add(new Binding("Text", bindingSource1, "AppVersion", true));
@ -1842,40 +1842,35 @@ namespace ImageCatalog
//
dataModelBindingSource1.DataSource = typeof(ImageCatalog_2.DataModel);
//
// groupBox12
// PictureBox3
//
groupBox12.Controls.Add(rdbLibrary2);
groupBox12.Controls.Add(rdbLibrary1);
groupBox12.Location = new Point(405, 625);
groupBox12.Name = "groupBox12";
groupBox12.Size = new Size(350, 175);
groupBox12.TabIndex = 49;
groupBox12.TabStop = false;
groupBox12.Text = "Libreria Manipolazione Grafica";
PictureBox3.BorderStyle = BorderStyle.FixedSingle;
PictureBox3.Location = new Point(432, 442);
PictureBox3.Margin = new Padding(6, 8, 6, 8);
PictureBox3.Name = "PictureBox3";
PictureBox3.Size = new Size(45, 53);
PictureBox3.TabIndex = 44;
PictureBox3.TabStop = false;
PictureBox3.Visible = false;
//
// rdbLibrary1
// btnSetTransparency
//
rdbLibrary1.AutoSize = true;
rdbLibrary1.Location = new Point(12, 37);
rdbLibrary1.Name = "rdbLibrary1";
rdbLibrary1.Size = new Size(188, 34);
rdbLibrary1.TabIndex = 0;
rdbLibrary1.TabStop = true;
rdbLibrary1.Text = "System.Graphics";
rdbLibrary1.DataBindings.Add(new Binding("Checked", bindingSource1, "UseSystemGraphics", true, DataSourceUpdateMode.OnPropertyChanged));
rdbLibrary1.UseVisualStyleBackColor = true;
btnSetTransparency.Location = new Point(288, 455);
btnSetTransparency.Name = "btnSetTransparency";
btnSetTransparency.Size = new Size(131, 40);
btnSetTransparency.TabIndex = 45;
btnSetTransparency.Text = "Imposta";
btnSetTransparency.UseVisualStyleBackColor = true;
//
// rdbLibrary2
// chkUseTransparentColor
//
rdbLibrary2.AutoSize = true;
rdbLibrary2.Location = new Point(12, 77);
rdbLibrary2.Name = "rdbLibrary2";
rdbLibrary2.Size = new Size(149, 34);
rdbLibrary2.TabIndex = 1;
rdbLibrary2.TabStop = true;
rdbLibrary2.Text = "ImageSharp";
rdbLibrary2.DataBindings.Add(new Binding("Checked", bindingSource1, "UseImageSharp", true, DataSourceUpdateMode.OnPropertyChanged));
rdbLibrary2.UseVisualStyleBackColor = true;
chkUseTransparentColor.AutoSize = true;
chkUseTransparentColor.Location = new Point(39, 461);
chkUseTransparentColor.Name = "chkUseTransparentColor";
chkUseTransparentColor.Size = new Size(210, 34);
chkUseTransparentColor.TabIndex = 46;
chkUseTransparentColor.Text = "Colore trasparente";
chkUseTransparentColor.UseVisualStyleBackColor = true;
//
// MainForm
//
@ -1905,6 +1900,8 @@ namespace ImageCatalog
((System.ComponentModel.ISupportInitialize)dataModelBindingSource).EndInit();
TabControl1.ResumeLayout(false);
TabPage5.ResumeLayout(false);
groupBox12.ResumeLayout(false);
groupBox12.PerformLayout();
GroupBox11.ResumeLayout(false);
GroupBox11.PerformLayout();
((System.ComponentModel.ISupportInitialize)numericUpDown2).EndInit();
@ -1939,12 +1936,9 @@ namespace ImageCatalog
TabPage4.ResumeLayout(false);
GroupBox6.ResumeLayout(false);
GroupBox6.PerformLayout();
((System.ComponentModel.ISupportInitialize)PictureBox2).EndInit();
((System.ComponentModel.ISupportInitialize)_PictureBox1).EndInit();
((System.ComponentModel.ISupportInitialize)PictureBox3).EndInit();
((System.ComponentModel.ISupportInitialize)dataModelBindingSource1).EndInit();
groupBox12.ResumeLayout(false);
groupBox12.PerformLayout();
((System.ComponentModel.ISupportInitialize)PictureBox3).EndInit();
ResumeLayout(false);
PerformLayout();
}
@ -2145,7 +2139,6 @@ namespace ImageCatalog
internal CheckBox CheckBox1;
internal TabPage TabPage4;
internal GroupBox GroupBox6;
internal PictureBox PictureBox2;
private PictureBox _PictureBox1;
internal PictureBox PictureBox1
@ -2194,8 +2187,6 @@ namespace ImageCatalog
internal TextBox TextBox10;
internal Label Label29;
internal Label Label30;
internal PictureBox PictureBox3;
private Label _Label27;
internal Label Label27
@ -2298,6 +2289,10 @@ namespace ImageCatalog
private GroupBox groupBox12;
private RadioButton rdbLibrary2;
private RadioButton rdbLibrary1;
internal PictureBox PictureBox3;
private ColorDialog colorDialog1;
private Button btnSetTransparency;
private CheckBox chkUseTransparentColor;
internal Button btnCreaCatalogoAsync
{

View file

@ -31,6 +31,7 @@ public partial class MainForm
private readonly PicSettings _picSettings;
// Prevent re-entrant updates between UI events and model PropertyChanged handling
private bool _suppressRadioUpdates = false;
private bool _transparentDialogOpen = false;
public MainForm(DataModel model, ImageCreationStuff imageCreationStuff, PicSettings picSettings,
ParametriSetup parametriSetup, ILogger<MainForm> logger)
@ -89,9 +90,14 @@ public partial class MainForm
btnOpenSourceFolder.Click += BtnOpenSourceFolder_Click;
btnOpenDestFolder.Click += BtnOpenDestFolder_Click;
// Show currently selected color in small PictureBox3
PictureBox3.BackColor = ColorTranslator.FromHtml(Model.TransparentColor);
// Version label is data-bound to DataModel.AppVersion; DataModel is populated with the version via DI
}
private void RdbLibrary_CheckedChanged(object? sender, EventArgs e)
{
// Keep behavior simple: when a radio button becomes checked, update the ViewModel
@ -142,6 +148,8 @@ public partial class MainForm
_Button5.BindCommand(Model.SaveSettingsCommand);
_Button6.BindCommand(Model.LoadSettingsCommand);
_Button8.BindCommand(Model.SelectColorCommand);
// Bind the transparency chooser button/command
btnSetTransparency.BindCommand(Model.SelectTransparentColorCommand);
// Subscribe to ViewModel events for UI dialogs (these need UI context)
Model.SelectSourceFolderRequested += OnSelectSourceFolderRequested;
@ -150,10 +158,54 @@ public partial class MainForm
Model.SaveSettingsRequested += OnSaveSettingsRequested;
Model.LoadSettingsRequested += OnLoadSettingsRequested;
Model.SelectColorRequested += OnSelectColorRequested;
Model.SelectTransparentColorRequested += OnSelectTransparentColorRequested;
// Show message requests (from ViewModel validation)
Model.ShowMessageRequested += OnShowMessageRequested;
}
private void OnSelectTransparentColorRequested(object? sender, EventArgs e)
{
// Ensure UI thread
if (InvokeRequired)
{
Invoke(new Action<object, EventArgs>(OnSelectTransparentColorRequested), sender, e as EventArgs ?? EventArgs.Empty);
return;
}
// Prevent re-entrancy: if the dialog is already open, ignore subsequent requests
if (_transparentDialogOpen) return;
_transparentDialogOpen = true;
var dlg = new ColorDialog { AllowFullOpen = true };
try
{
dlg.Color = ColorTranslator.FromHtml(Model.TransparentColor);
}
catch { }
try
{
if (dlg.ShowDialog() == DialogResult.OK)
{
Model.TransparentColor = ColorTranslator.ToHtml(dlg.Color);
PictureBox3.BackColor = dlg.Color;
// Update preview if logo exists
if (!string.IsNullOrWhiteSpace(Model.LogoFile) && File.Exists(Model.LogoFile))
{
UpdateLogoPictureBox(Model.LogoFile);
}
}
}
finally
{
_transparentDialogOpen = false;
}
}
private void BtnSetTransparency_Click(object? sender, EventArgs e)
{
Model.SelectTransparentColorCommand.Execute(null);
}
private void OnShowMessageRequested(object? sender, Tuple<string, string, MessageBoxIcon> args)
{
if (args is null) return;
@ -202,6 +254,64 @@ public partial class MainForm
false, DataSourceUpdateMode.OnPropertyChanged));
Label10.DataBindings.Add(new Binding("Text", bindingSource1, nameof(Model.ProcessingStatus),
false, DataSourceUpdateMode.OnPropertyChanged));
// Bind transparency model properties to UI
chkUseTransparentColor.DataBindings.Add(new Binding("Checked", bindingSource1, nameof(Model.UseTransparentColor), false, DataSourceUpdateMode.OnPropertyChanged));
// Show currently selected color in PictureBox3
PictureBox3.Visible = false;
if (!string.IsNullOrWhiteSpace(Model.TransparentColor))
{
try
{
PictureBox3.BackColor = ColorTranslator.FromHtml(Model.TransparentColor);
PictureBox3.Visible = true;
}
catch
{
PictureBox3.Visible = false;
}
}
// When logo file changes, update preview
Model.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(Model.LogoFile) || e.PropertyName == nameof(Model.UseTransparentColor) || e.PropertyName == nameof(Model.TransparentColor))
{
if (!string.IsNullOrWhiteSpace(Model.LogoFile) && System.IO.File.Exists(Model.LogoFile))
{
UpdateLogoPictureBox(Model.LogoFile);
}
}
};
// Bind transparent color hex and show color in PictureBox3
// Bind UseTransparentColor checkbox (designer control named CheckBox5 used for AddLogo earlier, add new binding control exists in designer)
// Use PictureBox3 to display color value
var colorBinding = new Binding("BackColor", bindingSource1, nameof(Model.TransparentColor), true, DataSourceUpdateMode.OnPropertyChanged);
colorBinding.Format += (s, e) =>
{
try
{
e.Value = ColorTranslator.FromHtml(e.Value?.ToString() ?? "#FFFFFF");
}
catch
{
e.Value = Color.White;
}
};
PictureBox3.DataBindings.Add(colorBinding);
// Bind checkbox for using color key transparency if such control exists (CheckBox5 was repurposed as AddLogo); create binding if available
try
{
// The designer has CheckBox5 for 'AddLogo'. We'll add a separate binding to a new control named CheckBoxUseTransparentColor if present.
var chk = this.Controls.Find("chkUseTransparentColor", true).FirstOrDefault() as CheckBox;
if (chk != null)
{
chk.DataBindings.Add(new Binding("Checked", bindingSource1, nameof(Model.UseTransparentColor), false, DataSourceUpdateMode.OnPropertyChanged));
}
}
catch { }
}
@ -212,6 +322,18 @@ public partial class MainForm
SetDefaults();
_logger.LogInformation("Programma Avviato");
// If settings were loaded before the form was shown, ensure the logo preview is updated
try
{
if (!string.IsNullOrWhiteSpace(Model.LogoFile) && File.Exists(Model.LogoFile))
{
UpdateLogoPictureBox(Model.LogoFile);
}
}
catch (Exception ex)
{
_logger.LogDebug(ex, "Failed to load logo during form load");
}
}
private string CalcTime(DateTime timeStart, DateTime timeStop, int numFoto)
@ -436,14 +558,50 @@ public partial class MainForm
try
{
await Model.LoadSettingsFromFileAsync(openDialog.FileName);
// Explicitly ensure UI is enabled after loading
Model.UiEnabled = true;
// Update logo preview if logo file exists
if (File.Exists(Model.LogoFile))
// If a logo path was stored in the settings, try to resolve and display it.
// The stored path may be absolute or relative to the settings file location.
try
{
UpdateLogoPictureBox(Model.LogoFile);
var storedLogo = Model.LogoFile;
// Trim whitespace and surrounding quotes which may be present in saved settings
if (!string.IsNullOrWhiteSpace(storedLogo))
{
storedLogo = storedLogo.Trim();
storedLogo = storedLogo.Trim('"');
}
if (!string.IsNullOrWhiteSpace(storedLogo))
{
string resolved = storedLogo;
// If not rooted, try to resolve relative to the settings file folder
var settingsFolder = Path.GetDirectoryName(openDialog.FileName) ?? string.Empty;
if (!Path.IsPathRooted(resolved) && !string.IsNullOrWhiteSpace(settingsFolder))
{
var candidate = Path.Combine(settingsFolder, resolved);
if (File.Exists(candidate)) resolved = candidate;
}
// If rooted but file doesn't exist, try filename near settings file
if (!File.Exists(resolved) && !string.IsNullOrWhiteSpace(settingsFolder))
{
var candidate2 = Path.Combine(settingsFolder, Path.GetFileName(resolved));
if (File.Exists(candidate2)) resolved = candidate2;
}
if (File.Exists(resolved))
{
// Update the model so data-bound controls reflect the resolved absolute path
Model.LogoFile = resolved;
UpdateLogoPictureBox(resolved);
}
}
}
catch (Exception ex)
{
_logger.LogDebug(ex, "Error resolving logo path after loading settings");
}
Text = "Image Catalog - " + Path.GetFileName(openDialog.FileName);
@ -519,17 +677,68 @@ public partial class MainForm
{
try
{
PictureBox1.Image = Image.FromFile(logoPath);
if (PictureBox1.Image.Height >= PictureBox1.Image.Width)
// Load image via System.Drawing for preview so we can use MakeTransparent when requested
using var img = System.Drawing.Image.FromFile(logoPath);
System.Drawing.Bitmap previewBmp;
// If using color-key transparency and a color is selected, apply MakeTransparent for preview
if (Model.UseTransparentColor && !string.IsNullOrWhiteSpace(Model.TransparentColor))
{
PictureBox1.Height = 160;
PictureBox1.Width = (int)(160 * PictureBox1.Image.Width / (double)PictureBox1.Image.Height);
try
{
var key = ColorTranslator.FromHtml(Model.TransparentColor);
previewBmp = new System.Drawing.Bitmap(img.Width, img.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var g = System.Drawing.Graphics.FromImage(previewBmp))
{
g.Clear(System.Drawing.Color.Transparent);
g.DrawImage(img, 0, 0, img.Width, img.Height);
}
// Apply exact color-key transparency
previewBmp.MakeTransparent(key);
PictureBox3.BackColor = key;
PictureBox3.Visible = true;
}
catch
{
previewBmp = new System.Drawing.Bitmap(img);
}
}
else
{
PictureBox1.Width = 160;
PictureBox1.Height = (int)(160 * PictureBox1.Image.Height / (double)PictureBox1.Image.Width);
previewBmp = new System.Drawing.Bitmap(img);
}
// Resize preview to fit into PictureBox1 while preserving aspect ratio
// Resize preview to fit into PictureBox1 while preserving aspect ratio
var boxW = PictureBox1.ClientSize.Width > 0 ? PictureBox1.ClientSize.Width : 449;
var boxH = PictureBox1.ClientSize.Height > 0 ? PictureBox1.ClientSize.Height : 369;
var ratio = Math.Min((double)boxW / previewBmp.Width, (double)boxH / previewBmp.Height);
var destW = Math.Max(1, (int)(previewBmp.Width * ratio));
var destH = Math.Max(1, (int)(previewBmp.Height * ratio));
var scaled = new System.Drawing.Bitmap(destW, destH, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var g = System.Drawing.Graphics.FromImage(scaled))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.Clear(System.Drawing.Color.Transparent);
// Center the image in the PictureBox
var offsetX = Math.Max(0, (boxW - destW) / 2);
var offsetY = Math.Max(0, (boxH - destH) / 2);
using var canvas = new System.Drawing.Bitmap(boxW, boxH, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var cg = System.Drawing.Graphics.FromImage(canvas))
{
cg.Clear(System.Drawing.Color.Transparent);
cg.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
cg.DrawImage(previewBmp, offsetX, offsetY, destW, destH);
}
g.DrawImage(canvas, 0, 0);
}
// Set PictureBox1 image (dispose previous)
var old = PictureBox1.Image;
PictureBox1.SizeMode = PictureBoxSizeMode.CenterImage;
PictureBox1.Image = scaled;
old?.Dispose();
previewBmp.Dispose();
}
catch
{
@ -537,6 +746,39 @@ public partial class MainForm
}
}
private void UpdateLogoPreviewWithColorKey(string logoPath, Color keyColor)
{
try
{
using var img = (Bitmap)Image.FromFile(logoPath);
// Create ARGB copy
var bmp = new Bitmap(img.Width, img.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var g = Graphics.FromImage(bmp))
{
g.DrawImage(img, 0, 0, img.Width, img.Height);
}
bmp.MakeTransparent(keyColor);
// Resize to PictureBox1 size similar to previous logic
Bitmap finalBmp;
if (bmp.Height >= bmp.Width)
{
finalBmp = new Bitmap(bmp, new Size((int)(160 * bmp.Width / (double)bmp.Height), 160));
}
else
{
finalBmp = new Bitmap(bmp, new Size(160, (int)(160 * bmp.Height / (double)bmp.Width)));
}
PictureBox1.Image = finalBmp;
}
catch
{
// ignore preview failures
}
}
private void setLabel18Text(string text)
{
if (Label18.InvokeRequired)

View file

@ -129,4 +129,7 @@
<metadata name="dataModelBindingSource1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>349, 17</value>
</metadata>
<metadata name="colorDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>802, 17</value>
</metadata>
</root>

View file

@ -168,6 +168,20 @@ namespace ImageCatalog_2.Models
[XmlElement("MarchioAggiungi")]
public bool AddLogo { get; set; }
// Color-key transparency settings
[JsonPropertyName("TransparentColor")]
[XmlElement("ColoreTrasparente")]
public string TransparentColor { get; set; } = "#FFFFFF";
[JsonPropertyName("UseTransparentColor")]
[XmlElement("UsaColoreTrasparente")]
public bool UseTransparentColor { get; set; } = false;
// Selected image processing library (e.g., "System.Graphics" or "ImageSharp")
[JsonPropertyName("ImageLibrary")]
[XmlElement("ImageLibrary")]
public string ImageLibrary { get; set; } = "System.Graphics";
// Options
[JsonPropertyName("ForceJpeg")]
[XmlElement("GeneraleForzaJpg")]

View file

@ -85,15 +85,15 @@ namespace ImageCatalog_2.Services
}
else if (prop.PropertyType == typeof(int))
{
value = _parametriSetup.LeggiParametro<int>(xmlName, (int)(prop.GetValue(loadedDto) ?? 0));
value = fileParams.LeggiParametro<int>(xmlName, (int)(prop.GetValue(loadedDto) ?? 0));
}
else if (prop.PropertyType == typeof(double))
{
value = _parametriSetup.LeggiParametro<double>(xmlName, (double)(prop.GetValue(loadedDto) ?? 0.0));
value = fileParams.LeggiParametro<double>(xmlName, (double)(prop.GetValue(loadedDto) ?? 0.0));
}
else if (prop.PropertyType == typeof(DateTime))
{
var strValue = _parametriSetup.LeggiParametroString(xmlName);
var strValue = fileParams.LeggiParametroString(xmlName);
if (DateTime.TryParse(strValue, out var dateValue))
{
value = dateValue;