Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Setup/Assets/Themes/Kobe-Light/Compare.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions Setup/Assets/Themes/Kobe-Light/CompareSlider.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions Setup/Assets/Themes/Kobe-Light/igtheme.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"IsShowTitlebarLogo": true,
"NavButtonLeft": "ViewPreviousImage.svg",
"NavButtonRight": "ViewNextImage.svg",
"CompareSliderHandle": "CompareSlider.svg",
"PreviewImage": "preview.webp",
"AppLogo": "Logo.svg"
},
Expand Down Expand Up @@ -47,6 +48,7 @@
"AutoZoom": "AutoZoom.svg",
"Checkerboard": "Checkerboard.svg",
"ColorPicker": "ColorPicker.svg",
"Compare": "Compare.svg",
"Crop": "Crop.svg",
"Delete": "Delete.svg",
"Edit": "Edit.svg",
Expand Down
1 change: 1 addition & 0 deletions Setup/Assets/Themes/Kobe/Compare.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions Setup/Assets/Themes/Kobe/CompareSlider.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions Setup/Assets/Themes/Kobe/igtheme.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"IsShowTitlebarLogo": true,
"NavButtonLeft": "ViewPreviousImage.svg",
"NavButtonRight": "ViewNextImage.svg",
"CompareSliderHandle": "CompareSlider.svg",
"PreviewImage": "preview.webp",
"AppLogo": "Logo.svg"
},
Expand All @@ -37,6 +38,7 @@
"AutoZoom": "AutoZoom.svg",
"Checkerboard": "Checkerboard.svg",
"ColorPicker": "ColorPicker.svg",
"Compare": "Compare.svg",
"Crop": "Crop.svg",
"Delete": "Delete.svg",
"Edit": "Edit.svg",
Expand Down
9 changes: 9 additions & 0 deletions Source/Components/ImageGlass.Base/Language/IgLang.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ public Dictionary<string, string> InitDefaultLanguage()

{ $"_._InvalidAction", "Invalid action" }, //v9.0
{ $"_._InvalidAction._Transformation", "ImageGlass does not support rotation, flipping for this image." }, //v9.0
{ $"_._InvalidAction._ComparisonMode", "This feature is not available in comparison mode." }, //v9.1


{ "_._UserAction._MenuNotFound", "Cannot find menu '{0}' to invoke the action" }, // v9.0
Expand Down Expand Up @@ -510,6 +511,7 @@ public Dictionary<string, string> InitDefaultLanguage()
{ "FrmMain.MnuTools", "Tools" }, //v3.0
{ "FrmMain.MnuColorPicker", "Color picker" }, //v5.0
{ "FrmMain.MnuCropTool", "Crop image" }, // v7.6
{ "FrmMain.MnuCompareTool", "Compare images" }, // v9.1
{ "FrmMain.MnuResizeTool", "Resize image" }, // v9.2
{ "FrmMain.MnuFrameNav", "Frame navigation" }, // v7.5
{ "FrmMain.MnuGetMoreTools", "Get more tools…" }, // v9.0
Expand Down Expand Up @@ -890,6 +892,13 @@ public Dictionary<string, string> InitDefaultLanguage()
#endregion // FrmCrop


#region FrmCompare
{ "FrmCompare._DropToReplaceMainImage", "Drop to replace main image" }, // v9.1
{ "FrmCompare._DropToSetComparisonImage", "Drop to set comparison image" }, // v9.1
{ "FrmCompare._SelectImageToCompare", "Select an image to compare using the button below." }, // v9.1
#endregion // FrmCompare


#region FrmColorPicker

{ "FrmColorPicker.BtnSettings._Tooltip", "Open Color picker settings…" }, //v9.0
Expand Down
12 changes: 6 additions & 6 deletions Source/Components/ImageGlass.Base/Photoing/Codecs/PhotoCodec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ private static (bool loadSuccessful, IgImgData result, string ext, MagickReadSet
base64Content = fs.ReadToEnd();
}

