mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-11 15:26:55 +03:00
Refactor ImageFormBase with a UnaryImageFormBase subclass
This commit is contained in:
parent
f37d2fd348
commit
3bcd97a97a
@ -3,7 +3,7 @@ using NAPS2.EtoForms.Widgets;
|
||||
|
||||
namespace NAPS2.EtoForms.Ui;
|
||||
|
||||
public class BlackWhiteForm : ImageFormBase
|
||||
public class BlackWhiteForm : UnaryImageFormBase
|
||||
{
|
||||
private readonly SliderWithTextBox _thresholdSlider = new();
|
||||
|
||||
|
@ -3,7 +3,7 @@ using NAPS2.EtoForms.Widgets;
|
||||
|
||||
namespace NAPS2.EtoForms.Ui;
|
||||
|
||||
public class BrightContForm : ImageFormBase
|
||||
public class BrightContForm : UnaryImageFormBase
|
||||
{
|
||||
private readonly SliderWithTextBox _brightnessSlider = new();
|
||||
private readonly SliderWithTextBox _contrastSlider = new();
|
||||
|
@ -23,8 +23,6 @@ public class CombineForm : ImageFormBase
|
||||
_scanningContext = scanningContext;
|
||||
Icon = new Icon(1f, Icons.combine.ToEtoImage());
|
||||
Title = UiStrings.Combine;
|
||||
CanApplyToAllSelected = false;
|
||||
ShowRevertButton = false;
|
||||
}
|
||||
|
||||
private UiImage Image1 { get; set; } = null!;
|
||||
@ -93,10 +91,10 @@ public class CombineForm : ImageFormBase
|
||||
// Otherwise, we look for the previous image in the list, which should be considered the first image, and then
|
||||
// this image is the second.
|
||||
var nextImage = SelectedImages?.ElementAtOrDefault(1) ??
|
||||
_imageList.Images.ElementAtOrDefault(_imageList.Images.IndexOf(Image) + 1);
|
||||
ImageList.Images.ElementAtOrDefault(ImageList.Images.IndexOf(Image) + 1);
|
||||
Image1 = nextImage != null
|
||||
? Image
|
||||
: _imageList.Images.ElementAtOrDefault(_imageList.Images.IndexOf(Image) - 1) ??
|
||||
: ImageList.Images.ElementAtOrDefault(ImageList.Images.IndexOf(Image) - 1) ??
|
||||
throw new InvalidOperationException("No image to combine with");
|
||||
Image2 = nextImage ?? Image;
|
||||
|
||||
@ -152,13 +150,13 @@ public class CombineForm : ImageFormBase
|
||||
using var combinedImage = CombineImages(renderedImage1, renderedImage2);
|
||||
|
||||
// TODO: Use working images for thumbnail?
|
||||
var thumbnail = combinedImage.Clone().PerformTransform(new ThumbnailTransform(_thumbnailController.RenderSize));
|
||||
var thumbnail = combinedImage.Clone().PerformTransform(new ThumbnailTransform(ThumbnailController.RenderSize));
|
||||
var ppd = new PostProcessingData { Thumbnail = thumbnail, ThumbnailTransformState = TransformState.Empty };
|
||||
var processedImage = _scanningContext.CreateProcessedImage(combinedImage).WithPostProcessingData(ppd, true);
|
||||
|
||||
_imageList.Mutate(new ListMutation<UiImage>.InsertAfter(new UiImage(processedImage), Image));
|
||||
ImageList.Mutate(new ListMutation<UiImage>.InsertAfter(new UiImage(processedImage), Image));
|
||||
// TODO: Maybe have a checkbox to keep the original images?
|
||||
_imageList.Mutate(new ListMutation<UiImage>.DeleteSelected(), ListSelection.Of(Image1, Image2));
|
||||
ImageList.Mutate(new ListMutation<UiImage>.DeleteSelected(), ListSelection.Of(Image1, Image2));
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
|
@ -3,7 +3,7 @@ using Eto.Forms;
|
||||
|
||||
namespace NAPS2.EtoForms.Ui;
|
||||
|
||||
public class CropForm : ImageFormBase
|
||||
public class CropForm : UnaryImageFormBase
|
||||
{
|
||||
private static CropTransform? _lastTransform;
|
||||
|
||||
@ -49,8 +49,9 @@ public class CropForm : ImageFormBase
|
||||
private int HandleLength =>
|
||||
(int) Math.Max(Math.Round(Math.Min(_overlayH, _overlayW) * HANDLE_LENGTH_RATIO), HANDLE_MIN_LENGTH);
|
||||
|
||||
protected override void InitTransform()
|
||||
protected override void OnPreLoad(EventArgs e)
|
||||
{
|
||||
base.OnPreLoad(e);
|
||||
if (_lastTransform != null && _lastTransform.OriginalWidth == ImageWidth &&
|
||||
_lastTransform.OriginalHeight == ImageHeight)
|
||||
{
|
||||
@ -65,12 +66,13 @@ public class CropForm : ImageFormBase
|
||||
}
|
||||
}
|
||||
|
||||
protected override void TransformSaved()
|
||||
protected override void Apply()
|
||||
{
|
||||
base.Apply();
|
||||
_lastTransform = (CropTransform) Transforms.Single();
|
||||
}
|
||||
|
||||
protected override void ResetTransform()
|
||||
protected override void Revert()
|
||||
{
|
||||
_cropT = _cropL = _cropB = _cropR = _realT = _realL = _realB = _realR = 0;
|
||||
Overlay.Invalidate();
|
||||
|
@ -3,7 +3,7 @@ using NAPS2.EtoForms.Widgets;
|
||||
|
||||
namespace NAPS2.EtoForms.Ui;
|
||||
|
||||
public class HueSatForm : ImageFormBase
|
||||
public class HueSatForm : UnaryImageFormBase
|
||||
{
|
||||
private readonly SliderWithTextBox _hueSlider = new();
|
||||
private readonly SliderWithTextBox _saturationSlider = new();
|
||||
|
@ -1,49 +1,49 @@
|
||||
using Eto.Drawing;
|
||||
using Eto.Forms;
|
||||
using NAPS2.EtoForms.Layout;
|
||||
using NAPS2.EtoForms.Widgets;
|
||||
|
||||
namespace NAPS2.EtoForms.Ui;
|
||||
|
||||
public abstract class ImageFormBase : EtoDialogBase
|
||||
{
|
||||
protected readonly UiImageList _imageList;
|
||||
protected readonly ThumbnailController _thumbnailController;
|
||||
|
||||
private readonly ImageView _imageView = new();
|
||||
private readonly CheckBox _applyToSelected = new();
|
||||
private readonly Button _revert = C.Button(UiStrings.Revert);
|
||||
|
||||
private readonly RefreshThrottle _renderThrottle;
|
||||
|
||||
// Image bounds in the coordinate space of the overlay control
|
||||
protected float _overlayT, _overlayL, _overlayR, _overlayB, _overlayW, _overlayH;
|
||||
|
||||
public ImageFormBase(Naps2Config config, UiImageList imageList, ThumbnailController thumbnailController) :
|
||||
protected ImageFormBase(Naps2Config config, UiImageList imageList, ThumbnailController thumbnailController) :
|
||||
base(config)
|
||||
{
|
||||
_imageList = imageList;
|
||||
_thumbnailController = thumbnailController;
|
||||
_revert.Click += Revert;
|
||||
ImageList = imageList;
|
||||
ThumbnailController = thumbnailController;
|
||||
_renderThrottle = new RefreshThrottle(RenderImage);
|
||||
Overlay.Paint += PaintOverlay;
|
||||
Overlay.SizeChanged += (_, _) => UpdateImageCoords();
|
||||
FormStateController.DefaultExtraLayoutSize = new Size(400, 400);
|
||||
}
|
||||
|
||||
public UiImage Image { get; set; } = null!;
|
||||
public List<UiImage>? SelectedImages { get; set; }
|
||||
|
||||
protected UiImageList ImageList { get; }
|
||||
protected ThumbnailController ThumbnailController { get; }
|
||||
|
||||
protected int ImageHeight { get; set; }
|
||||
protected int ImageWidth { get; set; }
|
||||
|
||||
protected IMemoryImage? DisplayImage { get; set; }
|
||||
protected Drawable Overlay { get; } = new();
|
||||
protected int OverlayBorderSize { get; set; }
|
||||
|
||||
protected override void BuildLayout()
|
||||
{
|
||||
foreach (var slider in Sliders)
|
||||
{
|
||||
slider.ValueChanged += UpdatePreviewBox;
|
||||
}
|
||||
|
||||
LayoutController.Content = L.Column(
|
||||
Overlay.Scale(),
|
||||
CreateControls(),
|
||||
SelectedImages is { Count: > 1 } && CanApplyToAllSelected ? _applyToSelected : C.None(),
|
||||
L.Row(
|
||||
ShowRevertButton ? _revert : C.None(),
|
||||
CreateExtraButtons(),
|
||||
C.Filler(),
|
||||
L.OkCancel(
|
||||
C.OkButton(this, beforeClose: Apply),
|
||||
@ -52,15 +52,26 @@ public abstract class ImageFormBase : EtoDialogBase
|
||||
);
|
||||
}
|
||||
|
||||
protected int ImageHeight { get; set; }
|
||||
protected int ImageWidth { get; set; }
|
||||
protected override void OnPreLoad(EventArgs e)
|
||||
{
|
||||
base.OnPreLoad(e);
|
||||
InitDisplayImage();
|
||||
ImageWidth = DisplayImage!.Width;
|
||||
ImageHeight = DisplayImage.Height;
|
||||
}
|
||||
|
||||
protected IMemoryImage? WorkingImage { get; set; }
|
||||
protected IMemoryImage? DisplayImage { get; set; }
|
||||
protected Drawable Overlay { get; } = new();
|
||||
protected int OverlayBorderSize { get; set; }
|
||||
protected override void OnLoad(EventArgs e)
|
||||
{
|
||||
base.OnLoad(e);
|
||||
UpdatePreviewBox();
|
||||
}
|
||||
|
||||
protected SliderWithTextBox[] Sliders { get; set; } = Array.Empty<SliderWithTextBox>();
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
DisplayImage?.Dispose();
|
||||
_imageView.Image?.Dispose();
|
||||
}
|
||||
|
||||
protected override void OnSizeChanged(EventArgs e)
|
||||
{
|
||||
@ -124,79 +135,20 @@ public abstract class ImageFormBase : EtoDialogBase
|
||||
});
|
||||
}
|
||||
|
||||
protected virtual LayoutElement CreateControls()
|
||||
protected abstract LayoutElement CreateControls();
|
||||
|
||||
protected virtual LayoutElement CreateExtraButtons() => C.None();
|
||||
|
||||
protected abstract IMemoryImage RenderPreview();
|
||||
|
||||
protected abstract void InitDisplayImage();
|
||||
|
||||
protected abstract void Apply();
|
||||
|
||||
protected void UpdatePreviewBox()
|
||||
{
|
||||
return L.Column(Sliders.Select(x => (LayoutElement) x).ToArray());
|
||||
}
|
||||
|
||||
public UiImage Image { get; set; } = null!;
|
||||
|
||||
public List<UiImage>? SelectedImages { get; set; }
|
||||
|
||||
protected bool CanScaleWorkingImage { get; set; } = true;
|
||||
|
||||
protected bool CanApplyToAllSelected { get; set; } = true;
|
||||
|
||||
protected bool ShowRevertButton { get; set; } = true;
|
||||
|
||||
protected virtual List<Transform> Transforms => throw new NotImplementedException();
|
||||
|
||||
private bool TransformMultiple => SelectedImages != null && _applyToSelected.IsChecked();
|
||||
|
||||
private List<UiImage> ImagesToTransform => TransformMultiple ? SelectedImages! : [Image];
|
||||
|
||||
protected virtual IMemoryImage RenderPreview()
|
||||
{
|
||||
var result = WorkingImage!.Clone();
|
||||
return result.PerformAllTransforms(Transforms);
|
||||
}
|
||||
|
||||
protected virtual void InitTransform()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void ResetTransform()
|
||||
{
|
||||
foreach (var slider in Sliders)
|
||||
{
|
||||
slider.IntValue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void TransformSaved()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnLoad(EventArgs e)
|
||||
{
|
||||
base.OnLoad(e);
|
||||
_applyToSelected.Text = string.Format(UiStrings.ApplyToSelected, SelectedImages?.Count);
|
||||
|
||||
InitDisplayImage();
|
||||
ImageWidth = DisplayImage!.Width;
|
||||
ImageHeight = DisplayImage.Height;
|
||||
InitTransform();
|
||||
UpdatePreviewBox();
|
||||
}
|
||||
|
||||
protected virtual void InitDisplayImage()
|
||||
{
|
||||
using var imageToRender = Image.GetClonedImage();
|
||||
WorkingImage = imageToRender.Render();
|
||||
|
||||
if (CanScaleWorkingImage)
|
||||
{
|
||||
// Scale down the image to the screen size for better efficiency without losing much fidelity
|
||||
var workingArea = GetScreenWorkingArea();
|
||||
var widthRatio = WorkingImage.Width / workingArea.Width;
|
||||
var heightRatio = WorkingImage.Height / workingArea.Height;
|
||||
if (widthRatio > 1 || heightRatio > 1)
|
||||
{
|
||||
WorkingImage = WorkingImage.PerformTransform(new ScaleTransform(1 / Math.Max(widthRatio, heightRatio)));
|
||||
}
|
||||
}
|
||||
|
||||
DisplayImage = WorkingImage.Clone();
|
||||
Overlay.Invalidate();
|
||||
_renderThrottle.RunAction();
|
||||
}
|
||||
|
||||
protected RectangleF GetScreenWorkingArea()
|
||||
@ -217,41 +169,4 @@ public abstract class ImageFormBase : EtoDialogBase
|
||||
// Assume 1080p screen by default
|
||||
return new RectangleF(0, 0, 1920, 1080);
|
||||
}
|
||||
|
||||
protected void UpdatePreviewBox()
|
||||
{
|
||||
Overlay.Invalidate();
|
||||
_renderThrottle.RunAction();
|
||||
}
|
||||
|
||||
protected virtual void Apply()
|
||||
{
|
||||
IMemoryImage? firstImageThumb = null;
|
||||
if (WorkingImage != null)
|
||||
{
|
||||
// Optimize thumbnail rendering for the first (or only) image since we already have it loaded into memory
|
||||
var transformed = WorkingImage.Clone().PerformAllTransforms(Transforms);
|
||||
firstImageThumb =
|
||||
transformed.PerformTransform(new ThumbnailTransform(_thumbnailController.RenderSize));
|
||||
}
|
||||
var mutation = new ImageListMutation.AddTransforms(
|
||||
Transforms.ToList(),
|
||||
new Dictionary<UiImage, IMemoryImage?> { [Image] = firstImageThumb });
|
||||
_imageList.Mutate(mutation, ListSelection.From(ImagesToTransform));
|
||||
TransformSaved();
|
||||
}
|
||||
|
||||
private void Revert(object? sender, EventArgs e)
|
||||
{
|
||||
ResetTransform();
|
||||
UpdatePreviewBox();
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
WorkingImage?.Dispose();
|
||||
DisplayImage?.Dispose();
|
||||
_imageView.Image?.Dispose();
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ using NAPS2.EtoForms.Widgets;
|
||||
|
||||
namespace NAPS2.EtoForms.Ui;
|
||||
|
||||
public class RotateForm : ImageFormBase
|
||||
public class RotateForm : UnaryImageFormBase
|
||||
{
|
||||
private const int MIN_LINE_DISTANCE = 50;
|
||||
|
||||
|
@ -3,7 +3,7 @@ using NAPS2.EtoForms.Widgets;
|
||||
|
||||
namespace NAPS2.EtoForms.Ui;
|
||||
|
||||
public class SharpenForm : ImageFormBase
|
||||
public class SharpenForm : UnaryImageFormBase
|
||||
{
|
||||
private readonly SliderWithTextBox _sharpenSlider = new();
|
||||
|
||||
|
@ -2,7 +2,7 @@ using Eto.Drawing;
|
||||
|
||||
namespace NAPS2.EtoForms.Ui;
|
||||
|
||||
public class SplitForm : ImageFormBase
|
||||
public class SplitForm : UnaryImageFormBase
|
||||
{
|
||||
public SplitForm(Naps2Config config, UiImageList imageList, ThumbnailController thumbnailController) :
|
||||
base(config, imageList, thumbnailController)
|
||||
|
108
NAPS2.Lib/EtoForms/Ui/UnaryImageFormBase.cs
Normal file
108
NAPS2.Lib/EtoForms/Ui/UnaryImageFormBase.cs
Normal file
@ -0,0 +1,108 @@
|
||||
using Eto.Forms;
|
||||
using NAPS2.EtoForms.Layout;
|
||||
using NAPS2.EtoForms.Widgets;
|
||||
|
||||
namespace NAPS2.EtoForms.Ui;
|
||||
|
||||
public abstract class UnaryImageFormBase(
|
||||
Naps2Config config,
|
||||
UiImageList imageList,
|
||||
ThumbnailController thumbnailController)
|
||||
: ImageFormBase(config, imageList, thumbnailController)
|
||||
{
|
||||
private readonly CheckBox _applyToSelected = new();
|
||||
private readonly Button _revert = C.Button(UiStrings.Revert);
|
||||
|
||||
protected IMemoryImage? WorkingImage { get; set; }
|
||||
|
||||
protected SliderWithTextBox[] Sliders { get; set; } = Array.Empty<SliderWithTextBox>();
|
||||
|
||||
protected bool CanScaleWorkingImage { get; set; } = true;
|
||||
|
||||
protected abstract List<Transform> Transforms { get; }
|
||||
|
||||
protected override void OnPreLoad(EventArgs e)
|
||||
{
|
||||
_applyToSelected.Text = string.Format(UiStrings.ApplyToSelected, SelectedImages?.Count);
|
||||
_revert.Click += (_, _) =>
|
||||
{
|
||||
Revert();
|
||||
UpdatePreviewBox();
|
||||
};
|
||||
foreach (var slider in Sliders)
|
||||
{
|
||||
slider.ValueChanged += UpdatePreviewBox;
|
||||
}
|
||||
base.OnPreLoad(e);
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
WorkingImage?.Dispose();
|
||||
}
|
||||
|
||||
protected override LayoutElement CreateControls()
|
||||
{
|
||||
return L.Column([
|
||||
..Sliders.Select(x => (LayoutElement) x),
|
||||
SelectedImages is { Count: > 1 } ? _applyToSelected : C.None()
|
||||
]);
|
||||
}
|
||||
|
||||
protected override LayoutElement CreateExtraButtons() => _revert;
|
||||
|
||||
private List<UiImage> ImagesToTransform => SelectedImages != null && _applyToSelected.IsChecked()
|
||||
? SelectedImages!
|
||||
: [Image];
|
||||
|
||||
protected override IMemoryImage RenderPreview()
|
||||
{
|
||||
var result = WorkingImage!.Clone();
|
||||
return result.PerformAllTransforms(Transforms);
|
||||
}
|
||||
|
||||
protected override void InitDisplayImage()
|
||||
{
|
||||
using var imageToRender = Image.GetClonedImage();
|
||||
WorkingImage = imageToRender.Render();
|
||||
|
||||
if (CanScaleWorkingImage)
|
||||
{
|
||||
// Scale down the image to the screen size for better efficiency without losing much fidelity
|
||||
var workingArea = GetScreenWorkingArea();
|
||||
var widthRatio = WorkingImage.Width / workingArea.Width;
|
||||
var heightRatio = WorkingImage.Height / workingArea.Height;
|
||||
if (widthRatio > 1 || heightRatio > 1)
|
||||
{
|
||||
WorkingImage = WorkingImage.PerformTransform(new ScaleTransform(1 / Math.Max(widthRatio, heightRatio)));
|
||||
}
|
||||
}
|
||||
|
||||
DisplayImage = WorkingImage.Clone();
|
||||
}
|
||||
|
||||
protected override void Apply()
|
||||
{
|
||||
IMemoryImage? firstImageThumb = null;
|
||||
if (WorkingImage != null)
|
||||
{
|
||||
// Optimize thumbnail rendering for the first (or only) image since we already have it loaded into memory
|
||||
var transformed = WorkingImage.Clone().PerformAllTransforms(Transforms);
|
||||
firstImageThumb =
|
||||
transformed.PerformTransform(new ThumbnailTransform(ThumbnailController.RenderSize));
|
||||
}
|
||||
var mutation = new ImageListMutation.AddTransforms(
|
||||
Transforms.ToList(),
|
||||
new Dictionary<UiImage, IMemoryImage?> { [Image] = firstImageThumb });
|
||||
ImageList.Mutate(mutation, ListSelection.From(ImagesToTransform));
|
||||
}
|
||||
|
||||
protected virtual void Revert()
|
||||
{
|
||||
foreach (var slider in Sliders)
|
||||
{
|
||||
slider.IntValue = 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user