119 lines
3.9 KiB
C#
119 lines
3.9 KiB
C#
using System.Collections.Generic;
|
|
using System.Windows;
|
|
using System.Windows.Media;
|
|
using Kniip.Editor.Annotations;
|
|
|
|
namespace Kniip.Editor;
|
|
|
|
/// <summary>
|
|
/// Tekent het knipsel met alle annotaties, plus de selectie en het bijsnijkader.
|
|
/// Het element is precies zo groot als de afbeelding in pixels; zoomen gebeurt met een LayoutTransform erboven.
|
|
/// </summary>
|
|
public sealed class AnnotationSurface : FrameworkElement
|
|
{
|
|
private static readonly Brush AccentBrush = RenderHelpers.Freeze(new SolidColorBrush(Palette.Accent));
|
|
private static readonly Brush GhostHandleBrush = RenderHelpers.Freeze(new SolidColorBrush(Color.FromArgb(170, 255, 255, 255)));
|
|
private static readonly Brush CropShadeBrush = RenderHelpers.Freeze(new SolidColorBrush(Color.FromArgb(120, 0, 0, 0)));
|
|
|
|
public EditorDocument? Document { get; set; }
|
|
|
|
public Annotation? Selected { get; set; }
|
|
|
|
public Rect? CropRect { get; set; }
|
|
|
|
/// <summary>Schaal van de weergave (DIP per afbeeldingspixel), om grepen altijd even groot te tonen.</summary>
|
|
public double ViewScale { get; set; } = 1;
|
|
|
|
public double HandleSize => 9 / ViewScale;
|
|
|
|
public AnnotationSurface()
|
|
{
|
|
Focusable = true;
|
|
FocusVisualStyle = null;
|
|
ClipToBounds = true;
|
|
}
|
|
|
|
protected override Size MeasureOverride(Size availableSize) =>
|
|
Document == null ? new Size(0, 0) : new Size(Document.Width, Document.Height);
|
|
|
|
protected override void OnRender(DrawingContext dc)
|
|
{
|
|
if (Document == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DocumentRenderer.Render(dc, Document);
|
|
|
|
if (Selected != null && !Selected.IsHidden && Document.Items.Contains(Selected))
|
|
{
|
|
DrawSelection(dc, Selected);
|
|
}
|
|
|
|
if (CropRect is Rect crop)
|
|
{
|
|
var full = new RectangleGeometry(new Rect(0, 0, Document.Width, Document.Height));
|
|
var shade = Geometry.Combine(full, new RectangleGeometry(crop), GeometryCombineMode.Exclude, null);
|
|
dc.DrawGeometry(CropShadeBrush, null, shade);
|
|
dc.DrawRectangle(null, CreateDashedPen(Brushes.White), crop);
|
|
dc.DrawRectangle(null, CreateDashedPen(AccentBrush, 4), crop);
|
|
}
|
|
}
|
|
|
|
private void DrawSelection(DrawingContext dc, Annotation item)
|
|
{
|
|
Rect bounds = item.Bounds;
|
|
double pad = 3 / ViewScale;
|
|
bounds.Inflate(pad, pad);
|
|
dc.DrawRectangle(null, CreateDashedPen(AccentBrush), bounds);
|
|
|
|
IReadOnlyList<Point> handles = item.GetHandles();
|
|
if (handles.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
double size = HandleSize;
|
|
var pen = new Pen(AccentBrush, 1.2 / ViewScale);
|
|
for (int i = 0; i < handles.Count; i++)
|
|
{
|
|
Point h = handles[i];
|
|
if (item.IsGhostHandle(i))
|
|
{
|
|
double radius = size * 0.35;
|
|
dc.DrawEllipse(GhostHandleBrush, pen, h, radius, radius);
|
|
continue;
|
|
}
|
|
|
|
dc.DrawRoundedRectangle(Brushes.White, pen, new Rect(h.X - size / 2, h.Y - size / 2, size, size),
|
|
1.5 / ViewScale, 1.5 / ViewScale);
|
|
}
|
|
}
|
|
|
|
private Pen CreateDashedPen(Brush brush, double offset = 0) => new(brush, 1 / ViewScale)
|
|
{
|
|
DashStyle = new DashStyle(new[] { 4.0, 4.0 }, offset)
|
|
};
|
|
|
|
public int HitHandle(Annotation item, Point p)
|
|
{
|
|
IReadOnlyList<Point> handles = item.GetHandles();
|
|
double tolerance = HandleSize / 2 + 2 / ViewScale;
|
|
int ghost = -1;
|
|
for (int i = handles.Count - 1; i >= 0; i--)
|
|
{
|
|
if (System.Math.Abs(handles[i].X - p.X) <= tolerance && System.Math.Abs(handles[i].Y - p.Y) <= tolerance)
|
|
{
|
|
if (!item.IsGhostHandle(i))
|
|
{
|
|
return i;
|
|
}
|
|
|
|
ghost = i;
|
|
}
|
|
}
|
|
|
|
return ghost;
|
|
}
|
|
}
|