Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Conceição <Tiago_caza@hotmail.com>2021-05-17 05:34:31 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2021-05-17 05:34:31 +0300
commit51c82318cf27b8e0d3425aa2ea1b093fc5b7d23f (patch)
tree1059ece01344eec934792d72bf8b0ed9aad4a06d /UVtools.WPF
parentedd9984a31a90791edf3bcf594855d08507428b5 (diff)
v2.12.0v2.12.0
- **Layer arithmetic:** - (Add) Allow to use ':' to define a layer range to set, eg, 0:20 to select from 0 to 20 layers - (Improvement) Modifications with set ROI and/or Mask(s) are only applied to target layer on that same regions - (Improvement) Disallow set one layer to the same layer without any modification - (Improvement) Clear and sanitize non-existing layers indexes - (Improvement) Disable the layer range selector from dialog - (Fix) Prevent error when using non-existing layers indexes - (Fix) Allow use only a mask for operations - (Fix) Implement the progress bar - **File formats:** - (Add) VDA.ZIP (Voxeldance Additive) - (Improvement) Add a check to global `LightPWM` if 0 it will force to 255 - (Improvement) Add a check to layer `LightPWM` if 0 it will force to 255 - (Add) Allow to save the selected region (ROI) to a image file - (Update) .NET 5.0.5 to 5.0.6 - (Fix) Getting the transposed rectangle in fliped images are offseting the position by -1 - (Fix) Tools: Hide ROI Region text when empty/not selected
Diffstat (limited to 'UVtools.WPF')
-rw-r--r--UVtools.WPF/Controls/AdvancedImageBox.axaml17
-rw-r--r--UVtools.WPF/Controls/AdvancedImageBox.axaml.cs768
-rw-r--r--UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs2
-rw-r--r--UVtools.WPF/Extensions/BitmapExtension.cs4
-rw-r--r--UVtools.WPF/Extensions/DrawingExtensions.cs8
-rw-r--r--UVtools.WPF/LayerCache.cs10
-rw-r--r--UVtools.WPF/MainWindow.LayerPreview.cs101
-rw-r--r--UVtools.WPF/MainWindow.axaml31
-rw-r--r--UVtools.WPF/MainWindow.axaml.cs2
-rw-r--r--UVtools.WPF/Structures/AppVersionChecker.cs1
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj2
-rw-r--r--UVtools.WPF/UserSettings.cs68
-rw-r--r--UVtools.WPF/Windows/ToolWindow.axaml1
13 files changed, 659 insertions, 356 deletions
diff --git a/UVtools.WPF/Controls/AdvancedImageBox.axaml b/UVtools.WPF/Controls/AdvancedImageBox.axaml
index 2c288f6..6866ddf 100644
--- a/UVtools.WPF/Controls/AdvancedImageBox.axaml
+++ b/UVtools.WPF/Controls/AdvancedImageBox.axaml
@@ -8,10 +8,9 @@
RowDefinitions="*,Auto"
ColumnDefinitions="*,Auto">
- <ContentControl
- Name="ViewPort"
- Background="Transparent"
- />
+ <ContentPresenter Grid.Row="0" Grid.Column="0"
+ Name="ViewPort"
+ Background="Transparent"/>
<ScrollBar
Name="VerticalScrollBar"
@@ -20,8 +19,7 @@
ViewportSize="{Binding #ViewPort.Bounds.Height}"
Minimum="0"
Maximum="0"
- Visibility="Auto"
- />
+ Visibility="Auto"/>
<ScrollBar
Name="HorizontalScrollBar"
@@ -30,13 +28,12 @@
ViewportSize="{Binding #ViewPort.Bounds.Width}"
Minimum="0"
Maximum="0"
- Visibility="Auto"
- />
+ Visibility="Auto"/>
<Border
Grid.Row="1"
Grid.Column="1"
- Background="WhiteSmoke"
- />
+ Background="WhiteSmoke"/>
+
</Grid>
</UserControl>
diff --git a/UVtools.WPF/Controls/AdvancedImageBox.axaml.cs b/UVtools.WPF/Controls/AdvancedImageBox.axaml.cs
index 02e8f87..94910fc 100644
--- a/UVtools.WPF/Controls/AdvancedImageBox.axaml.cs
+++ b/UVtools.WPF/Controls/AdvancedImageBox.axaml.cs
@@ -2,15 +2,17 @@
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
-using System.Diagnostics;
using System.Drawing;
using System.Runtime.CompilerServices;
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
using UVtools.Core.Extensions;
using UVtools.WPF.Extensions;
using Bitmap = Avalonia.Media.Imaging.Bitmap;
@@ -26,7 +28,7 @@ namespace UVtools.WPF.Controls
{
public ScrollBar HorizontalScrollBar { get; }
public ScrollBar VerticalScrollBar { get; }
- public ContentControl ViewPortControl { get; }
+ public ContentPresenter ViewPort { get; }
public Vector Offset
{
@@ -40,19 +42,18 @@ namespace UVtools.WPF.Controls
}
}
- public Size Viewport => ViewPortControl.Bounds.Size;
+ public Size ViewPortSize => ViewPort.Bounds.Size;
#region Bindable Base
/// <summary>
/// Multicast event for property change notifications.
/// </summary>
private PropertyChangedEventHandler _propertyChanged;
- private readonly List<string> events = new ();
public new event PropertyChangedEventHandler PropertyChanged
{
- add { _propertyChanged += value; events.Add("added"); }
- remove { _propertyChanged -= value; events.Add("removed"); }
+ add => _propertyChanged += value;
+ remove => _propertyChanged -= value;
}
protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
@@ -123,16 +124,10 @@ namespace UVtools.WPF.Controls
/// <summary>
/// Returns the default zoom levels
/// </summary>
- public static ZoomLevelCollection Default
- {
- get
- {
- return new ZoomLevelCollection(new[]
- {
- 7, 10, 15, 20, 25, 30, 50, 70, 100, 150, 200, 300, 400, 500, 600, 700, 800, 1200, 1600, 3200
- });
- }
- }
+ public static ZoomLevelCollection Default =>
+ new(new[] {
+ 7, 10, 15, 20, 25, 30, 50, 70, 100, 150, 200, 300, 400, 500, 600, 700, 800, 1200, 1600, 3200
+ });
#endregion
@@ -233,9 +228,9 @@ namespace UVtools.WPF.Controls
/// <param name="arrayIndex">A 64-bit integer that represents the index in the <see cref="Array"/> at which storing begins.</param>
public void CopyTo(int[] array, int arrayIndex)
{
- for (int i = 0; i < this.Count; i++)
+ for (int i = 0; i < Count; i++)
{
- array[arrayIndex + i] = this.List.Values[i];
+ array[arrayIndex + i] = List.Values[i];
}
}
@@ -245,7 +240,7 @@ namespace UVtools.WPF.Controls
/// <param name="zoomLevel">The zoom level.</param>
public int FindNearest(int zoomLevel)
{
- int nearestValue = this.List.Values[0];
+ int nearestValue = List.Values[0];
int nearestDifference = Math.Abs(nearestValue - zoomLevel);
for (int i = 1; i < Count; i++)
{
@@ -297,8 +292,8 @@ namespace UVtools.WPF.Controls
/// <returns>The next matching increased zoom level for the given current zoom if applicable, otherwise the nearest zoom.</returns>
public int NextZoom(int zoomLevel)
{
- var index = IndexOf(this.FindNearest(zoomLevel));
- if (index < this.Count - 1)
+ var index = IndexOf(FindNearest(zoomLevel));
+ if (index < Count - 1)
{
index++;
}
@@ -381,7 +376,7 @@ namespace UVtools.WPF.Controls
public enum SizeModes : byte
{
/// <summary>
- /// The image is disiplayed according to current zoom and scroll properties.
+ /// The image is displayed according to current zoom and scroll properties.
/// </summary>
Normal,
@@ -393,7 +388,7 @@ namespace UVtools.WPF.Controls
/// <summary>
/// The image is stretched to fill as much of the client area of the control as possible, whilst retaining the same aspect ratio for the width and height.
/// </summary>
- //Fit
+ Fit
}
[Flags]
@@ -452,68 +447,95 @@ namespace UVtools.WPF.Controls
#endregion
- #region Constants
- public static readonly int MinZoom = 10;
- public static readonly int MaxZoom = 3500;
- #endregion
+ public static readonly DirectProperty<AdvancedImageBox, bool> CanRenderProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(
+ nameof(CanRender),
+ o => o.CanRender);
+ /// <summary>
+ /// Gets or sets if control can render the image
+ /// </summary>
public bool CanRender
{
get => _canRender;
set
{
- if (!RaiseAndSetIfChanged(ref _canRender, value)) return;
+ if (!SetAndRaise(CanRenderProperty, ref _canRender, value)) return;
if (_canRender) TriggerRender();
}
}
+ public static readonly StyledProperty<byte> GridCellSizeProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, byte>(nameof(GridCellSize), 15);
+
/// <summary>
- /// Gets or sets the basic cell size
+ /// Gets or sets the grid cell size
/// </summary>
public byte GridCellSize
{
- get => _gridCellSize;
- set => RaiseAndSetIfChanged(ref _gridCellSize, value);
+ get => GetValue(GridCellSizeProperty);
+ set => SetValue(GridCellSizeProperty, value);
}
+ public static readonly StyledProperty<ISolidColorBrush> GridColorProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(GridColor), Brushes.Gainsboro);
+
/// <summary>
/// Gets or sets the color used to create the checkerboard style background
/// </summary>
public ISolidColorBrush GridColor
{
- get => _gridColor;
- set => RaiseAndSetIfChanged(ref _gridColor, value);
+ get => GetValue(GridColorProperty);
+ set => SetValue(GridColorProperty, value);
}
+ public static readonly StyledProperty<ISolidColorBrush> GridColorAlternateProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(GridColorAlternate), Brushes.White);
+
/// <summary>
/// Gets or sets the color used to create the checkerboard style background
/// </summary>
public ISolidColorBrush GridColorAlternate
{
- get => _gridColorAlternate;
- set => RaiseAndSetIfChanged(ref _gridColorAlternate, value);
+ get => GetValue(GridColorAlternateProperty);
+ set => SetValue(GridColorAlternateProperty, value);
}
+ public static readonly StyledProperty<Bitmap> ImageProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, Bitmap>(nameof(Image));
+
/// <summary>
/// Gets or sets the image to be displayed
/// </summary>
public Bitmap Image
{
- get => _image;
+ get => GetValue(ImageProperty);
set
{
- if (!RaiseAndSetIfChanged(ref _image, value)) return;
+ SetValue(ImageProperty, value);
- if (Image is null)
+ if (value is null)
{
SelectNone();
}
UpdateViewPort();
TriggerRender();
+
+ RaisePropertyChanged(nameof(IsImageLoaded));
}
}
+ public WriteableBitmap ImageAsWriteableBitmap => (WriteableBitmap) Image;
+
+ public bool IsImageLoaded => Image is not null;
+
+ public static readonly DirectProperty<AdvancedImageBox, Bitmap> TrackerImageProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, Bitmap>(
+ nameof(TrackerImage),
+ o => o.TrackerImage,
+ (o, v) => o.TrackerImage = v);
+
/// <summary>
/// Gets or sets an image to follow the mouse pointer
/// </summary>
@@ -522,29 +544,33 @@ namespace UVtools.WPF.Controls
get => _trackerImage;
set
{
- if (!RaiseAndSetIfChanged(ref _trackerImage, value)) return;
- RaisePropertyChanged(nameof(HaveTrackerImage));
+ if (!SetAndRaise(TrackerImageProperty, ref _trackerImage, value)) return;
TriggerRender();
+ RaisePropertyChanged(nameof(HaveTrackerImage));
}
}
- public bool HaveTrackerImage => !(_trackerImage is null);
+ public bool HaveTrackerImage => _trackerImage is not null;
+
+ public static readonly StyledProperty<bool> TrackerImageAutoZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(TrackerImageAutoZoom), true);
/// <summary>
/// Gets or sets if the tracker image will be scaled to the current zoom
/// </summary>
public bool TrackerImageAutoZoom
{
- get => _trackerImageAutoZoom;
- set => RaiseAndSetIfChanged(ref _trackerImageAutoZoom, value);
+ get => GetValue(TrackerImageAutoZoomProperty);
+ set => SetValue(TrackerImageAutoZoomProperty, value);
}
-
+
public bool IsHorizontalBarVisible
{
get
{
if (Image is null) return false;
- return ScaledImageWidth > Viewport.Width;
+ if (SizeMode != SizeModes.Normal) return false;
+ return ScaledImageWidth > ViewPortSize.Width;
}
}
@@ -553,34 +579,51 @@ namespace UVtools.WPF.Controls
get
{
if (Image is null) return false;
- return ScaledImageHeight > Viewport.Height;
+ if (SizeMode != SizeModes.Normal) return false;
+ return ScaledImageHeight > ViewPortSize.Height;
}
}
- public static readonly DirectProperty<AdvancedImageBox, bool> ShowGridProperty =
- AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(nameof(ShowGrid),
- c => c.ShowGrid, (c, v) => c.ShowGrid = v);
+ public static readonly StyledProperty<bool> ShowGridProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(ShowGrid), true);
+
/// <summary>
- /// Gets or sets if the checkerboard background should be displayed
+ /// Gets or sets the grid visibility when reach high zoom levels
/// </summary>
public bool ShowGrid
{
- get => _showGrid;
- set => SetAndRaise(ShowGridProperty, ref _showGrid, value);
+ get => GetValue(ShowGridProperty);
+ set => SetValue(ShowGridProperty, value);
}
+ public static readonly DirectProperty<AdvancedImageBox, Point> PointerPositionProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, Point>(
+ nameof(PointerPosition),
+ o => o.PointerPosition);
+
+ /// <summary>
+ /// Gets the current pointer position
+ /// </summary>
public Point PointerPosition
{
get => _pointerPosition;
- private set => RaiseAndSetIfChanged(ref _pointerPosition, value);
+ private set => SetAndRaise(PointerPositionProperty, ref _pointerPosition, value);
}
+ public static readonly DirectProperty<AdvancedImageBox, bool> IsPanningProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(
+ nameof(IsPanning),
+ o => o.IsPanning);
+
+ /// <summary>
+ /// Gets if control is currently panning
+ /// </summary>
public bool IsPanning
{
get => _isPanning;
protected set
{
- if (!RaiseAndSetIfChanged(ref _isPanning, value)) return;
+ if (!SetAndRaise(IsPanningProperty, ref _isPanning, value)) return;
_startScrollPosition = Offset;
if (value)
@@ -596,94 +639,242 @@ namespace UVtools.WPF.Controls
}
}
+ public static readonly DirectProperty<AdvancedImageBox, bool> IsSelectingProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, bool>(
+ nameof(IsSelecting),
+ o => o.IsSelecting);
+
+ /// <summary>
+ /// Gets if control is currently selecting a ROI
+ /// </summary>
public bool IsSelecting
{
get => _isSelecting;
- protected set => RaiseAndSetIfChanged(ref _isSelecting, value);
+ protected set => SetAndRaise(IsSelectingProperty, ref _isSelecting, value);
}
+ /// <summary>
+ /// Gets the center point of the viewport
+ /// </summary>
public Point CenterPoint
{
get
{
var viewport = GetImageViewPort();
- return new Point(viewport.Width / 2, viewport.Height / 2);
+ return new(viewport.Width / 2, viewport.Height / 2);
}
}
+ public static readonly StyledProperty<bool> AutoPanProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(AutoPan), true);
+
+ /// <summary>
+ /// Gets or sets if the control can pan with the mouse
+ /// </summary>
public bool AutoPan
{
- get => _autoPan;
- set => RaiseAndSetIfChanged(ref _autoPan, value);
+ get => GetValue(AutoPanProperty);
+ set => SetValue(AutoPanProperty, value);
}
+ public static readonly StyledProperty<MouseButtons> PanWithMouseButtonsProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, MouseButtons>(nameof(PanWithMouseButtons), MouseButtons.LeftButton | MouseButtons.MiddleButton | MouseButtons.RightButton);
+
+ /// <summary>
+ /// Gets or sets the mouse buttons to pan the image
+ /// </summary>
public MouseButtons PanWithMouseButtons
{
- get => _panWithMouseButtons;
- set => RaiseAndSetIfChanged(ref _panWithMouseButtons, value);
+ get => GetValue(PanWithMouseButtonsProperty);
+ set => SetValue(PanWithMouseButtonsProperty, value);
}
+ public static readonly StyledProperty<bool> PanWithArrowsProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(PanWithArrows), true);
+
+ /// <summary>
+ /// Gets or sets if the control can pan with the keyboard arrows
+ /// </summary>
public bool PanWithArrows
{
- get => _panWithArrows;
- set => RaiseAndSetIfChanged(ref _panWithArrows, value);
+ get => GetValue(PanWithArrowsProperty);
+ set => SetValue(PanWithArrowsProperty, value);
}
+ public static readonly StyledProperty<MouseButtons> SelectWithMouseButtonsProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, MouseButtons>(nameof(SelectWithMouseButtons), MouseButtons.LeftButton | MouseButtons.RightButton);
+
+
+ /// <summary>
+ /// Gets or sets the mouse buttons to select a region on image
+ /// </summary>
public MouseButtons SelectWithMouseButtons
{
- get => _selectWithMouseButtons;
- set => RaiseAndSetIfChanged(ref _selectWithMouseButtons, value);
+ get => GetValue(SelectWithMouseButtonsProperty);
+ set => SetValue(SelectWithMouseButtonsProperty, value);
}
- public bool InvertMouse
+ public static readonly StyledProperty<bool> InvertMousePanProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(InvertMousePan), false);
+
+ /// <summary>
+ /// Gets or sets if mouse pan is inverted
+ /// </summary>
+ public bool InvertMousePan
{
- get => _invertMouse;
- set => RaiseAndSetIfChanged(ref _invertMouse, value);
+ get => GetValue(InvertMousePanProperty);
+ set => SetValue(InvertMousePanProperty, value);
}
+ public static readonly StyledProperty<bool> AutoCenterProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(AutoCenter), true);
+
+ /// <summary>
+ /// Gets or sets if image is auto centered
+ /// </summary>
public bool AutoCenter
{
- get => _autoCenter;
- set => RaiseAndSetIfChanged(ref _autoCenter, value);
+ get => GetValue(AutoCenterProperty);
+ set => SetValue(AutoCenterProperty, value);
}
+ public static readonly StyledProperty<SizeModes> SizeModeProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, SizeModes>(nameof(SizeMode), SizeModes.Normal);
+
+ /// <summary>
+ /// Gets or sets the image size mode
+ /// </summary>
public SizeModes SizeMode
{
- get => _sizeMode;
- set => RaiseAndSetIfChanged(ref _sizeMode, value);
+ get => GetValue(SizeModeProperty);
+ set
+ {
+ SetValue(SizeModeProperty, value);
+ SizeModeChanged();
+ RaisePropertyChanged(nameof(IsHorizontalBarVisible));
+ RaisePropertyChanged(nameof(IsVerticalBarVisible));
+ }
}
- private bool _allowZoom = true;
- public virtual bool AllowZoom
+ private void SizeModeChanged()
{
- get => _allowZoom;
- set => RaiseAndSetIfChanged(ref _allowZoom, value);
+ switch (SizeMode)
+ {
+ case SizeModes.Normal:
+ HorizontalScrollBar.Visibility = ScrollBarVisibility.Auto;
+ VerticalScrollBar.Visibility = ScrollBarVisibility.Auto;
+ break;
+ case SizeModes.Stretch:
+ case SizeModes.Fit:
+ HorizontalScrollBar.Visibility = ScrollBarVisibility.Hidden;
+ VerticalScrollBar.Visibility = ScrollBarVisibility.Hidden;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(SizeMode), SizeMode, null);
+ }
}
+ public static readonly StyledProperty<bool> AllowZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, bool>(nameof(AllowZoom), true);
+
+ /// <summary>
+ /// Gets or sets if zoom is allowed
+ /// </summary>
+ public bool AllowZoom
+ {
+ get => GetValue(AllowZoomProperty);
+ set => SetValue(AllowZoomProperty, value);
+ }
+
+ public static readonly DirectProperty<AdvancedImageBox, ZoomLevelCollection> ZoomLevelsProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, ZoomLevelCollection>(
+ nameof(ZoomLevels),
+ o => o.ZoomLevels,
+ (o, v) => o.ZoomLevels = v);
+
ZoomLevelCollection _zoomLevels = ZoomLevelCollection.Default;
/// <summary>
/// Gets or sets the zoom levels.
/// </summary>
/// <value>The zoom levels.</value>
- public virtual ZoomLevelCollection ZoomLevels
+ public ZoomLevelCollection ZoomLevels
{
get => _zoomLevels;
- set => RaiseAndSetIfChanged(ref _zoomLevels, value);
+ set => SetAndRaise(ZoomLevelsProperty, ref _zoomLevels, value);
}
+ public static readonly StyledProperty<int> MinZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(MinZoom), 10);
+
+ /// <summary>
+ /// Gets or sets the minimum possible zoom.
+ /// </summary>
+ /// <value>The zoom.</value>
+ public int MinZoom
+ {
+ get => GetValue(MinZoomProperty);
+ set => SetValue(MinZoomProperty, value);
+ }
+
+ public static readonly StyledProperty<int> MaxZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(MaxZoom), 3500);
+
+ /// <summary>
+ /// Gets or sets the maximum possible zoom.
+ /// </summary>
+ /// <value>The zoom.</value>
+ public int MaxZoom
+ {
+ get => GetValue(MaxZoomProperty);
+ set => SetValue(MaxZoomProperty, value);
+ }
+
+
+ public static readonly DirectProperty<AdvancedImageBox, int> OldZoomProperty =
+ AvaloniaProperty.RegisterDirect<AdvancedImageBox, int>(
+ nameof(OldZoom),
+ o => o.OldZoom);
+
private int _oldZoom = 100;
- private int _zoom = 100;
/// <summary>
- /// Gets or sets the zoom.
+ /// Gets the previous zoom value
/// </summary>
/// <value>The zoom.</value>
- public virtual int OldZoom
+ public int OldZoom
{
get => _oldZoom;
- set => RaiseAndSetIfChanged(ref _oldZoom, value);
+ private set => SetAndRaise(OldZoomProperty, ref _oldZoom, value);
}
+ public static readonly StyledProperty<int> ZoomProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(Zoom), 100);
+
+ /// <summary>
+ /// Gets or sets the zoom.
+ /// </summary>
+ /// <value>The zoom.</value>
+ public int Zoom
+ {
+ get => GetValue(ZoomProperty);
+ set
+ {
+ var newZoom = value.Clamp(MinZoom, MaxZoom);
+
+ var previousZoom = Zoom;
+ if (previousZoom == newZoom) return;
+ OldZoom = previousZoom;
+ SetValue(ZoomProperty, value);
+
+ UpdateViewPort();
+ TriggerRender();
+
+ RaisePropertyChanged(nameof(IsHorizontalBarVisible));
+ RaisePropertyChanged(nameof(IsVerticalBarVisible));
+ }
+ }
+
+ /*
/// <summary>
/// Gets or sets the zoom.
/// </summary>
@@ -708,52 +899,90 @@ namespace UVtools.WPF.Controls
//SetZoom(value, value > Zoom ? ImageZoomActions.ZoomIn : ImageZoomActions.ZoomOut);
}
}
+ */
- public virtual bool IsActualSize => Zoom == 100;
+ public bool IsActualSize => Zoom == 100;
+
+ public static readonly StyledProperty<ISolidColorBrush> PixelGridColorProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(PixelGridColor), Brushes.DimGray);
- private ISolidColorBrush _pixelGridColor = Brushes.DimGray;
/// <summary>
/// Gets or sets the color of the pixel grid.
/// </summary>
/// <value>The color of the pixel grid.</value>
public virtual ISolidColorBrush PixelGridColor
{
- get => _pixelGridColor;
- set => RaiseAndSetIfChanged(ref _pixelGridColor, value);
+ get => GetValue(PixelGridColorProperty);
+ set => SetValue(PixelGridColorProperty, value);
}
- private int _pixelGridThreshold = 5;
+ public static readonly StyledProperty<int> PixelGridZoomThresholdProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, int>(nameof(PixelGridZoomThreshold), 5);
+
/// <summary>
/// Gets or sets the minimum size of zoomed pixel's before the pixel grid will be drawn
/// </summary>
/// <value>The pixel grid threshold.</value>
- public virtual int PixelGridThreshold
+ public int PixelGridZoomThreshold
{
- get => _pixelGridThreshold;
- set => RaiseAndSetIfChanged(ref _pixelGridThreshold, value);
+ get => GetValue(PixelGridZoomThresholdProperty);
+ set => SetValue(PixelGridZoomThresholdProperty, value);
}
+ public static readonly StyledProperty<SelectionModes> SelectionModeProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, SelectionModes>(nameof(SelectionMode), SelectionModes.None);
+
public SelectionModes SelectionMode
{
- get => _selectionMode;
- set => RaiseAndSetIfChanged(ref _selectionMode, value);
+ get => GetValue(SelectionModeProperty);
+ set => SetValue(SelectionModeProperty, value);
}
+ public static readonly StyledProperty<ISolidColorBrush> SelectionColorProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, ISolidColorBrush>(nameof(SelectionColor), new SolidColorBrush(new Color(127, 0, 128, 255)));
+
public ISolidColorBrush SelectionColor
{
- get => _selectionColor;
- set => RaiseAndSetIfChanged(ref _selectionColor, value);
+ get => GetValue(SelectionColorProperty);
+ set => SetValue(SelectionColorProperty, value);
}
+ public static readonly StyledProperty<Rect> SelectionRegionProperty =
+ AvaloniaProperty.Register<AdvancedImageBox, Rect>(nameof(SelectionRegion), Rect.Empty);
+
+
public Rect SelectionRegion
{
- get => _selectionRegion;
+ get => GetValue(SelectionRegionProperty);
set
{
- if (!RaiseAndSetIfChanged(ref _selectionRegion, value)) return;
+ SetValue(SelectionRegionProperty, value);
+ //if (!RaiseAndSetIfChanged(ref _selectionRegion, value)) return;
TriggerRender();
RaisePropertyChanged(nameof(HaveSelection));
+ RaisePropertyChanged(nameof(SelectionRegionNet));
+ RaisePropertyChanged(nameof(SelectionPixelSize));
+ }
+ }
+
+ public Rectangle SelectionRegionNet
+ {
+ get
+ {
+ var rect = SelectionRegion;
+ return new Rectangle((int) Math.Ceiling(rect.X), (int)Math.Ceiling(rect.Y),
+ (int)Math.Floor(rect.Width), (int)Math.Floor(rect.Height)
+ );
+ }
+ }
+
+ public PixelSize SelectionPixelSize
+ {
+ get
+ {
+ var rect = SelectionRegion;
+ return new PixelSize((int) Math.Floor(rect.Width), (int) Math.Floor(rect.Height));
}
}
@@ -765,23 +994,7 @@ namespace UVtools.WPF.Controls
private Vector _startScrollPosition;
private bool _isPanning;
private bool _isSelecting;
- private Bitmap _image;
private Bitmap _trackerImage;
- private bool _trackerImageAutoZoom = true;
- private byte _gridCellSize;
- private ISolidColorBrush _gridColor = Brushes.Gainsboro;
- private ISolidColorBrush _gridColorAlternate = Brushes.White;
- private bool _showGrid = true;
- private bool _autoPan = true;
- private MouseButtons _panWithMouseButtons = MouseButtons.LeftButton | MouseButtons.MiddleButton | MouseButtons.RightButton;
- private bool _panWithArrows = true;
- private MouseButtons _selectWithMouseButtons = MouseButtons.LeftButton | MouseButtons.RightButton;
- private bool _invertMouse = false;
- private bool _autoCenter = true;
- private SizeModes _sizeMode = SizeModes.Normal;
- private ISolidColorBrush _selectionColor = new SolidColorBrush(new Color(127, 0, 128, 255));
- private Rect _selectionRegion = Rect.Empty;
- private SelectionModes _selectionMode = SelectionModes.None;
private bool _canRender = true;
private Point _pointerPosition;
@@ -796,11 +1009,13 @@ namespace UVtools.WPF.Controls
HorizontalScrollBar = this.FindControl<ScrollBar>("HorizontalScrollBar");
VerticalScrollBar = this.FindControl<ScrollBar>("VerticalScrollBar");
- ViewPortControl = this.FindControl<ContentControl>("ViewPort");
+ ViewPort = this.FindControl<ContentPresenter>("ViewPort");
+
+ SizeModeChanged();
HorizontalScrollBar.Scroll += ScrollBarOnScroll;
VerticalScrollBar.Scroll += ScrollBarOnScroll;
- ViewPortControl.PointerWheelChanged += FillContainerOnPointerWheelChanged;
+ ViewPort.PointerWheelChanged += FillContainerOnPointerWheelChanged;
}
private void ScrollBarOnScroll(object? sender, ScrollEventArgs e)
@@ -828,7 +1043,6 @@ namespace UVtools.WPF.Controls
private void FillContainerOnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
{
- Debug.WriteLine("mouse whell");
e.Handled = true;
if (Image is null) return;
if (AllowZoom && SizeMode == SizeModes.Normal)
@@ -839,7 +1053,7 @@ namespace UVtools.WPF.Controls
// TODO: Really should update the source method to handle multiple increments rather than calling it multiple times
/*for (int i = 0; i < spins; i++)
{*/
- ProcessMouseZoom(e.Delta.Y > 0, e.GetPosition(ViewPortControl));
+ ProcessMouseZoom(e.Delta.Y > 0, e.GetPosition(ViewPort));
//}
}
}
@@ -865,10 +1079,10 @@ namespace UVtools.WPF.Controls
var result = action switch
{
ZoomActions.None => Zoom,
- ZoomActions.ZoomIn => ZoomLevels.NextZoom(Zoom),
- ZoomActions.ZoomOut => ZoomLevels.PreviousZoom(Zoom),
+ ZoomActions.ZoomIn => _zoomLevels.NextZoom(Zoom),
+ ZoomActions.ZoomOut => _zoomLevels.PreviousZoom(Zoom),
ZoomActions.ActualSize => 100,
- _ => throw new ArgumentOutOfRangeException(nameof(action)),
+ _ => throw new ArgumentOutOfRangeException(nameof(action), action, null),
};
return result;
}
@@ -895,8 +1109,8 @@ namespace UVtools.WPF.Controls
int currentZoom = Zoom;
int newZoom = GetZoomLevel(action);
- if (preservePosition && Zoom != currentZoom)
- CanRender = false;
+ /*if (preservePosition && Zoom != currentZoom)
+ CanRender = false;*/
RestoreSizeMode();
Zoom = newZoom;
@@ -949,7 +1163,7 @@ namespace UVtools.WPF.Controls
/// </param>
/// <returns><c>Point.Empty</c> if the point could not be matched to the source image, otherwise the new translated point</returns>
public Point PointToImage(double x, double y, bool fitToBounds = true)
- => PointToImage(x, y, fitToBounds);
+ => PointToImage(new Point(x, y), fitToBounds);
/// <summary>
/// Converts the given client size point to represent a coordinate on the source image.
@@ -962,7 +1176,7 @@ namespace UVtools.WPF.Controls
/// <returns><c>Point.Empty</c> if the point could not be matched to the source image, otherwise the new translated point</returns>
public Point PointToImage(int x, int y, bool fitToBounds = true)
{
- return PointToImage(x, y, fitToBounds);
+ return PointToImage(new Point(x, y), fitToBounds);
}
/// <summary>
@@ -985,10 +1199,11 @@ namespace UVtools.WPF.Controls
x = (point.X + Offset.X - viewport.X) / ZoomFactor;
y = (point.Y + Offset.Y - viewport.Y) / ZoomFactor;
+ var image = Image;
if (fitToBounds)
{
- x = x.Clamp(0, Image.Size.Width-1);
- y = y.Clamp(0, Image.Size.Height-1);
+ x = x.Clamp(0, image.Size.Width-1);
+ y = y.Clamp(0, image.Size.Height-1);
}
}
else
@@ -997,7 +1212,7 @@ namespace UVtools.WPF.Controls
y = 0;
}
- return new Point(x, y);
+ return new(x, y);
}
/// <summary>
@@ -1028,8 +1243,9 @@ namespace UVtools.WPF.Controls
public virtual void ScrollTo(Point imageLocation, Point relativeDisplayPoint)
{
//CanRender = false;
- var x = imageLocation.X * ZoomFactor - relativeDisplayPoint.X;
- var y = imageLocation.Y * ZoomFactor - relativeDisplayPoint.Y;
+ var zoomFactor = ZoomFactor;
+ var x = imageLocation.X * zoomFactor - relativeDisplayPoint.X;
+ var y = imageLocation.Y * zoomFactor - relativeDisplayPoint.Y;
_canRender = true;
@@ -1077,34 +1293,35 @@ namespace UVtools.WPF.Controls
}
/// <summary>
- /// Zooms to the maximum size for displaying the entire image within the bounds of the control.
+ /// Zooms to the maximum size for displaying the entire image within the bounds of the control.
/// </summary>
public virtual void ZoomToFit()
{
- if (Image is null) return;
+ var image = Image;
+ if (image is null) return;
double zoom;
double aspectRatio;
- if (Image.Size.Width > Image.Size.Height)
+ if (image.Size.Width > image.Size.Height)
{
- aspectRatio = Viewport.Width / Image.Size.Width;
+ aspectRatio = ViewPortSize.Width / image.Size.Width;
zoom = aspectRatio * 100.0;
- if (Viewport.Height < Image.Size.Height * zoom / 100.0)
+ if (ViewPortSize.Height < image.Size.Height * zoom / 100.0)
{
- aspectRatio = Viewport.Height / Image.Size.Height;
+ aspectRatio = ViewPortSize.Height / image.Size.Height;
zoom = aspectRatio * 100.0;
}
}
else
{
- aspectRatio = Viewport.Height / Image.Size.Height;
+ aspectRatio = ViewPortSize.Height / image.Size.Height;
zoom = aspectRatio * 100.0;
- if (Viewport.Width < Image.Size.Width * zoom / 100.0)
+ if (ViewPortSize.Width < image.Size.Width * zoom / 100.0)
{
- aspectRatio = Viewport.Width / Image.Size.Width;
+ aspectRatio = ViewPortSize.Width / image.Size.Width;
zoom = aspectRatio * 100.0;
}
}
@@ -1143,18 +1360,18 @@ namespace UVtools.WPF.Controls
/// </summary>
/// <param name="rectangle">The rectangle to fit the view port to.</param>
/// <param name="margin">Give a margin to rectangle by a value to zoom-out that pixel value</param>
- public virtual void ZoomToRegion(Rectangle rectangle, double margin = 0) => ZoomToRegion(rectangle.ToAvalonia(), margin);
+ public void ZoomToRegion(Rectangle rectangle, double margin = 0) => ZoomToRegion(rectangle.ToAvalonia(), margin);
/// <summary>
/// Adjusts the view port to fit the given region
/// </summary>
/// <param name="rectangle">The rectangle to fit the view port to.</param>
/// <param name="margin">Give a margin to rectangle by a value to zoom-out that pixel value</param>
- public virtual void ZoomToRegion(Rect rectangle, double margin = 0)
+ public void ZoomToRegion(Rect rectangle, double margin = 0)
{
if (margin > 0) rectangle = rectangle.Inflate(margin);
- var ratioX = Viewport.Width / rectangle.Width;
- var ratioY = Viewport.Height / rectangle.Height;
+ var ratioX = ViewPortSize.Width / rectangle.Width;
+ var ratioY = ViewPortSize.Height / rectangle.Height;
var zoomFactor = Math.Min(ratioX, ratioY);
var cx = rectangle.X + rectangle.Width / 2;
var cy = rectangle.Y + rectangle.Height / 2;
@@ -1178,14 +1395,14 @@ namespace UVtools.WPF.Controls
/// </summary>
/// <param name="imageLocation">The point of the image to attempt to center.</param>
public virtual void CenterAt(System.Drawing.Point imageLocation)
- => ScrollTo(new Point(imageLocation.X, imageLocation.Y), new Point(Viewport.Width / 2, Viewport.Height / 2));
+ => ScrollTo(new Point(imageLocation.X, imageLocation.Y), new Point(ViewPortSize.Width / 2, ViewPortSize.Height / 2));
/// <summary>
/// Centers the given point in the image in the center of the control
/// </summary>
/// <param name="imageLocation">The point of the image to attempt to center.</param>
public virtual void CenterAt(Point imageLocation)
- => ScrollTo(imageLocation, new Point(Viewport.Width / 2, Viewport.Height / 2));
+ => ScrollTo(imageLocation, new Point(ViewPortSize.Width / 2, ViewPortSize.Height / 2));
/// <summary>
/// Centers the given point in the image in the center of the control
@@ -1228,13 +1445,13 @@ namespace UVtools.WPF.Controls
//var height = scaledImageHeight <= Viewport.Height ? Viewport.Height : scaledImageHeight;
bool changed = false;
- if (HorizontalScrollBar.Maximum != width)
+ if (Math.Abs(HorizontalScrollBar.Maximum - width) > 0.01)
{
HorizontalScrollBar.Maximum = width;
changed = true;
}
- if (VerticalScrollBar.Maximum != scaledImageHeight)
+ if (Math.Abs(VerticalScrollBar.Maximum - scaledImageHeight) > 0.01)
{
VerticalScrollBar.Maximum = height;
changed = true;
@@ -1304,7 +1521,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="Point"/> which has been scaled to match the current zoom level</returns>
public virtual Point GetScaledPoint(Point source)
{
- return new Point(source.X * ZoomFactor, source.Y * ZoomFactor);
+ return new(source.X * ZoomFactor, source.Y * ZoomFactor);
}
/// <summary>
@@ -1314,7 +1531,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="PointF"/> which has been scaled to match the current zoom level</returns>
public virtual PointF GetScaledPoint(PointF source)
{
- return new PointF((float)(source.X * this.ZoomFactor), (float)(source.Y * this.ZoomFactor));
+ return new((float)(source.X * ZoomFactor), (float)(source.Y * ZoomFactor));
}
/// <summary>
@@ -1372,7 +1589,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="Rectangle"/> which has been scaled to match the current zoom level</returns>
public virtual Rect GetScaledRectangle(Rect source)
{
- return new Rect(source.Left * ZoomFactor, source.Top * ZoomFactor, source.Width * ZoomFactor, source.Height * ZoomFactor);
+ return new(source.Left * ZoomFactor, source.Top * ZoomFactor, source.Width * ZoomFactor, source.Height * ZoomFactor);
}
/// <summary>
@@ -1382,7 +1599,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="RectangleF"/> which has been scaled to match the current zoom level</returns>
public virtual RectangleF GetScaledRectangle(RectangleF source)
{
- return new RectangleF((float)(source.Left * ZoomFactor), (float)(source.Top * ZoomFactor), (float)(source.Width * ZoomFactor), (float)(source.Height * ZoomFactor));
+ return new((float)(source.Left * ZoomFactor), (float)(source.Top * ZoomFactor), (float)(source.Width * ZoomFactor), (float)(source.Height * ZoomFactor));
}
/// <summary>
@@ -1393,7 +1610,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="SizeF"/> which has been resized to match the current zoom level</returns>
public SizeF GetScaledSize(float width, float height)
{
- return this.GetScaledSize(new SizeF(width, height));
+ return GetScaledSize(new SizeF(width, height));
}
/// <summary>
@@ -1404,7 +1621,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="Size"/> which has been resized to match the current zoom level</returns>
public Size GetScaledSize(int width, int height)
{
- return this.GetScaledSize(new Size(width, height));
+ return GetScaledSize(new Size(width, height));
}
/// <summary>
@@ -1414,7 +1631,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="SizeF"/> which has been resized to match the current zoom level</returns>
public virtual SizeF GetScaledSize(SizeF source)
{
- return new SizeF((float)(source.Width * this.ZoomFactor), (float)(source.Height * this.ZoomFactor));
+ return new((float)(source.Width * ZoomFactor), (float)(source.Height * ZoomFactor));
}
/// <summary>
@@ -1424,7 +1641,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="Size"/> which has been resized to match the current zoom level</returns>
public virtual Size GetScaledSize(Size source)
{
- return new Size(source.Width * ZoomFactor, source.Height * ZoomFactor);
+ return new(source.Width * ZoomFactor, source.Height * ZoomFactor);
}
/// <summary>
@@ -1433,12 +1650,13 @@ namespace UVtools.WPF.Controls
/// <exception cref="System.InvalidOperationException">Thrown if no image is currently set</exception>
public virtual void SelectAll()
{
- if (Image is null) return;
- SelectionRegion = new Rect(0, 0, Image.Size.Width, Image.Size.Height);
+ var image = Image;
+ if (image is null) return;
+ SelectionRegion = new Rect(0, 0, image.Size.Width, image.Size.Height);
}
/// <summary>
- /// Clears any existing selection region
+ /// Clears any existing selection region
/// </summary>
public virtual void SelectNone()
{
@@ -1454,25 +1672,29 @@ namespace UVtools.WPF.Controls
public override void Render(DrawingContext context)
{
- Debug.WriteLine($"Render: {DateTime.Now.Ticks}");
+ //Debug.WriteLine($"Render: {DateTime.Now.Ticks}");
base.Render(context);
// Draw Grid
- if (ShowGrid)
+ var gridCellSize = GridCellSize;
+ if (ShowGrid & gridCellSize > 0 && (!IsHorizontalBarVisible || !IsVerticalBarVisible))
{
// draw the background
- var currentColor = GridColor;
- for (int y = 0; y < Viewport.Height; y += GridCellSize)
+ var gridColor = GridColor;
+ var altColor = GridColorAlternate;
+ var currentColor = gridColor;
+ for (int y = 0; y < ViewPortSize.Height; y += gridCellSize)
{
var firstRowColor = currentColor;
- for (int x = 0; x < Viewport.Width; x += GridCellSize)
+
+ for (int x = 0; x < ViewPortSize.Width; x += gridCellSize)
{
- context.FillRectangle(currentColor, new Rect(x, y, GridCellSize, GridCellSize));
- currentColor = ReferenceEquals(currentColor, GridColor) ? GridColorAlternate : GridColor;
+ context.FillRectangle(currentColor, new Rect(x, y, gridCellSize, gridCellSize));
+ currentColor = ReferenceEquals(currentColor, gridColor) ? altColor : gridColor;
}
- if (firstRowColor == currentColor)
- currentColor = ReferenceEquals(currentColor, GridColor) ? GridColorAlternate : GridColor;
+ if (Equals(firstRowColor, currentColor))
+ currentColor = ReferenceEquals(currentColor, gridColor) ? altColor : gridColor;
}
}
@@ -1481,18 +1703,22 @@ namespace UVtools.WPF.Controls
context.FillRectangle(Background, new Rect(0, 0, Viewport.Width, Viewport.Height));
}*/
- if (Image is null) return;
+ var image = Image;
+ if (image is null) return;
// Draw iamge
- context.DrawImage(_image,
+ context.DrawImage(image,
GetSourceImageRegion(),
GetImageViewPort()
);
+ var zoomFactor = ZoomFactor;
+
+
if (HaveTrackerImage && _pointerPosition.X >= 0 && _pointerPosition.Y >= 0)
{
var destSize = TrackerImageAutoZoom
- ? new Size(_trackerImage.Size.Width * ZoomFactor, _trackerImage.Size.Height * ZoomFactor)
- : _image.Size;
+ ? new Size(_trackerImage.Size.Width * zoomFactor, _trackerImage.Size.Height * zoomFactor)
+ : image.Size;
var destPos = new Point(
_pointerPosition.X - destSize.Width / 2,
@@ -1505,20 +1731,19 @@ namespace UVtools.WPF.Controls
//SkiaContext.SkCanvas.dr
// Draw pixel grid
- var pixelSize = ZoomFactor;
- if (pixelSize > PixelGridThreshold)
+ if (zoomFactor > PixelGridZoomThreshold && SizeMode == SizeModes.Normal)
{
var viewport = GetImageViewPort();
- var offsetX = Offset.X % pixelSize;
- var offsetY = Offset.Y % pixelSize;
+ var offsetX = Offset.X % zoomFactor;
+ var offsetY = Offset.Y % zoomFactor;
Pen pen = new(PixelGridColor);
- for (double x = viewport.X + pixelSize - offsetX; x < viewport.Right; x += pixelSize)
+ for (double x = viewport.X + zoomFactor - offsetX; x < viewport.Right; x += zoomFactor)
{
context.DrawLine(pen, new Avalonia.Point(x, viewport.X), new Avalonia.Point(x, viewport.Bottom));
}
- for (double y = viewport.Y + pixelSize - offsetY; y < viewport.Bottom; y += pixelSize)
+ for (double y = viewport.Y + zoomFactor - offsetY; y < viewport.Bottom; y += zoomFactor)
{
context.DrawLine(pen, new Avalonia.Point(viewport.Y, y), new Avalonia.Point(viewport.Right, y));
}
@@ -1529,9 +1754,10 @@ namespace UVtools.WPF.Controls
if (!SelectionRegion.IsEmpty)
{
var rect = GetOffsetRectangle(SelectionRegion);
- context.FillRectangle(SelectionColor, rect);
- Color solidColor = Color.FromArgb(255, SelectionColor.Color.R, SelectionColor.Color.G, SelectionColor.Color.B);
- context.DrawRectangle(new Pen(solidColor.ToUint32()), rect);
+ var selectionColor = SelectionColor;
+ context.FillRectangle(selectionColor, rect);
+ Color color = Color.FromArgb(255, selectionColor.Color.R, selectionColor.Color.G, selectionColor.Color.B);
+ context.DrawRectangle(new Pen(color.ToUint32()), rect);
}
}
@@ -1544,7 +1770,7 @@ namespace UVtools.WPF.Controls
{
var offset = GetOffsetPoint(new Point (source.X, source.Y));
- return new Point((int)offset.X, (int)offset.Y);
+ return new((int)offset.X, (int)offset.Y);
}
/// <summary>
@@ -1581,7 +1807,7 @@ namespace UVtools.WPF.Controls
var offsetX = viewport.Left + Offset.X;
var offsetY = viewport.Top + Offset.Y;
- return new Point(scaled.X + offsetX, scaled.Y + offsetY);
+ return new(scaled.X + offsetX, scaled.Y + offsetY);
}
/// <summary>
@@ -1596,7 +1822,7 @@ namespace UVtools.WPF.Controls
var offsetX = viewport.Left - Offset.X;
var offsetY = viewport.Top - Offset.Y;
- return new Rect(new Point(scaled.Left + offsetX, scaled.Top + offsetY), scaled.Size);
+ return new(new Point(scaled.Left + offsetX, scaled.Top + offsetY), scaled.Size);
}
/// <summary>
@@ -1609,7 +1835,7 @@ namespace UVtools.WPF.Controls
/// <returns>A <see cref="Rectangle"/> which has been resized and repositioned to match the current zoom level and image offset</returns>
public Rectangle GetOffsetRectangle(int x, int y, int width, int height)
{
- return this.GetOffsetRectangle(new Rectangle(x, y, width, height));
+ return GetOffsetRectangle(new Rectangle(x, y, width, height));
}
/// <summary>
@@ -1637,8 +1863,7 @@ namespace UVtools.WPF.Controls
var offsetX = viewport.Left + Offset.X;
var offsetY = viewport.Top + Offset.Y;
- return new Rectangle(new System.Drawing.Point((int)(scaled.Left + offsetX), (int)(scaled.Top + offsetY)),
- new System.Drawing.Size((int)scaled.Size.Width, (int)scaled.Size.Height));
+ return new(new System.Drawing.Point((int)(scaled.Left + offsetX), (int)(scaled.Top + offsetY)), new System.Drawing.Size((int)scaled.Size.Width, (int)scaled.Size.Height));
}
/// <summary>
@@ -1650,7 +1875,8 @@ namespace UVtools.WPF.Controls
/// </returns>
public Rectangle FitRectangle(Rectangle rectangle)
{
- if (Image is null) return Rectangle.Empty;
+ var image = Image;
+ if (image is null) return Rectangle.Empty;
var x = rectangle.X;
var y = rectangle.Y;
var w = rectangle.Width;
@@ -1666,17 +1892,17 @@ namespace UVtools.WPF.Controls
y = 0;
}
- if (x + w > Image.Size.Width)
+ if (x + w > image.Size.Width)
{
- w = (int)(Image.Size.Width - x);
+ w = (int)(image.Size.Width - x);
}
- if (y + h > Image.Size.Height)
+ if (y + h > image.Size.Height)
{
- h = (int)(Image.Size.Height - y);
+ h = (int)(image.Size.Height - y);
}
- return new Rectangle(x, y, w, h);
+ return new(x, y, w, h);
}
/// <summary>
@@ -1688,7 +1914,8 @@ namespace UVtools.WPF.Controls
/// </returns>
public Rect FitRectangle(Rect rectangle)
{
- if (Image is null) return Rect.Empty;
+ var image = Image;
+ if (image is null) return Rect.Empty;
var x = rectangle.X;
var y = rectangle.Y;
var w = rectangle.Width;
@@ -1706,94 +1933,119 @@ namespace UVtools.WPF.Controls
y = 0;
}
- if (x + w > Image.Size.Width)
+ if (x + w > image.Size.Width)
{
- w = Image.Size.Width - x;
+ w = image.Size.Width - x;
}
- if (y + h > Image.Size.Height)
+ if (y + h > image.Size.Height)
{
- h = Image.Size.Height - y;
+ h = image.Size.Height - y;
}
- return new Rect(x, y, w, h);
+ return new(x, y, w, h);
}
/// <summary>
/// Gets the source image region.
/// </summary>
/// <returns></returns>
- public virtual Rect GetSourceImageRegion()
+ public Rect GetSourceImageRegion()
{
- if (Image is null) return Rect.Empty;
+ var image = Image;
+ if (image is null) return Rect.Empty;
- if (SizeMode != SizeModes.Stretch)
+ switch (SizeMode)
{
- var viewPort = GetImageViewPort();
- double sourceLeft = (Offset.X / ZoomFactor);
- double sourceTop = (Offset.Y / ZoomFactor);
- double sourceWidth = (viewPort.Width / ZoomFactor);
- double sourceHeight = (viewPort.Height / ZoomFactor);
-
- return new Rect(sourceLeft, sourceTop, sourceWidth, sourceHeight);
+ case SizeModes.Normal:
+ var offset = Offset;
+ var viewPort = GetImageViewPort();
+ var zoomFactor = ZoomFactor;
+ double sourceLeft = (offset.X / zoomFactor);
+ double sourceTop = (offset.Y / zoomFactor);
+ double sourceWidth = (viewPort.Width / zoomFactor);
+ double sourceHeight = (viewPort.Height / zoomFactor);
+
+ return new(sourceLeft, sourceTop, sourceWidth, sourceHeight);
}
- return new Rect(0, 0, Image.Size.Width, Image.Size.Height);
+ return new(0, 0, image.Size.Width, image.Size.Height);
+
}
/// <summary>
/// Gets the image view port.
/// </summary>
/// <returns></returns>
- public virtual Rect GetImageViewPort()
+ public Rect GetImageViewPort()
{
- if (Viewport.Width == 0 && Viewport.Height == 0) return Rect.Empty;
+ if (ViewPortSize.Width == 0 && ViewPortSize.Height == 0) return Rect.Empty;
double xOffset = 0;
double yOffset = 0;
- double width;
- double height;
+ double width = 0;
+ double height = 0;
- if (SizeMode != SizeModes.Stretch)
+ switch (SizeMode)
{
- if (AutoCenter)
- {
- xOffset = (!IsHorizontalBarVisible ? (Viewport.Width - ScaledImageWidth) / 2 : 0);
- yOffset = (!IsVerticalBarVisible ? (Viewport.Height - ScaledImageHeight) / 2 : 0);
- }
+ case SizeModes.Normal:
+ if (AutoCenter)
+ {
+ xOffset = (!IsHorizontalBarVisible ? (ViewPortSize.Width - ScaledImageWidth) / 2 : 0);
+ yOffset = (!IsVerticalBarVisible ? (ViewPortSize.Height - ScaledImageHeight) / 2 : 0);
+ }
- width = Math.Min(ScaledImageWidth - Math.Abs(Offset.X), Viewport.Width);
- height = Math.Min(ScaledImageHeight - Math.Abs(Offset.Y), Viewport.Height);
- }
- else
- {
- width = Viewport.Width;
- height = Viewport.Height;
+ width = Math.Min(ScaledImageWidth - Math.Abs(Offset.X), ViewPortSize.Width);
+ height = Math.Min(ScaledImageHeight - Math.Abs(Offset.Y), ViewPortSize.Height);
+ break;
+ case SizeModes.Stretch:
+ width = ViewPortSize.Width;
+ height = ViewPortSize.Height;
+ break;
+ case SizeModes.Fit:
+ var image = Image;
+ double scaleFactor = Math.Min(ViewPortSize.Width / image.Size.Width, ViewPortSize.Height / image.Size.Height);
+
+ width = Math.Floor(image.Size.Width * scaleFactor);
+ height = Math.Floor(image.Size.Height * scaleFactor);
+
+ if (AutoCenter)
+ {
+ xOffset = (ViewPortSize.Width - width) / 2;
+ yOffset = (ViewPortSize.Height - height) / 2;
+ }
+
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(SizeMode), SizeMode, null);
}
- return new Rect(xOffset, yOffset, width, height);
+ return new(xOffset, yOffset, width, height);
}
/// <summary>
- /// Gets the width of the scaled image.
+ /// Gets the width of the scaled image.
/// </summary>
/// <value>The width of the scaled image.</value>
- protected virtual double ScaledImageWidth => Image.Size.Width * ZoomFactor;
+ protected double ScaledImageWidth => Image.Size.Width * ZoomFactor;
/// <summary>
- /// Gets the height of the scaled image.
+ /// Gets the height of the scaled image.
/// </summary>
/// <value>The height of the scaled image.</value>
- protected virtual double ScaledImageHeight => Image.Size.Height * ZoomFactor;
+ protected double ScaledImageHeight => Image.Size.Height * ZoomFactor;
- public double ZoomFactor => _zoom / 100.0;
+ /// <summary>
+ /// Gets the zoom factor, the zoom / 100
+ /// </summary>
+ public double ZoomFactor => Zoom / 100.0;
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
if (e.Handled
- || IsPanning
- || IsSelecting
+ || _isPanning
+ || _isSelecting
|| Image is null) return;
var pointer = e.GetCurrentPoint(this);
@@ -1816,6 +2068,7 @@ namespace UVtools.WPF.Controls
pointer.Properties.IsRightButtonPressed && (PanWithMouseButtons & MouseButtons.RightButton) != 0
)
|| !AutoPan
+ || SizeMode != SizeModes.Normal
) return;
@@ -1824,8 +2077,8 @@ namespace UVtools.WPF.Controls
var location = pointer.Position;
- if (location.X > Viewport.Width) return;
- if (location.Y > Viewport.Height) return;
+ if (location.X > ViewPortSize.Width) return;
+ if (location.Y > ViewPortSize.Height) return;
_startMousePosition = location;
}
@@ -1854,18 +2107,18 @@ namespace UVtools.WPF.Controls
var pointer = e.GetCurrentPoint(this);
PointerPosition = pointer.Position;
- if (!IsPanning && !IsSelecting)
+ if (!_isPanning && !_isSelecting)
{
TriggerRender(true);
return;
}
- if (IsPanning)
+ if (_isPanning)
{
double x;
double y;
- if (!InvertMouse)
+ if (!InvertMousePan)
{
x = _startScrollPosition.X + (_startMousePosition.X - _pointerPosition.X);
y = _startScrollPosition.Y + (_startMousePosition.Y - _pointerPosition.Y);
@@ -1878,7 +2131,7 @@ namespace UVtools.WPF.Controls
Offset = new Vector(x, y);
}
- else if (IsSelecting)
+ else if (_isSelecting)
{
double x;
double y;
@@ -1912,10 +2165,11 @@ namespace UVtools.WPF.Controls
x -= imageOffset.X - Offset.X;
y -= imageOffset.Y - Offset.Y;
- x /= ZoomFactor;
- y /= ZoomFactor;
- w /= ZoomFactor;
- h /= ZoomFactor;
+ var zoomFactor = ZoomFactor;
+ x /= zoomFactor;
+ y /= zoomFactor;
+ w /= zoomFactor;
+ h /= zoomFactor;
if (w != 0 && h != 0)
{
@@ -1925,6 +2179,38 @@ namespace UVtools.WPF.Controls
e.Handled = true;
}
+
+ public Bitmap GetSelectedBitmap()
+ {
+ var image = ImageAsWriteableBitmap;
+ if (image is null || !HaveSelection) return null;
+ var selection = SelectionRegionNet;
+ var pixelSize = SelectionPixelSize;
+ using var frameBuffer = image.Lock();
+
+ var newBitmap = new WriteableBitmap(pixelSize, image.Dpi, frameBuffer.Format, AlphaFormat.Unpremul);
+ using var newFrameBuffer = newBitmap.Lock();
+
+ int i = 0;
+
+ unsafe
+ {
+ var inputPixels = (uint*) (void*) frameBuffer.Address;
+ var targetPixels = (uint*) (void*) newFrameBuffer.Address;
+
+ for (int y = selection.Y; y < selection.Bottom; y++)
+ {
+ var thisY = y * frameBuffer.Size.Width;
+ for (int x = selection.X; x < selection.Right; x++)
+ {
+ targetPixels[i++] = inputPixels[thisY + x];
+ }
+ }
+ }
+
+ return newBitmap;
+ }
+
#endregion
}
}
diff --git a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs
index f71bef9..2ab8826 100644
--- a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs
@@ -11,6 +11,7 @@ using Avalonia.Markup.Xaml;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Operations;
+using UVtools.WPF.Extensions;
using UVtools.WPF.Windows;
namespace UVtools.WPF.Controls.Tools
@@ -117,6 +118,7 @@ namespace UVtools.WPF.Controls.Tools
if (Operation.Modifiers is null || Operation.Modifiers.Length == 0)
{
CanRun = false;
+ App.MainWindow.MessageBoxError("No available properties to edit on this file format.", BaseOperation.Title).GetAwaiter();
return;
}
diff --git a/UVtools.WPF/Extensions/BitmapExtension.cs b/UVtools.WPF/Extensions/BitmapExtension.cs
index ae5f5ce..748f190 100644
--- a/UVtools.WPF/Extensions/BitmapExtension.cs
+++ b/UVtools.WPF/Extensions/BitmapExtension.cs
@@ -118,9 +118,7 @@ namespace UVtools.WPF.Extensions
using var lockBuffer = writableBitmap.Lock();
-
-
-
+
unsafe
{
diff --git a/UVtools.WPF/Extensions/DrawingExtensions.cs b/UVtools.WPF/Extensions/DrawingExtensions.cs
index b848c48..dbaea1e 100644
--- a/UVtools.WPF/Extensions/DrawingExtensions.cs
+++ b/UVtools.WPF/Extensions/DrawingExtensions.cs
@@ -15,22 +15,22 @@ namespace UVtools.WPF.Extensions
{
public static Avalonia.Media.Color ToAvalonia(this System.Drawing.Color color)
{
- return new Avalonia.Media.Color(color.A, color.R, color.G, color.B);
+ return new(color.A, color.R, color.G, color.B);
}
public static System.Drawing.Color ToDotNet(this Avalonia.Media.Color color)
{
- return System.Drawing.Color.FromArgb(color.A, color.R, color.G, color.B);
+ return Color.FromArgb(color.A, color.R, color.G, color.B);
}
public static Rect ToAvalonia(this Rectangle rectangle)
{
- return new Rect(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
+ return new(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
}
public static Rectangle ToDotNet(this Rect rectangle)
{
- return new Rectangle((int) rectangle.X, (int) rectangle.Y, (int) rectangle.Width, (int) rectangle.Height);
+ return new((int) rectangle.X, (int) rectangle.Y, (int) rectangle.Width, (int) rectangle.Height);
}
}
}
diff --git a/UVtools.WPF/LayerCache.cs b/UVtools.WPF/LayerCache.cs
index d68f9a0..79bd632 100644
--- a/UVtools.WPF/LayerCache.cs
+++ b/UVtools.WPF/LayerCache.cs
@@ -68,12 +68,10 @@ namespace UVtools.WPF
{
get
{
- using (var framebuffer = Bitmap.Lock())
- {
- var info = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
- framebuffer.Format.ToSkColorType(), SKAlphaType.Premul);
- return SKSurface.Create(info, framebuffer.Address, framebuffer.RowBytes).Canvas;
- }
+ using var framebuffer = Bitmap.Lock();
+ var info = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
+ framebuffer.Format.ToSkColorType(), SKAlphaType.Premul);
+ return SKSurface.Create(info, framebuffer.Address, framebuffer.RowBytes).Canvas;
}
}
diff --git a/UVtools.WPF/MainWindow.LayerPreview.cs b/UVtools.WPF/MainWindow.LayerPreview.cs
index 5553887..b837352 100644
--- a/UVtools.WPF/MainWindow.LayerPreview.cs
+++ b/UVtools.WPF/MainWindow.LayerPreview.cs
@@ -93,62 +93,54 @@ namespace UVtools.WPF
LayerNavigationTooltipBorder = this.FindControl<Border>("Layer.Navigation.Tooltip.Border");
_issuesSliderCanvas = this.Find<Canvas>("Layer.Navigation.IssuesCanvas");
-
-
_showLayerImageDifference = Settings.LayerPreview.ShowLayerDifference;
_showLayerOutlinePrintVolumeBoundary = Settings.LayerPreview.VolumeBoundsOutline;
_showLayerOutlineLayerBoundary = Settings.LayerPreview.LayerBoundsOutline;
_showLayerOutlineHollowAreas = Settings.LayerPreview.HollowOutline;
-
+
LayerImageBox.ZoomLevels = new AdvancedImageBox.ZoomLevelCollection(AppSettings.ZoomLevels);
- LayerImageBox.PropertyChanged += (sender, e) =>
+
+ LayerImageBox.GetObservable(AdvancedImageBox.ZoomProperty).Subscribe(zoom =>
{
- if (e.PropertyName == nameof(LayerImageBox.Zoom))
+ var oldZoom = LayerImageBox.OldZoom;
+ RaisePropertyChanged(nameof(LayerZoomStr));
+ AddLogVerbose($"Zoomed from {oldZoom} to {zoom}");
+
+ if (_showLayerImageCrosshairs &&
+ Issues.Count > 0 &&
+ (oldZoom < 50 &&
+ zoom >= 50 // Trigger refresh as crosshair thickness increases at lower zoom levels
+ || oldZoom > 100 && zoom <= 100
+ || oldZoom is >= 50 and <= 100 && (zoom is < 50 or > 100)
+ || oldZoom <= AppSettings.CrosshairFadeLevel &&
+ zoom > AppSettings.CrosshairFadeLevel // Trigger refresh as zoom level manually crosses fade threshold
+ || oldZoom > AppSettings.CrosshairFadeLevel && zoom <= AppSettings.CrosshairFadeLevel)
+
+ )
{
- RaisePropertyChanged(nameof(LayerZoomStr));
- AddLogVerbose($"Zoomed from {LayerImageBox.OldZoom} to {LayerImageBox.Zoom}");
-
- if (ShowLayerImageCrosshairs &&
- Issues.Count > 0 &&
- (LayerImageBox.OldZoom < 50 &&
- LayerImageBox.Zoom >= 50 // Trigger refresh as crosshair thickness increases at lower zoom levels
- || LayerImageBox.OldZoom > 100 && LayerImageBox.Zoom <= 100
- || LayerImageBox.OldZoom >= 50 && LayerImageBox.OldZoom <= 100 && (LayerImageBox.Zoom < 50 || LayerImageBox.Zoom > 100)
- || LayerImageBox.OldZoom <= AppSettings.CrosshairFadeLevel &&
- LayerImageBox.Zoom > AppSettings.CrosshairFadeLevel // Trigger refresh as zoom level manually crosses fade threshold
- || LayerImageBox.OldZoom > AppSettings.CrosshairFadeLevel && LayerImageBox.Zoom <= AppSettings.CrosshairFadeLevel)
-
- )
+ if (Settings.LayerPreview.CrosshairShowOnlyOnSelectedIssues)
{
- if (Settings.LayerPreview.CrosshairShowOnlyOnSelectedIssues)
- {
- if (IssuesGrid.SelectedItems.Count == 0 || !IssuesGrid.SelectedItems.Cast<LayerIssue>().Any(
- issue => // Find a valid candidate to update layer preview, otherwise quit
- issue.LayerIndex == _actualLayer && issue.Type != LayerIssue.IssueType.EmptyLayer &&
- issue.Type != LayerIssue.IssueType.TouchingBound)) return;
- }
- else
- {
- if (!Issues.Any(
- issue => // Find a valid candidate to update layer preview, otherwise quit
- issue.LayerIndex == _actualLayer && issue.Type != LayerIssue.IssueType.EmptyLayer &&
- issue.Type != LayerIssue.IssueType.TouchingBound)) return;
- }
-
- // A timer is used here rather than invoking ShowLayer directly to eliminate sublte visual flashing
- // that will occur on the transition when the crosshair fades or unfades if ShowLayer is called directly.
- ShowLayer();
+ if (IssuesGrid.SelectedItems.Count == 0 || !IssuesGrid.SelectedItems.Cast<LayerIssue>().Any(
+ issue => // Find a valid candidate to update layer preview, otherwise quit
+ issue.LayerIndex == _actualLayer && issue.Type != LayerIssue.IssueType.EmptyLayer &&
+ issue.Type != LayerIssue.IssueType.TouchingBound)) return;
+ }
+ else
+ {
+ if (!Issues.Any(
+ issue => // Find a valid candidate to update layer preview, otherwise quit
+ issue.LayerIndex == _actualLayer && issue.Type != LayerIssue.IssueType.EmptyLayer &&
+ issue.Type != LayerIssue.IssueType.TouchingBound)) return;
}
- return;
- }
-
- if (e.PropertyName == nameof(LayerImageBox.SelectionRegion))
- {
- RaisePropertyChanged(nameof(LayerROIStr));
+ // A timer is used here rather than invoking ShowLayer directly to eliminate sublte visual flashing
+ // that will occur on the transition when the crosshair fades or unfades if ShowLayer is called directly.
+ ShowLayer();
}
+ });
- };
+ LayerImageBox.GetObservable(AdvancedImageBox.SelectionRegionProperty)
+ .Subscribe(rect => RaisePropertyChanged(nameof(LayerROIStr)));
LayerImageBox.PointerMoved += LayerImageBoxOnPointerMoved;
LayerImageBox.KeyUp += LayerImageBox_KeyUp;
@@ -1248,7 +1240,7 @@ namespace UVtools.WPF
if (!_showLayerImageFlipped) return;
if (_showLayerImageFlippedHorizontally)
{
- rectangle.Location = new Point(LayerCache.Image.Width - 1 - rectangle.Right, rectangle.Y);
+ rectangle.Location = new Point(LayerCache.Image.Width - rectangle.Right, rectangle.Y);
}
if (_showLayerImageFlippedVertically)
@@ -1801,7 +1793,8 @@ namespace UVtools.WPF
public async void SaveCurrentLayerImage()
{
- SaveFileDialog dialog = new SaveFileDialog
+ if (!IsFileLoaded) return;
+ SaveFileDialog dialog = new()
{
Filters = Helpers.PngFileFilter,
DefaultExtension = ".png",
@@ -1814,6 +1807,22 @@ namespace UVtools.WPF
LayerCache.ImageBgr.Save(result);
}
+ public async void SaveCurrentROIImage()
+ {
+ if (!IsFileLoaded || !LayerImageBox.HaveSelection) return;
+ SaveFileDialog dialog = new()
+ {
+ Filters = Helpers.PngFileFilter,
+ DefaultExtension = ".png",
+ InitialFileName = $"{Path.GetFileNameWithoutExtension(SlicerFile.FileFullPath)}_layer{ActualLayer}_ROI.png"
+ };
+
+ var result = await dialog.ShowAsync(this);
+ if (string.IsNullOrEmpty(result)) return;
+
+ LayerImageBox.GetSelectedBitmap()?.Save(result);
+ }
+
const byte _pixelEditorCursorMinDiamater = 10;
public void UpdatePixelEditorCursor()
{
diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml
index ce4c87d..50fd61d 100644
--- a/UVtools.WPF/MainWindow.axaml
+++ b/UVtools.WPF/MainWindow.axaml
@@ -284,11 +284,13 @@
VerticalAlignment="Center">
<TextBlock Text="{Binding SlicerFile.LayerHeight, StringFormat=Layer height: \{0\}mm}"/>
- <TextBlock Text=" | "/>
- <TextBlock Text="{Binding SlicerFile.BottomLayerCount, StringFormat=Bottom layers: {0}}"/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}" Text=" | "/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}"
+ Text="{Binding SlicerFile.BottomLayerCount, StringFormat=Bottom layers: {0}}"/>
- <TextBlock Text=" | "/>
- <TextBlock Text="{Binding SlicerFile.ExposureRepresentation, StringFormat=Exposure: {0}}"/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyExposureTime}" Text=" | "/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyExposureTime}"
+ Text="{Binding SlicerFile.ExposureRepresentation, StringFormat=Exposure: {0}}"/>
<TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}" Text=" | "/>
<TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}"
@@ -1875,8 +1877,20 @@
ToolTip.Tip="Save layer image to a file"
VerticalAlignment="Stretch"
Margin="1,0,0,0">
- <StackPanel Orientation="Horizontal">
- <Image Source="/Assets/Icons/save-16x16.png"/>
+ <Button.ContextMenu>
+ <ContextMenu PlacementMode="Bottom">
+ <MenuItem
+ Command="{Binding SaveCurrentROIImage}"
+ Header="Save the selected region (ROI)">
+ <MenuItem.Icon>
+ <Image Source="/Assets/Icons/object-group-16x16.png"/>
+ </MenuItem.Icon>
+ </MenuItem>
+ </ContextMenu>
+ </Button.ContextMenu>
+ <StackPanel Orientation="Horizontal">
+ <Image Source="/Assets/Icons/save-16x16.png"/>
+ <TextBlock Text=" ⮟"/>
</StackPanel>
</Button>
@@ -1891,9 +1905,8 @@
>
<uc:AdvancedImageBox
ShowGrid="{Binding Settings.LayerPreview.ShowBackgroudGrid}"
- GridCellSize="15"
- Name="LayerImage"
- />
+ GridCellSize="15"
+ Name="LayerImage"/>
</Border>
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index b8c454e..de6127a 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -508,7 +508,7 @@ namespace UVtools.WPF
var clientSizeObs = this.GetObservable(ClientSizeProperty);
clientSizeObs.Subscribe(size => UpdateLayerTrackerHighlightIssues());
var windowStateObs = this.GetObservable(WindowStateProperty);
- windowStateObs.Subscribe(size => UpdateLayerTrackerHighlightIssues());
+ windowStateObs.Subscribe(windowsState => UpdateLayerTrackerHighlightIssues());
DataContext = this;
diff --git a/UVtools.WPF/Structures/AppVersionChecker.cs b/UVtools.WPF/Structures/AppVersionChecker.cs
index 83ba22a..89cceaa 100644
--- a/UVtools.WPF/Structures/AppVersionChecker.cs
+++ b/UVtools.WPF/Structures/AppVersionChecker.cs
@@ -116,7 +116,6 @@ namespace UVtools.WPF.Structures
Debug.WriteLine($"Version checker: v{App.VersionStr} <=> v{tag_name}");
Version checkVersion = new(tag_name);
Changelog = json.body;
-
//if (string.Compare(tag_name, App.VersionStr, StringComparison.OrdinalIgnoreCase) > 0)
if (App.Version.CompareTo(checkVersion) < 0)
{
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index f5b81db..3eb829a 100644
--- a/UVtools.WPF/UVtools.WPF.csproj
+++ b/UVtools.WPF/UVtools.WPF.csproj
@@ -12,7 +12,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
- <Version>2.11.2</Version>
+ <Version>2.12.0</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs
index c66f81e..38ebee4 100644
--- a/UVtools.WPF/UserSettings.cs
+++ b/UVtools.WPF/UserSettings.cs
@@ -224,7 +224,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush TooltipOverlayBackgroundBrush
{
- get => new SolidColorBrush(_tooltipOverlayBackgroundColor.ToAvalonia());
+ get => new(_tooltipOverlayBackgroundColor.ToAvalonia());
set => TooltipOverlayBackgroundColor = new Color(value);
}
@@ -247,7 +247,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush VolumeBoundsOutlineBrush
{
- get => new SolidColorBrush(_volumeBoundsOutlineColor.ToAvalonia());
+ get => new(_volumeBoundsOutlineColor.ToAvalonia());
set => VolumeBoundsOutlineColor = new Color(value);
}
@@ -276,7 +276,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush LayerBoundsOutlineBrush
{
- get => new SolidColorBrush(_layerBoundsOutlineColor.ToAvalonia());
+ get => new(_layerBoundsOutlineColor.ToAvalonia());
set => LayerBoundsOutlineColor = new Color(value);
}
@@ -305,7 +305,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush HollowOutlineBrush
{
- get => new SolidColorBrush(_hollowOutlineColor.ToAvalonia());
+ get => new(_hollowOutlineColor.ToAvalonia());
set => HollowOutlineColor = new Color(value);
}
@@ -334,7 +334,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush MaskOutlineBrush
{
- get => new SolidColorBrush(_maskOutlineColor.ToAvalonia());
+ get => new(_maskOutlineColor.ToAvalonia());
set => MaskOutlineColor = new Color(value);
}
@@ -363,7 +363,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush PreviousLayerDifferenceBrush
{
- get => new SolidColorBrush(_previousLayerDifferenceColor.ToAvalonia());
+ get => new(_previousLayerDifferenceColor.ToAvalonia());
set => PreviousLayerDifferenceColor = new Color(value);
}
@@ -380,7 +380,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush NextLayerDifferenceBrush
{
- get => new SolidColorBrush(_nextLayerDifferenceColor.ToAvalonia());
+ get => new(_nextLayerDifferenceColor.ToAvalonia());
set => NextLayerDifferenceColor = new Color(value);
}
@@ -397,7 +397,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush BothLayerDifferenceBrush
{
- get => new SolidColorBrush(_bothLayerDifferenceColor.ToAvalonia());
+ get => new(_bothLayerDifferenceColor.ToAvalonia());
set => BothLayerDifferenceColor = new Color(value);
}
@@ -426,7 +426,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush IslandBrush
{
- get => new SolidColorBrush(_islandColor.ToAvalonia());
+ get => new(_islandColor.ToAvalonia());
set => IslandColor = new Color(value);
}
@@ -443,7 +443,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush IslandHighlightBrush
{
- get => new SolidColorBrush(_islandHighlightColor.ToAvalonia());
+ get => new(_islandHighlightColor.ToAvalonia());
set => IslandHighlightColor = new Color(value);
}
@@ -460,7 +460,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush OverhangBrush
{
- get => new SolidColorBrush(_overhangColor.ToAvalonia());
+ get => new(_overhangColor.ToAvalonia());
set => OverhangColor = new Color(value);
}
@@ -477,7 +477,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush OverhangHighlightBrush
{
- get => new SolidColorBrush(_overhangHighlightColor.ToAvalonia());
+ get => new(_overhangHighlightColor.ToAvalonia());
set => OverhangHighlightColor = new Color(value);
}
@@ -494,7 +494,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush ResinTrapBrush
{
- get => new SolidColorBrush(_resinTrapColor.ToAvalonia());
+ get => new(_resinTrapColor.ToAvalonia());
set => ResinTrapColor = new Color(value);
}
@@ -511,7 +511,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush ResinTrapHighlightBrush
{
- get => new SolidColorBrush(_resinTrapHighlightColor.ToAvalonia());
+ get => new(_resinTrapHighlightColor.ToAvalonia());
set => ResinTrapHighlightColor = new Color(value);
}
public Color TouchingBoundsColor
@@ -527,7 +527,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush TouchingBoundsBrush
{
- get => new SolidColorBrush(_touchingBoundsColor.ToAvalonia());
+ get => new(_touchingBoundsColor.ToAvalonia());
set => TouchingBoundsColor = new Color(value);
}
@@ -544,7 +544,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush CrosshairBrush
{
- get => new SolidColorBrush(_crosshairColor.ToAvalonia());
+ get => new(_crosshairColor.ToAvalonia());
set => CrosshairColor = new Color(value);
}
@@ -877,15 +877,15 @@ namespace UVtools.WPF
[Serializable]
public sealed class PixelEditorUserSettings : BindableBase
{
- private Color _addPixelColor = new Color(255, 144, 238, 144);
- private Color _addPixelHighlightColor = new Color(255, 0, 255, 0);
- private Color _removePixelColor = new Color(255, 219, 112, 147);
- private Color _removePixelHighlightColor = new Color(255, 139, 0, 0);
- private Color _supportsColor = new Color(255, 0, 255, 255);
- private Color _supportsHighlightColor = new Color(255, 0, 139, 139);
- private Color _drainHoleColor = new Color(255, 142, 69, 133);
- private Color _drainHoleHighlightColor = new Color(255, 159, 0, 197);
- private Color _cursorColor = new Color(150, 52, 152, 219);
+ private Color _addPixelColor = new(255, 144, 238, 144);
+ private Color _addPixelHighlightColor = new(255, 0, 255, 0);
+ private Color _removePixelColor = new(255, 219, 112, 147);
+ private Color _removePixelHighlightColor = new(255, 139, 0, 0);
+ private Color _supportsColor = new(255, 0, 255, 255);
+ private Color _supportsHighlightColor = new(255, 0, 139, 139);
+ private Color _drainHoleColor = new(255, 142, 69, 133);
+ private Color _drainHoleHighlightColor = new(255, 159, 0, 197);
+ private Color _cursorColor = new(150, 52, 152, 219);
private bool _partialUpdateIslandsOnEditing = true;
private bool _closeEditorOnApply;
@@ -902,7 +902,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush AddPixelBrush
{
- get => new SolidColorBrush(_addPixelColor.ToAvalonia());
+ get => new(_addPixelColor.ToAvalonia());
set => AddPixelColor = new Color(value);
}
@@ -919,7 +919,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush AddPixelHighlightBrush
{
- get => new SolidColorBrush(_addPixelHighlightColor.ToAvalonia());
+ get => new(_addPixelHighlightColor.ToAvalonia());
set => AddPixelHighlightColor = new Color(value);
}
@@ -936,7 +936,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush RemovePixelBrush
{
- get => new SolidColorBrush(_removePixelColor.ToAvalonia());
+ get => new(_removePixelColor.ToAvalonia());
set => RemovePixelColor = new Color(value);
}
@@ -953,7 +953,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush RemovePixelHighlightBrush
{
- get => new SolidColorBrush(_removePixelHighlightColor.ToAvalonia());
+ get => new(_removePixelHighlightColor.ToAvalonia());
set => RemovePixelHighlightColor = new Color(value);
}
@@ -970,7 +970,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush SupportsBrush
{
- get => new SolidColorBrush(_supportsColor.ToAvalonia());
+ get => new(_supportsColor.ToAvalonia());
set => SupportsColor = new Color(value);
}
@@ -987,7 +987,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush SupportsHighlightBrush
{
- get => new SolidColorBrush(_supportsHighlightColor.ToAvalonia());
+ get => new(_supportsHighlightColor.ToAvalonia());
set => SupportsHighlightColor = new Color(value);
}
@@ -1004,7 +1004,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush DrainHoleBrush
{
- get => new SolidColorBrush(_drainHoleColor.ToAvalonia());
+ get => new(_drainHoleColor.ToAvalonia());
set => DrainHoleColor = new Color(value);
}
@@ -1021,7 +1021,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush DrainHoleHighlightBrush
{
- get => new SolidColorBrush(_drainHoleHighlightColor.ToAvalonia());
+ get => new(_drainHoleHighlightColor.ToAvalonia());
set => DrainHoleHighlightColor = new Color(value);
}
@@ -1038,7 +1038,7 @@ namespace UVtools.WPF
[XmlIgnore]
public SolidColorBrush CursorBrush
{
- get => new SolidColorBrush(_cursorColor.ToAvalonia());
+ get => new(_cursorColor.ToAvalonia());
set => CursorColor = new Color(value);
}
diff --git a/UVtools.WPF/Windows/ToolWindow.axaml b/UVtools.WPF/Windows/ToolWindow.axaml
index 5b244dc..15f20dc 100644
--- a/UVtools.WPF/Windows/ToolWindow.axaml
+++ b/UVtools.WPF/Windows/ToolWindow.axaml
@@ -224,6 +224,7 @@
<StackPanel Margin="15" Spacing="5">
<TextBlock VerticalAlignment="Center"
+ IsVisible="{Binding IsROIVisible}"
Text="{Binding ROI, StringFormat=Region: \{0\}}" />
<CheckBox