-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathInkLayer.cs
271 lines (218 loc) · 8.47 KB
/
InkLayer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace NDI_Telestrator
{
public class InkLayer : System.Windows.Controls.InkCanvas
{
#region Property notifications
protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public enum HistoryDataType
{
ClearBoard,
AddStroke,
DeleteStroke
}
public struct HistoryData
{
public HistoryDataType type;
public object dataA;
public object dataB;
}
public Stack<HistoryData> backHistory = new Stack<HistoryData>();
public Queue<HistoryData> forwardHistory = new Queue<HistoryData>();
/// <summary>
/// Use this function when adding a new item to the back history.
/// EXCEPT for Undo / Redo operations
/// </summary>
/// <param name="evt">History Data</param>
private void pushBackHistory(HistoryData evt)
{
// TODO: Check if working wtih stroke move / copy / drag
backHistory.Push(evt);
forwardHistory.Clear();
_notifyUpdate();
}
public event EventHandler LayerUpdated;
// The stylus/touch ink doesn't get captured via the RenderTargetBitmap
// function so we'll add it to a Stroke that we add it in
StylusPointCollection stylusStrokeBuffer = null;
public BitmapFrame Bitmap
{
get
{
return Draw(Brushes.White);
}
}
private void _notifyUpdate()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Bitmap"));
LayerUpdated?.Invoke(this, null);
}
public InkLayer(Canvas parent) : base()
{
Background = System.Windows.Media.Brushes.Transparent;
UseCustomCursor = true;
Width = parent.Width;
Height = parent.Height;
parent.SizeChanged += (a, b) =>
{
Width = b.NewSize.Width;
Height = b.NewSize.Height;
_notifyUpdate();
};
}
// Generate a bitmap of the individual layer
public BitmapFrame Draw(Brush background = null)
{
if (double.IsNaN(Width)) return null;
//if (!IsLoaded) return null;
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
if (background != null) drawingContext.DrawRectangle(background, null, new Rect(0, 0, (int)Width, (int)Height));
Strokes.Draw(drawingContext);
drawingContext.Close();
RenderTargetBitmap rtb = new RenderTargetBitmap((int)Width, (int)Height, 96d, 96d, PixelFormats.Default);
rtb.Render(drawingVisual);
return BitmapFrame.Create(rtb);
}
}
public void Undo()
{
if (backHistory.Count > 0)
{
HistoryData evt = backHistory.Pop();
switch (evt.type)
{
case HistoryDataType.AddStroke:
Strokes.Remove((Stroke)evt.dataA); //Strokes.RemoveAt(Strokes.Count - 1);
break;
case HistoryDataType.ClearBoard:
evt.dataB = Strokes;
Strokes = (StrokeCollection)evt.dataA;
break;
}
forwardHistory.Enqueue(evt);
_notifyUpdate();
}
}
public void Redo()
{
if (forwardHistory.Count > 0)
{
HistoryData evt = forwardHistory.Dequeue();
switch (evt.type)
{
case HistoryDataType.AddStroke:
Strokes.Add((Stroke)evt.dataA);
break;
case HistoryDataType.ClearBoard:
evt.dataA = Strokes;
Strokes = (StrokeCollection)evt.dataB;
break;
}
backHistory.Push(evt);
_notifyUpdate();
}
}
public void Clear()
{
pushBackHistory(new HistoryData { type = HistoryDataType.ClearBoard, dataA = Strokes });
Strokes = new StrokeCollection();
_notifyUpdate();
}
private void _handleStrokeCollection(Stroke stroke)
{
pushBackHistory(new HistoryData { type = HistoryDataType.AddStroke, dataA = stroke });
}
protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
{
// Do nothing
// Don't let anyone use this uwu
// base.OnStrokeCollected(e);
}
protected override void OnPreviewStylusDown(StylusDownEventArgs e)
{
Strokes.Add(new Stroke(stylusStrokeBuffer = e.StylusDevice.GetStylusPoints(this), DefaultDrawingAttributes));
// Handling here slows the rendering
// But not handling adds a 1-pixel (or so) stroke
// EDIT: If PreviewStylusUp is handled, the 1-pixel stroke is not added
// b.Handled = true;
base.OnPreviewStylusDown(e);
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
// Cancel mouse down event if the stylus was used (Prevents single pixel stroke)
if (stylusStrokeBuffer != null)
{
e.Handled = true;
return;
}
base.OnPreviewMouseDown(e);
}
//protected override void OnPreviewStylusUp(StylusEventArgs e)
//{
// // Clear the buffer when the stylus is lifted
// // // stylusStrokeBuffer = null;
// // // Manually trigger events
// // // RaiseEvent(new InkCanvasStrokeCollectedEventArgs(Strokes[Strokes.Count - 1]) { RoutedEvent = InkCanvas.StrokeCollectedEvent });
// // // Blocking this event stops the 1-pixel stroke from being added
// // // But currently breaks mouse use (need to release the stylus / mouse?)
// // // EDIT: For now we'll just remove it I guess..
// // // e.Handled = true;
// base.OnPreviewStylusUp(e);
//}
// The 1-pixel stroke gets added somewhere between PreviewStylusUp and StylusUp
private bool wasStrokeCaptured = false;
protected override void OnStylusUp(StylusEventArgs e)
{
if (stylusStrokeBuffer != null)
{
// Remove the last stroke (1)
stylusStrokeBuffer = null;
Strokes.RemoveAt(Strokes.Count - 1);
wasStrokeCaptured = true;
// Using OnMouseLeftButtonUp instead now
// OnStrokeCollected(new InkCanvasStrokeCollectedEventArgs(Strokes[Strokes.Count - 1]) { RoutedEvent = InkCanvas.StrokeCollectedEvent });
}
base.OnStylusUp(e);
}
protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
{
if (!wasStrokeCaptured) wasStrokeCaptured = IsMouseCaptured;
base.OnPreviewMouseUp(e);
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (wasStrokeCaptured) _handleStrokeCollection(Strokes[Strokes.Count - 1]);
base.OnMouseLeftButtonUp(e);
}
protected override void OnPreviewStylusMove(StylusEventArgs e)
{
if (stylusStrokeBuffer == null)
{
base.OnPreviewStylusMove(e);
return;
}
// Add points to the buffer
stylusStrokeBuffer.Add(e.StylusDevice.GetStylusPoints(this));
// Blocks events that would populate the 1-pixel stroke
e.Handled = true;
}
}
}