if (result.CanAnimate)
if (result.CanAnimate && options?.FirstFrameOnly != true)
{
result.Source = BHelper.ToGdiPlusBitmapFromBase64(base64Content);
}
Expand All @@ -887,16 +887,16 @@ private static (bool loadSuccessful, IgImgData result, string ext, MagickReadSet
try
{
// Note: Using WIC is much faster than using MagickImageCollection
if (result.CanAnimate)
if (result.CanAnimate && options?.FirstFrameOnly != true)
{
result.Source = BHelper.ToGdiPlusBitmap(filePath);
}
// multiple frame
else if (result.FrameCount > 0)
// multiple frame (but not animating or FirstFrameOnly requested)
else if (result.FrameCount > 1 && options?.FirstFrameOnly != true)
{
result.Source = WicBitmapDecoder.Load(filePath);
}
// single frame
// single frame or FirstFrameOnly requested
else
{
result.Image = WicBitmapSource.Load(filePath);
Expand All @@ -913,7 +913,7 @@ private static (bool loadSuccessful, IgImgData result, string ext, MagickReadSet
{
using var webp = new WebPWrapper();

if (result.CanAnimate)
if (result.CanAnimate && options?.FirstFrameOnly != true)
{
var aniWebP = webp.AnimLoad(filePath);
var frames = aniWebP.Select(frame =>
Expand Down
5 changes: 5 additions & 0 deletions Source/Components/ImageGlass.Base/Types/Const.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,9 @@ public static class Const
/// </summary>
public const string IMAGE_FORMATS = ".3fr;.apng;.ari;.arw;.avif;.b64;.bay;.bmp;.cap;.cr2;.cr3;.crw;.cur;.cut;.dcr;.dcs;.dds;.dib;.dng;.drf;.eip;.emf;.erf;.exif;.exr;.fff;.fits;.flif;.gif;.gifv;.gpr;.hdp;.hdr;.heic;.heif;.hif;.ico;.iiq;.jfif;.jp2;.jpe;.jpeg;.jpg;.jxl;.jxr;.k25;.kdc;.mdc;.mef;.mjpeg;.mos;.mrw;.nef;.nrw;.obm;.orf;.pbm;.pcx;.pef;.pgm;.png;.ppm;.psb;.psd;.ptx;.pxn;.qoi;.r3d;.raf;.raw;.rw2;.rwl;.rwz;.sr2;.srf;.srw;.svg;.tga;.tif;.tiff;.viff;.wdp;.webp;.wmf;.wpg;.x3f;.xbm;.xpm;.xv";

/// <summary>
/// File extensions that may contain animation and should use WebView2 for rendering.
/// </summary>
public static readonly string[] ANIMATED_FORMATS = [".gif", ".gifv", ".webp", ".apng"];

}
19 changes: 19 additions & 0 deletions Source/Components/ImageGlass.Gallery/ImageGallery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,25 @@ public void ResetErrorImage()
mErrorImageChanged = false;
}


/// <summary>
/// Gets or sets whether comparison mode is active (shows A/B indicators).
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool ComparisonMode { get; set; } = false;

/// <summary>
/// Gets or sets the index of the main comparison image (A).
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int ComparisonMainIndex { get; set; } = -1;

/// <summary>
/// Gets or sets the file path of the comparison image (B).
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string ComparisonImagePath { get; set; } = string.Empty;

#endregion


Expand Down
93 changes: 93 additions & 0 deletions Source/Components/ImageGlass.Gallery/Renderer/StyleRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,99 @@ public virtual void DrawItem(Graphics g, ImageGalleryItem item, ItemState state,
{
ControlPaint.DrawFocusRectangle(g, bounds);
}

// Draw comparison A/B badges
if (ImageGalleryOwner.ComparisonMode)
{
DrawComparisonBadge(g, item, bounds);
}
}

/// <summary>
/// Draws the A/B comparison badge for items in comparison mode.
/// </summary>
protected virtual void DrawComparisonBadge(Graphics g, ImageGalleryItem item, Rectangle bounds)
{
var isMainImage = item.Index == ImageGalleryOwner.ComparisonMainIndex;
var isCompareImage = !string.IsNullOrEmpty(ImageGalleryOwner.ComparisonImagePath) &&
string.Equals(item.FilePath, ImageGalleryOwner.ComparisonImagePath, StringComparison.OrdinalIgnoreCase);

if (!isMainImage && !isCompareImage) return;

var colorA = Color.FromArgb(220, 59, 130, 246); // Blue
var colorB = Color.FromArgb(220, 34, 197, 94); // Green

var badgePadding = 4;
var badgeHeight = 20;

g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

using var font = new Font("Segoe UI", 9f, FontStyle.Bold);
using var format = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};

if (isMainImage && isCompareImage)
{
// Split pill: A on left (blue), B on right (green)
var pillWidth = 36;
var pillRect = new Rectangle(
bounds.Right - pillWidth - badgePadding,
bounds.Top + badgePadding,
pillWidth,
badgeHeight);

var halfWidth = pillWidth / 2;
var radius = badgeHeight / 2;

// Draw left half (A - blue) with rounded left side
using var pathLeft = new System.Drawing.Drawing2D.GraphicsPath();
pathLeft.AddArc(pillRect.Left, pillRect.Top, badgeHeight, badgeHeight, 90, 180);
pathLeft.AddLine(pillRect.Left + radius, pillRect.Top, pillRect.Left + halfWidth, pillRect.Top);
pathLeft.AddLine(pillRect.Left + halfWidth, pillRect.Top, pillRect.Left + halfWidth, pillRect.Bottom);
pathLeft.AddLine(pillRect.Left + halfWidth, pillRect.Bottom, pillRect.Left + radius, pillRect.Bottom);
pathLeft.CloseFigure();

using var brushA = new SolidBrush(colorA);
g.FillPath(brushA, pathLeft);

// Draw right half (B - green) with rounded right side
using var pathRight = new System.Drawing.Drawing2D.GraphicsPath();
pathRight.AddLine(pillRect.Left + halfWidth, pillRect.Top, pillRect.Right - radius, pillRect.Top);
pathRight.AddArc(pillRect.Right - badgeHeight, pillRect.Top, badgeHeight, badgeHeight, -90, 180);
pathRight.AddLine(pillRect.Right - radius, pillRect.Bottom, pillRect.Left + halfWidth, pillRect.Bottom);
pathRight.CloseFigure();

using var brushB = new SolidBrush(colorB);
g.FillPath(brushB, pathRight);

// Draw letters
var leftRect = new RectangleF(pillRect.Left, pillRect.Top, halfWidth, badgeHeight);
var rightRect = new RectangleF(pillRect.Left + halfWidth, pillRect.Top, halfWidth, badgeHeight);
g.DrawString("A", font, Brushes.White, leftRect, format);
g.DrawString("B", font, Brushes.White, rightRect, format);
}
else
{
// Single circular badge
var badgeSize = 20;
var badgeRect = new Rectangle(
bounds.Right - badgeSize - badgePadding,
bounds.Top + badgePadding,
badgeSize,
badgeSize);

var badgeText = isMainImage ? "A" : "B";
var badgeColor = isMainImage ? colorA : colorB;

using var brush = new SolidBrush(badgeColor);
g.FillEllipse(brush, badgeRect);
g.DrawString(badgeText, font, Brushes.White, badgeRect, format);
}

g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
}

/// <summary>
Expand Down
27 changes: 27 additions & 0 deletions Source/Components/ImageGlass.Settings/WebUI/DXCanvas_Webview2.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,33 @@
</div>
</div>

<div id="layerComparison" hidden tabindex="0">
<div class="comparison-container">
<div class="comparison-image comparison-image-left">
<img id="compareImageLeft" alt="Image A">
</div>
<div class="comparison-image comparison-image-right">
<img id="compareImageRight" alt="Image B">
</div>
<div class="comparison-slider">
<div class="comparison-slider-line"></div>
<div class="comparison-slider-handle">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M8 5l-5 7 5 7M16 5l5 7-5 7"/>
</svg>
</div>
</div>
<div class="comparison-drop-overlay comparison-drop-overlay-left">
<div class="comparison-drop-border"></div>
<span class="comparison-drop-text">Drop to replace main image</span>
</div>
<div class="comparison-drop-overlay comparison-drop-overlay-right">
<div class="comparison-drop-border"></div>
<span class="comparison-drop-text">Drop to set comparison image</span>
</div>
</div>
</div>

<script src="./dist/DXCanvas_Webview2.js"></script>
</body>
</html>
Loading