|
@@ -0,0 +1,119 @@
|
|
|
+using Avalonia;
|
|
|
+using Avalonia.Controls;
|
|
|
+using Avalonia.Controls.Shapes;
|
|
|
+using Avalonia.Input;
|
|
|
+using Avalonia.Markup.Xaml;
|
|
|
+using Avalonia.Media;
|
|
|
+using Avalonia.Media.Imaging;
|
|
|
+using System.Collections.ObjectModel;
|
|
|
+
|
|
|
+namespace InABox.Avalonia.Components;
|
|
|
+
|
|
|
+public partial class InkCanvas : UserControl
|
|
|
+{
|
|
|
+ public static readonly StyledProperty<double> LineThicknessProperty =
|
|
|
+ AvaloniaProperty.Register<InkCanvas, double>(nameof(LineThickness), 3.0);
|
|
|
+
|
|
|
+ public static readonly StyledProperty<Color> LineColourProperty =
|
|
|
+ AvaloniaProperty.Register<InkCanvas, Color>(nameof(LineColour), Colors.Black);
|
|
|
+
|
|
|
+ public double LineThickness
|
|
|
+ {
|
|
|
+ get => GetValue(LineThicknessProperty);
|
|
|
+ set => SetValue(LineThicknessProperty, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ public Color LineColour
|
|
|
+ {
|
|
|
+ get => GetValue(LineColourProperty);
|
|
|
+ set => SetValue(LineColourProperty, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ private ObservableCollection<Polyline> Lines = new();
|
|
|
+
|
|
|
+ private Polyline? CurrentLine;
|
|
|
+
|
|
|
+ public event EventHandler? Changed;
|
|
|
+
|
|
|
+ public InkCanvas()
|
|
|
+ {
|
|
|
+ InitializeComponent();
|
|
|
+
|
|
|
+ Lines.CollectionChanged += Lines_CollectionChanged;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Clear()
|
|
|
+ {
|
|
|
+ Canvas.Children.Clear();
|
|
|
+ Lines.Clear();
|
|
|
+ Changed?.Invoke(this, EventArgs.Empty);
|
|
|
+ }
|
|
|
+
|
|
|
+ public byte[] SaveImage()
|
|
|
+ {
|
|
|
+ var width = (int)Math.Floor(Canvas.Bounds.Width);
|
|
|
+ var height = (int)Math.Floor(Canvas.Bounds.Height);
|
|
|
+
|
|
|
+ var renderBitmap = new RenderTargetBitmap(new PixelSize(width, height));
|
|
|
+ using var context = renderBitmap.CreateDrawingContext();
|
|
|
+ foreach(var line in Lines)
|
|
|
+ {
|
|
|
+ line.Render(context);
|
|
|
+ }
|
|
|
+
|
|
|
+ using var stream = new MemoryStream();
|
|
|
+ renderBitmap.Save(stream);
|
|
|
+ return stream.ToArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Lines_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
|
|
+ {
|
|
|
+ Canvas.Children.Clear();
|
|
|
+ foreach(var line in Lines)
|
|
|
+ {
|
|
|
+ if (!Canvas.Children.Contains(line))
|
|
|
+ {
|
|
|
+ Canvas.Children.Add(line);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Point ConvertToImageCoordinates(Point canvasCoordinates)
|
|
|
+ {
|
|
|
+ return canvasCoordinates;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Canvas_PointerPressed(object? sender, PointerPressedEventArgs e)
|
|
|
+ {
|
|
|
+ var position = ConvertToImageCoordinates(e.GetPosition(Canvas));
|
|
|
+ CurrentLine = new Polyline
|
|
|
+ {
|
|
|
+ Points = new ObservableCollection<Point>(new Point[] { position }),
|
|
|
+ StrokeThickness = LineThickness,
|
|
|
+ Stroke = new SolidColorBrush(LineColour),
|
|
|
+ StrokeJoin = PenLineJoin.Round,
|
|
|
+ StrokeLineCap = PenLineCap.Round
|
|
|
+ };
|
|
|
+ Lines.Add(CurrentLine);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Canvas_PointerMoved(object? sender, PointerEventArgs e)
|
|
|
+ {
|
|
|
+ if(CurrentLine is not null)
|
|
|
+ {
|
|
|
+ var position = ConvertToImageCoordinates(e.GetPosition(Canvas));
|
|
|
+ CurrentLine.Points.Add(position);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Canvas_PointerReleased(object? sender, PointerReleasedEventArgs e)
|
|
|
+ {
|
|
|
+ if(CurrentLine is not null)
|
|
|
+ {
|
|
|
+ var position = ConvertToImageCoordinates(e.GetPosition(Canvas));
|
|
|
+ CurrentLine.Points.Add(position);
|
|
|
+ CurrentLine = null;
|
|
|
+ Changed?.Invoke(this, new());
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|