microsoft / Win2D

Win2D is an easy-to-use Windows Runtime API for immediate mode 2D graphics rendering with GPU acceleration. It is available to C#, C++ and VB developers writing apps for the Windows Universal Platform (UWP). It utilizes the power of Direct2D, and integrates seamlessly with XAML and CoreWindow.
http://microsoft.github.io/Win2D
Other
1.82k stars 286 forks source link

CanvasDrawingSession.DrawInk in WinUI3 #855

Open arcadiogarcia opened 3 years ago

arcadiogarcia commented 3 years ago

The DrawInk method shows up in the WinUI3 docs (https://microsoft.github.io/Win2D/WinUI3/html/Overload_Microsoft_Graphics_Canvas_CanvasDrawingSession_DrawInk.htm) but it is missing in the actual package.

Is this an error in the docs or is this a work in progress? Is there a rough timeline for when this functionality will show up?

duncanmacmichael commented 2 years ago

Hi @arcadiogarcia, good question. This type depends on Windows.UI.Input.Inking and there is no corresponding WinUI 3 type yet. We should update our docs to reflect this, so I've opened an internal work item for us to do so. In the meantime, I'd recommend checking the WinUI 3 docs and WinUI repo for the latest updates on inking support in WinUI 3.

pfresnay commented 9 months ago

Hi @duncanmacmichael it seems that InkStroke is now part of WinUI3. Any update to Win2D to support DrawInk?

zkvii commented 4 months ago

You can achieve a similar effect through offscreen rendering. Below is a similar example. I think it should be easy to customize the pen stroke style.

<?xml version="1.0" encoding="utf-8"?>

<UserControl
    x:Class="FluentPdf.Views.D2DCanvas"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:xaml="using:Microsoft.Graphics.Canvas.UI.Xaml"
    xmlns:converters="using:FluentPdf.Converters"
    mc:Ignorable="d">
    <UserControl.Resources>
        <converters:IsDrawingConverter x:Key="IsDrawingConverter" />
    </UserControl.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="200" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{x:Bind ViewModel.IsDrawing,Mode=OneWay,Converter={StaticResource IsDrawingConverter}}"  ></TextBlock>
            </StackPanel>
        </Grid>
        <xaml:CanvasControl Grid.Row="1" x:Name="DCanvas" CreateResources="DCreateResources" Draw="DDraw"
                            ClearColor="CornFlowerBlue" />
    </Grid>
</UserControl>
using System;
using System.Collections.ObjectModel;
using Microsoft.Graphics.Canvas.UI;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
using System.Threading.Tasks;
using Microsoft.Graphics.Canvas;
using Microsoft.UI;
using System.ComponentModel;
using System.Runtime.CompilerServices;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace FluentPdf.Views
{
    public class DCanvasPoint(float x, float y, float pressure)
    {
        public float X { get; set; } = x;
        public float Y { get; set; } = y;

        public float Pressure = pressure;
    }

    public class DCanvasLine
    {
        public ObservableCollection<DCanvasPoint> LinePoints = new();
        public DCanvasPoint StartPoint { get; set; }
        public DCanvasPoint EndPoint { get; set; }

        public int PointsCount => LinePoints.Count;

        public void AddPoint(DCanvasPoint point)
        {
            if (PointsCount == 0)
            {
                StartPoint = point;
            }

            LinePoints.Add(point);
            EndPoint = point;
        }
    }

    public class D2DCanvasViewModel : INotifyPropertyChanged
    {
        private bool _isDrawing;

        private bool _isDrawingReady = false;

        // public CanvasBitmap DBitmap;
        public CanvasRenderTarget InkPresenter;

        public void D2DCanvasViewModelInit(int width, int height)
        {
            InkPresenter = new CanvasRenderTarget(CanvasDevice.GetSharedDevice(), width, height, 144);
        }

        public ObservableCollection<DCanvasLine> DLines { get; set; } = new();
        public DCanvasLine CurrentDLine;

        public void AddToLines()
        {
            DLines.Add(CurrentDLine);
            CurrentDLine = null;
        }

        public void AddPointToCurrentDLine(DCanvasPoint point)
        {
            CurrentDLine ??= new DCanvasLine();
            CurrentDLine.AddPoint(point);
        }

        public bool IsDrawingReady
        {
            get => _isDrawingReady;
            set
            {
                if (_isDrawingReady == value) return;
                _isDrawingReady = value;
                OnPropertyChanged();
            }
        }

        public bool IsDrawing
        {
            get => _isDrawing;
            set
            {
                if (_isDrawing == value) return;
                _isDrawing = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public sealed partial class D2DCanvas
    {
        // private CanvasSolidColorBrush _redBrush;

        public D2DCanvasViewModel ViewModel;

        public D2DCanvas()
        {
            this.InitializeComponent();
            ViewModel = new D2DCanvasViewModel();
            Loaded += D2DCanvas_Loaded;
            Unloaded += D2DCanvas_Unloaded;
            SizeChanged += D2DCanvas_SizeChanged;
            // DCanvas.FocusEngaged += DCanvas_FocusEngaged;
            // DCanvas.FocusDisengaged += DCanvas_FocusDisengaged;

            DCanvas.PointerPressed += DCanvas_PointerPressed;
            DCanvas.PointerMoved += DCanvas_PointerMoved;
            DCanvas.PointerReleased += DCanvas_PointerReleased;
            DCanvas.PointerExited += DCanvas_PointerExited;
        }

        private void DCanvas_PointerExited(object sender, PointerRoutedEventArgs e)
        {
            ViewModel.IsDrawing = false;
        }

        private void DCanvas_PointerReleased(object sender, PointerRoutedEventArgs e)
        {
            if (ViewModel.IsDrawing)
            {
                ViewModel.AddToLines();
            }

            ViewModel.IsDrawing = false;
        }

        private void DCanvas_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            if (!ViewModel.IsDrawing) return;

            var point = e.GetCurrentPoint(DCanvas);
            var pressure = point.Properties.Pressure;

            if (!(pressure > 0)) return;
            // ViewModel.Points.Add(new CanvasPoint(point.Position._x, point.Position._y, pressure));
            var previousPoint = ViewModel.CurrentDLine?.EndPoint;
            ViewModel.AddPointToCurrentDLine(new DCanvasPoint(point.Position._x, point.Position._y, pressure));

            DCanvas.Invalidate();
            using var ds = ViewModel.InkPresenter.CreateDrawingSession();
            if (previousPoint != null)
                ds.DrawLine(previousPoint.X, previousPoint.Y, point.Position._x, point.Position._y, Colors.Black,
                    pressure);
            // DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, DCanvas.Invalidate);
            // Draw something
        }

        private void DCanvas_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            ViewModel.IsDrawing = true;
        }

        private void D2DCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            DCanvas.Invalidate();
        }

        private void D2DCanvas_Loaded(object sender, RoutedEventArgs e)
        {
            ViewModel.D2DCanvasViewModelInit((int)DCanvas.ActualWidth, (int)DCanvas.ActualHeight);
        }

        private void D2DCanvas_Unloaded(object sender, RoutedEventArgs e)
        {
            DCanvas.RemoveFromVisualTree();
            DCanvas = null;
        }

        private void DCreateResources(CanvasControl sender, CanvasCreateResourcesEventArgs args)
        {
            args.TrackAsyncAction(DCreateResourcesAsync(sender).AsAsyncAction());
        }

        Task DCreateResourcesAsync(CanvasControl sender)
        {
            return Task.CompletedTask;
            // Load bitmaps, create brushes, etc.
            // ViewModel.DBitmap = await CanvasBitmap.LoadAsync(sender, "Assets/sample.png");
        }

        private void DDraw(CanvasControl sender, CanvasDrawEventArgs args)
        {
            using var ds = args.DrawingSession;
            // Draw something
            // ds.DrawImage(ViewModel.DBitmap);

            ds.DrawImage(ViewModel.InkPresenter);
        }
    }
}
adstep commented 1 month ago

Also hitting this issue. Would be great to see support for Ink now that it's supported in WinUI3.

adstep commented 1 month ago

The DrawInk methods seem gated by an ifdef for WINUI3_SUPPORTS_INKING.

I can see there's a flag to set FunctionLevelLWINUI3_SUPPORTS_INKING to true in the release profile Win2d.cpp.props..

What else do we need to do to get this enabled @duncanmacmichael?