|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Drawing.Imaging;
|
|
|
using System.IO;
|
|
|
using System.Linq;
|
|
|
using System.Runtime.InteropServices;
|
|
|
using System.Threading;
|
|
|
using System.Threading.Tasks;
|
|
|
using System.Windows;
|
|
|
using System.Windows.Controls;
|
|
|
using System.Windows.Data;
|
|
|
using System.Windows.Ink;
|
|
|
using System.Windows.Input;
|
|
|
using System.Windows.Interop;
|
|
|
using System.Windows.Media;
|
|
|
using System.Windows.Media.Animation;
|
|
|
using System.Windows.Media.Imaging;
|
|
|
using System.Windows.Shapes;
|
|
|
using System.Windows.Threading;
|
|
|
using Brush = System.Windows.Media.Brush;
|
|
|
using Color = System.Windows.Media.Color;
|
|
|
using Point = System.Windows.Point;
|
|
|
|
|
|
namespace WpfSketch
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// Window1.xaml 的交互逻辑
|
|
|
/// </summary>
|
|
|
public partial class Window1 : Window
|
|
|
{
|
|
|
public delegate void ImageSaveEventHandler(BitmapSource image);
|
|
|
public ImageSaveEventHandler ImageSaveEvent;
|
|
|
private ColorPicker DefaultColorPicker;
|
|
|
public string ImageFile { get; set; }
|
|
|
/// <summary>
|
|
|
/// 目标图片
|
|
|
/// </summary>
|
|
|
public BitmapSource DestImage { get; set; } = null;
|
|
|
private static Mutex mutex = new Mutex(true, "alldream-WpfSketch");
|
|
|
public Window1()
|
|
|
{
|
|
|
_history = new Stack<StrokesHistoryNode>();
|
|
|
_redoHistory = new Stack<StrokesHistoryNode>();
|
|
|
|
|
|
InitializeComponent();
|
|
|
//ImageFile = "C:\\temp\\out-m.png";
|
|
|
DefaultColorPicker = new ColorPicker();
|
|
|
//SetColor(DefaultColorPicker);
|
|
|
_selectedColor = DefaultColorPicker;
|
|
|
|
|
|
SetEnable(false, _mode);
|
|
|
|
|
|
SetTopMost(true);
|
|
|
|
|
|
SetBrushSize(_brushSizes[_brushIndex]);
|
|
|
|
|
|
//ExtraToolPanel.Opacity = 0;
|
|
|
btnFontIncrease.Visibility = Visibility.Hidden;
|
|
|
btnFontDecrease.Visibility = Visibility.Hidden;
|
|
|
|
|
|
MainInkCanvas.Strokes.StrokesChanged += StrokesChanged;
|
|
|
|
|
|
//MainInkCanvas.AddHandler(InkCanvas.MouseLeftButtonDownEvent, new MouseButtonEventHandler(MainInkCanvas_MouseLeftButtonDown), true);
|
|
|
//MainInkCanvas.MouseLeftButtonDown += MainInkCanvas_MouseLeftButtonDown;
|
|
|
MainInkCanvas.PreviewMouseLeftButtonDown += MainInkCanvas_MouseLeftButtonDown;
|
|
|
MainInkCanvas.MouseLeftButtonUp += MainInkCanvas_MouseLeftButtonUp;
|
|
|
MainInkCanvas.MouseRightButtonUp += MainInkCanvas_MouseRightButtonUp;
|
|
|
MainInkCanvas.MouseMove += MainInkCanvas_MouseMove;
|
|
|
|
|
|
_drawerTextBox.IsHitTestVisible = true;
|
|
|
_drawerTextBox.FontSize = 24.0;
|
|
|
//_drawerTextBox.Background = Application.Current.Resources["TrueTransparent"] as Brush;
|
|
|
_drawerTextBox.Background = Brushes.Transparent;//
|
|
|
_drawerTextBox.AcceptsReturn = true;
|
|
|
_drawerTextBox.TextWrapping = TextWrapping.Wrap;
|
|
|
_drawerTextBox.LostFocus += _drawerTextBox_LostFocus;
|
|
|
}
|
|
|
public Window1(BitmapSource destImage)
|
|
|
: this()
|
|
|
{
|
|
|
this.DestImage = destImage;
|
|
|
}
|
|
|
#region /---------Setter---------/
|
|
|
|
|
|
private ColorPicker _selectedColor;
|
|
|
private bool _inkVisibility = true;
|
|
|
private bool _displayExtraToolPanel = false;
|
|
|
private bool _enable = false;
|
|
|
private readonly int[] _brushSizes = { 4, 6, 8, 10, 14 };
|
|
|
private int _brushIndex = 0;
|
|
|
private bool _displayOrientation;
|
|
|
private DrawMode _mode = DrawMode.Pen;
|
|
|
|
|
|
private void SetEnable(bool enable, DrawMode mode)
|
|
|
{
|
|
|
_enable = enable;
|
|
|
_mode = mode;
|
|
|
|
|
|
InkCanvasEditingMode editingMode = InkCanvasEditingMode.Ink;
|
|
|
bool bUseCustomCursor = true;
|
|
|
|
|
|
switch (_mode)
|
|
|
{
|
|
|
case DrawMode.Select:
|
|
|
bUseCustomCursor = false;
|
|
|
editingMode = InkCanvasEditingMode.Select;
|
|
|
break;
|
|
|
case DrawMode.Pen:
|
|
|
editingMode = InkCanvasEditingMode.Ink;
|
|
|
break;
|
|
|
case DrawMode.Text:
|
|
|
editingMode = InkCanvasEditingMode.None;
|
|
|
break;
|
|
|
case DrawMode.Line:
|
|
|
editingMode = InkCanvasEditingMode.None;
|
|
|
break;
|
|
|
case DrawMode.Arrow:
|
|
|
editingMode = InkCanvasEditingMode.None;
|
|
|
break;
|
|
|
case DrawMode.Rectangle:
|
|
|
editingMode = InkCanvasEditingMode.None;
|
|
|
break;
|
|
|
case DrawMode.Circle:
|
|
|
editingMode = InkCanvasEditingMode.None;
|
|
|
break;
|
|
|
case DrawMode.Ray:
|
|
|
editingMode = InkCanvasEditingMode.None;
|
|
|
break;
|
|
|
case DrawMode.Erase:
|
|
|
bUseCustomCursor = false;
|
|
|
editingMode = InkCanvasEditingMode.EraseByStroke;
|
|
|
break;
|
|
|
case DrawMode.None:
|
|
|
_mode = DrawMode.None;
|
|
|
break;
|
|
|
default:
|
|
|
_mode = DrawMode.Select;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (_mode == DrawMode.Ray)
|
|
|
{
|
|
|
MainInkCanvas.Cursor = new Cursor(new MemoryStream(WpfSketch.Properties.Resources.激光));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
MainInkCanvas.Cursor = Cursors.Cross;
|
|
|
}
|
|
|
|
|
|
if (_mode == DrawMode.Text)
|
|
|
{
|
|
|
btnFontIncrease.Visibility = Visibility.Visible;
|
|
|
btnFontDecrease.Visibility = Visibility.Visible;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
btnFontIncrease.Visibility = Visibility.Hidden;
|
|
|
btnFontDecrease.Visibility = Visibility.Hidden;
|
|
|
}
|
|
|
|
|
|
MainInkCanvas.UseCustomCursor = bUseCustomCursor;
|
|
|
MainInkCanvas.EditingMode = editingMode;
|
|
|
if (!_enable || _mode == DrawMode.None)
|
|
|
{
|
|
|
MainInkCanvas.EditingMode = InkCanvasEditingMode.None;
|
|
|
}
|
|
|
btnNone.IsChecked = !enable;
|
|
|
//if(_mode == DrawMode.None)
|
|
|
//{
|
|
|
// btnNone.IsChecked = true;
|
|
|
//}
|
|
|
// Background = Application.Current.Resources[enable ? "FakeTransparent" : "TrueTransparent"] as Brush;
|
|
|
}
|
|
|
//private void SetColor(ColorPicker b)
|
|
|
//{
|
|
|
// if (ReferenceEquals(_selectedColor, b)) return;
|
|
|
// var solidColorBrush = b.Foreground as SolidColorBrush;
|
|
|
// if (solidColorBrush == null)
|
|
|
// return;
|
|
|
|
|
|
// MainInkCanvas.DefaultDrawingAttributes.Color = solidColorBrush.Color;
|
|
|
|
|
|
// b.IsActived = true;
|
|
|
// if (_selectedColor != null)
|
|
|
// _selectedColor.IsActived = false;
|
|
|
// _selectedColor = b;
|
|
|
|
|
|
// _drawerTextBox.Foreground = solidColorBrush;
|
|
|
//}
|
|
|
private void SetForeColor(Color color)
|
|
|
{
|
|
|
if(_selectedColor == null)
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
var solidColorBrush = _selectedColor.Foreground as SolidColorBrush;
|
|
|
|
|
|
solidColorBrush.Color = color;
|
|
|
MainInkCanvas.DefaultDrawingAttributes.FitToCurve = true;
|
|
|
MainInkCanvas.DefaultDrawingAttributes.IgnorePressure = false;
|
|
|
MainInkCanvas.DefaultDrawingAttributes.IsHighlighter = false;
|
|
|
MainInkCanvas.DefaultDrawingAttributes.StylusTip = StylusTip.Ellipse;
|
|
|
|
|
|
MainInkCanvas.DefaultDrawingAttributes.Color = solidColorBrush.Color;
|
|
|
|
|
|
_drawerTextBox.Foreground = solidColorBrush;
|
|
|
}
|
|
|
private void SetBrushSize(double s)
|
|
|
{
|
|
|
MainInkCanvas.DefaultDrawingAttributes.Height = s;
|
|
|
MainInkCanvas.DefaultDrawingAttributes.Width = s;
|
|
|
}
|
|
|
private void SetOrientation(bool v)
|
|
|
{
|
|
|
//PaletteRotate.BeginAnimation(RotateTransform.AngleProperty, new DoubleAnimation(v ? -90 : 0, Duration4));
|
|
|
//Palette.BeginAnimation(MinWidthProperty, new DoubleAnimation(v ? 90 : 0, Duration7));
|
|
|
_displayOrientation = v;
|
|
|
}
|
|
|
private void SetTopMost(bool v)
|
|
|
{
|
|
|
//PinButton.IsActived = v;
|
|
|
Topmost = v;
|
|
|
}
|
|
|
#endregion
|
|
|
#region Shape Drawer
|
|
|
private Point _drawerIntPos;
|
|
|
private bool _drawerIsMove = false;
|
|
|
private Stroke _drawerLastStroke;
|
|
|
private TextBox _drawerTextBox = new TextBox();
|
|
|
|
|
|
private void _drawerTextBox_LostFocus(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
String text = _drawerTextBox.Text;
|
|
|
|
|
|
if (text.Length > 0)
|
|
|
{
|
|
|
var textBlock = new TextBlock();
|
|
|
//textBlock.TextWrapping = TextWrapping.NoWrap;
|
|
|
textBlock.Text = text;
|
|
|
|
|
|
MainInkCanvas.Children.Add(textBlock);
|
|
|
|
|
|
textBlock.Visibility = Visibility.Visible;
|
|
|
textBlock.Foreground = _drawerTextBox.Foreground;
|
|
|
textBlock.FontSize = _drawerTextBox.FontSize;
|
|
|
textBlock.TextWrapping = _drawerTextBox.TextWrapping;
|
|
|
|
|
|
InkCanvas.SetLeft(textBlock, InkCanvas.GetLeft(_drawerTextBox));
|
|
|
InkCanvas.SetTop(textBlock, InkCanvas.GetTop(_drawerTextBox));
|
|
|
|
|
|
textBlock.Width = _drawerTextBox.Width;
|
|
|
textBlock.Height = _drawerTextBox.Height;
|
|
|
}
|
|
|
|
|
|
MainInkCanvas.Children.Remove(_drawerTextBox);
|
|
|
|
|
|
//throw new NotImplementedException();
|
|
|
}
|
|
|
private void MainInkCanvas_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
|
|
|
{
|
|
|
SetEnable(false, _mode);
|
|
|
}
|
|
|
private void MainInkCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
|
|
{
|
|
|
if (_enable == false || _mode == DrawMode.Select || _mode == DrawMode.Pen || _mode == DrawMode.None || e.LeftButton != MouseButtonState.Pressed)
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
if (MainInkCanvas.Children.Contains(_drawerTextBox))
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
_ignoreStrokesChange = true;
|
|
|
_drawerIsMove = true;
|
|
|
_drawerIntPos = e.GetPosition(MainInkCanvas);
|
|
|
_drawerLastStroke = null;
|
|
|
|
|
|
if (_mode == DrawMode.Text)
|
|
|
{
|
|
|
_drawerTextBox.Text = "";
|
|
|
|
|
|
if (MainInkCanvas.Children.Contains(_drawerTextBox) == false)
|
|
|
MainInkCanvas.Children.Add(_drawerTextBox);
|
|
|
|
|
|
_drawerTextBox.Visibility = Visibility.Visible;
|
|
|
InkCanvas.SetLeft(_drawerTextBox, _drawerIntPos.X);
|
|
|
InkCanvas.SetTop(_drawerTextBox, _drawerIntPos.Y);
|
|
|
_drawerTextBox.Width = 0;
|
|
|
_drawerTextBox.Height = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void MainInkCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
|
|
{
|
|
|
if (_drawerIsMove == true)
|
|
|
{
|
|
|
Point endP = e.GetPosition(MainInkCanvas);
|
|
|
|
|
|
if (_drawerLastStroke != null && _mode != DrawMode.Ray && _mode != DrawMode.Text)
|
|
|
{
|
|
|
StrokeCollection collection = new StrokeCollection();
|
|
|
collection.Add(_drawerLastStroke);
|
|
|
Push(_history, new StrokesHistoryNode(collection, StrokesHistoryNodeType.Added));
|
|
|
}
|
|
|
|
|
|
if (_drawerLastStroke != null && (_mode == DrawMode.Ray || _mode == DrawMode.Text))
|
|
|
{
|
|
|
//us animation?
|
|
|
/*
|
|
|
var ani = new DoubleAnimation(1, 1, Duration4);
|
|
|
ani.Completed += (obj,arg)=> { MainInkCanvas.Strokes.Remove(_drawerLastStroke); };
|
|
|
MainInkCanvas.BeginAnimation(OpacityProperty, ani);
|
|
|
*/
|
|
|
MainInkCanvas.Strokes.Remove(_drawerLastStroke);
|
|
|
}
|
|
|
|
|
|
if (_mode == DrawMode.Text)
|
|
|
{
|
|
|
// resize drawer text box
|
|
|
_drawerTextBox.Width = Math.Abs(endP.X - _drawerIntPos.X);
|
|
|
_drawerTextBox.Height = Math.Abs(endP.Y - _drawerIntPos.Y);
|
|
|
|
|
|
if (_drawerTextBox.Width <= 100 || _drawerTextBox.Height <= 40)
|
|
|
{
|
|
|
_drawerTextBox.Width = 100;
|
|
|
_drawerTextBox.Height = 40;
|
|
|
}
|
|
|
|
|
|
InkCanvas.SetLeft(_drawerTextBox, Math.Min(_drawerIntPos.X, endP.X));
|
|
|
InkCanvas.SetTop(_drawerTextBox, Math.Min(_drawerIntPos.Y, endP.Y));
|
|
|
|
|
|
_drawerTextBox.Focus();
|
|
|
}
|
|
|
|
|
|
_drawerIsMove = false;
|
|
|
_ignoreStrokesChange = false;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (_mode != DrawMode.Select)
|
|
|
{
|
|
|
if (_mode == DrawMode.None)
|
|
|
{
|
|
|
SetEnable(false, _mode);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
DrawMode mode = _mode;
|
|
|
SetEnable(true, DrawMode.None);
|
|
|
SetEnable(true, mode);
|
|
|
}
|
|
|
}
|
|
|
//else
|
|
|
//{
|
|
|
// //SetEnable(true, _mode);
|
|
|
// //_enable = true;
|
|
|
//}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void MainInkCanvas_MouseMove(object sender, MouseEventArgs e)
|
|
|
{
|
|
|
if (_drawerIsMove == false)
|
|
|
return;
|
|
|
|
|
|
DrawingAttributes drawingAttributes = MainInkCanvas.DefaultDrawingAttributes.Clone();
|
|
|
Stroke stroke = null;
|
|
|
|
|
|
drawingAttributes.StylusTip = StylusTip.Ellipse;
|
|
|
drawingAttributes.IgnorePressure = true;
|
|
|
|
|
|
Point endP = e.GetPosition(MainInkCanvas);
|
|
|
|
|
|
if (_mode == DrawMode.Text)
|
|
|
{
|
|
|
List<Point> pointList = new List<Point>
|
|
|
{
|
|
|
new Point(_drawerIntPos.X, _drawerIntPos.Y),
|
|
|
new Point(_drawerIntPos.X, endP.Y),
|
|
|
new Point(endP.X, endP.Y),
|
|
|
new Point(endP.X, _drawerIntPos.Y),
|
|
|
new Point(_drawerIntPos.X, _drawerIntPos.Y),
|
|
|
};
|
|
|
|
|
|
drawingAttributes.Width = 2;
|
|
|
drawingAttributes.Height = 2;
|
|
|
drawingAttributes.FitToCurve = false;//must be false,other wise rectangle can not be drawed correct
|
|
|
|
|
|
StylusPointCollection point = new StylusPointCollection(pointList);
|
|
|
stroke = new Stroke(point)
|
|
|
{
|
|
|
DrawingAttributes = drawingAttributes,
|
|
|
};
|
|
|
}
|
|
|
else if (_mode == DrawMode.Ray)
|
|
|
{
|
|
|
//high lighter is ray line
|
|
|
drawingAttributes.IsHighlighter = true;
|
|
|
|
|
|
List<Point> pointList = new List<Point>
|
|
|
{
|
|
|
new Point(_drawerIntPos.X, _drawerIntPos.Y),
|
|
|
new Point(endP.X, endP.Y),
|
|
|
};
|
|
|
|
|
|
StylusPointCollection point = new StylusPointCollection(pointList);
|
|
|
stroke = new Stroke(point)
|
|
|
{
|
|
|
DrawingAttributes = drawingAttributes,
|
|
|
};
|
|
|
}
|
|
|
else if (_mode == DrawMode.Line)
|
|
|
{
|
|
|
List<Point> pointList = new List<Point>
|
|
|
{
|
|
|
new Point(_drawerIntPos.X, _drawerIntPos.Y),
|
|
|
new Point(endP.X, endP.Y),
|
|
|
};
|
|
|
|
|
|
StylusPointCollection point = new StylusPointCollection(pointList);
|
|
|
stroke = new Stroke(point)
|
|
|
{
|
|
|
DrawingAttributes = drawingAttributes,
|
|
|
};
|
|
|
}
|
|
|
else if (_mode == DrawMode.Arrow)
|
|
|
{
|
|
|
double w = 15, h = 15;
|
|
|
double theta = Math.Atan2(_drawerIntPos.Y - endP.Y, _drawerIntPos.X - endP.X);
|
|
|
double sint = Math.Sin(theta);
|
|
|
double cost = Math.Cos(theta);
|
|
|
|
|
|
List<Point> pointList = new List<Point>
|
|
|
{
|
|
|
new Point(_drawerIntPos.X, _drawerIntPos.Y),
|
|
|
new Point(endP.X , endP.Y),
|
|
|
new Point(endP.X + (w*cost - h*sint), endP.Y + (w*sint + h*cost)),
|
|
|
new Point(endP.X,endP.Y),
|
|
|
new Point(endP.X + (w*cost + h*sint), endP.Y - (h*cost - w*sint)),
|
|
|
};
|
|
|
|
|
|
StylusPointCollection point = new StylusPointCollection(pointList);
|
|
|
|
|
|
drawingAttributes.FitToCurve = false;//must be false,other wise rectangle can not be drawed correct
|
|
|
|
|
|
stroke = new Stroke(point)
|
|
|
{
|
|
|
DrawingAttributes = drawingAttributes,
|
|
|
};
|
|
|
}
|
|
|
else if (_mode == DrawMode.Rectangle)
|
|
|
{
|
|
|
List<Point> pointList = new List<Point>
|
|
|
{
|
|
|
new Point(_drawerIntPos.X, _drawerIntPos.Y),
|
|
|
new Point(_drawerIntPos.X, endP.Y),
|
|
|
new Point(endP.X, endP.Y),
|
|
|
new Point(endP.X, _drawerIntPos.Y),
|
|
|
new Point(_drawerIntPos.X, _drawerIntPos.Y),
|
|
|
};
|
|
|
|
|
|
drawingAttributes.FitToCurve = false;//must be false,other wise rectangle can not be drawed correct
|
|
|
|
|
|
StylusPointCollection point = new StylusPointCollection(pointList);
|
|
|
stroke = new Stroke(point)
|
|
|
{
|
|
|
DrawingAttributes = drawingAttributes,
|
|
|
};
|
|
|
}
|
|
|
else if (_mode == DrawMode.Circle)
|
|
|
{
|
|
|
List<Point> pointList = GenerateEclipseGeometry(_drawerIntPos, endP);
|
|
|
StylusPointCollection point = new StylusPointCollection(pointList);
|
|
|
stroke = new Stroke(point)
|
|
|
{
|
|
|
DrawingAttributes = drawingAttributes
|
|
|
};
|
|
|
}
|
|
|
|
|
|
if (_drawerLastStroke != null)
|
|
|
MainInkCanvas.Strokes.Remove(_drawerLastStroke);
|
|
|
|
|
|
if (stroke != null)
|
|
|
MainInkCanvas.Strokes.Add(stroke);
|
|
|
|
|
|
_drawerLastStroke = stroke;
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
#region /---------Generator---------/
|
|
|
private static string GenerateFileName(string fileExt = ".fdw")
|
|
|
{
|
|
|
return DateTime.Now.ToString("yyyyMMdd-HHmmss") + fileExt;
|
|
|
}
|
|
|
|
|
|
private List<Point> GenerateEclipseGeometry(Point st, Point ed)
|
|
|
{
|
|
|
double a = 0.5 * (ed.X - st.X);
|
|
|
double b = 0.5 * (ed.Y - st.Y);
|
|
|
List<Point> pointList = new List<Point>();
|
|
|
for (double r = 0; r <= 2 * Math.PI; r = r + 0.01)
|
|
|
{
|
|
|
pointList.Add(new Point(0.5 * (st.X + ed.X) + a * Math.Cos(r), 0.5 * (st.Y + ed.Y) + b * Math.Sin(r)));
|
|
|
}
|
|
|
return pointList;
|
|
|
}
|
|
|
#endregion
|
|
|
#region /---------IO---------/
|
|
|
private StrokeCollection _preLoadStrokes = null;
|
|
|
private void QuickSave(string filename = "QuickSave_")
|
|
|
{
|
|
|
//Save(new FileStream("Save\\" + filename + GenerateFileName(), FileMode.OpenOrCreate));
|
|
|
if(this.DestImage != null)
|
|
|
{
|
|
|
Save2Image();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
Save(null);
|
|
|
}
|
|
|
}
|
|
|
private void Save2Image()
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
//string strFile = "C:\\temp\\rend.png";
|
|
|
double width = MainInkCanvas.ActualWidth;
|
|
|
double height = MainInkCanvas.ActualHeight;
|
|
|
RenderTargetBitmap bmpCopied = new RenderTargetBitmap((int)Math.Round(width)
|
|
|
, (int)Math.Round(height), 96, 96, PixelFormats.Default);
|
|
|
DrawingVisual dv = new DrawingVisual();
|
|
|
using (DrawingContext dc = dv.RenderOpen())
|
|
|
{
|
|
|
VisualBrush vb = new VisualBrush(MainInkCanvas);
|
|
|
dc.DrawImage(this.img.Source, new Rect(new System.Windows.Point(), new System.Windows.Size(width, height)));
|
|
|
dc.DrawRectangle(vb, null, new Rect(new System.Windows.Point(), new System.Windows.Size(width, height)));
|
|
|
}
|
|
|
bmpCopied.Render(dv);
|
|
|
this.DestImage = bmpCopied;
|
|
|
_saved = true;
|
|
|
ImageSaveEvent?.Invoke(this.DestImage);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
MessageBox.Show(ex.Message);
|
|
|
}
|
|
|
}
|
|
|
private void Save(Stream fs)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
//string strFile = "C:\\temp\\rend.png";
|
|
|
double width = MainInkCanvas.ActualWidth;
|
|
|
double height = MainInkCanvas.ActualHeight;
|
|
|
RenderTargetBitmap bmpCopied = new RenderTargetBitmap((int)Math.Round(width)
|
|
|
, (int)Math.Round(height), 96, 96, PixelFormats.Default);
|
|
|
DrawingVisual dv = new DrawingVisual();
|
|
|
using (DrawingContext dc = dv.RenderOpen())
|
|
|
{
|
|
|
VisualBrush vb = new VisualBrush(MainInkCanvas);
|
|
|
dc.DrawImage(this.img.Source, new Rect(new System.Windows.Point(), new System.Windows.Size(width, height)));
|
|
|
dc.DrawRectangle(vb, null, new Rect(new System.Windows.Point(), new System.Windows.Size(width, height)));
|
|
|
}
|
|
|
bmpCopied.Render(dv);
|
|
|
using (FileStream file = new FileStream(ImageFile, FileMode.Create, FileAccess.Write))
|
|
|
{
|
|
|
PngBitmapEncoder encoder = new PngBitmapEncoder();
|
|
|
encoder.Frames.Add(BitmapFrame.Create(bmpCopied));
|
|
|
encoder.Save(file);
|
|
|
_saved = true;
|
|
|
}
|
|
|
}
|
|
|
catch(Exception ex)
|
|
|
{
|
|
|
MessageBox.Show(ex.Message);
|
|
|
}
|
|
|
//try
|
|
|
//{
|
|
|
// if (fs == Stream.Null) return;
|
|
|
// MainInkCanvas.Strokes.Save(fs);
|
|
|
// _saved = true;
|
|
|
// Display("画板保存成功");
|
|
|
// fs.Close();
|
|
|
//}
|
|
|
//catch (Exception ex)
|
|
|
//{
|
|
|
// MessageBox.Show(ex.ToString());
|
|
|
// Display("画板保存失败");
|
|
|
//}
|
|
|
}
|
|
|
private StrokeCollection Load(Stream fs)
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
return new StrokeCollection(fs);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
MessageBox.Show(ex.ToString());
|
|
|
Display("画板加载失败");
|
|
|
}
|
|
|
return new StrokeCollection();
|
|
|
}
|
|
|
private void AnimatedReload(StrokeCollection sc)
|
|
|
{
|
|
|
_preLoadStrokes = sc;
|
|
|
//var ani = new DoubleAnimation(0, Duration3);
|
|
|
//ani.Completed += LoadAniCompleted;
|
|
|
//MainInkCanvas.BeginAnimation(OpacityProperty, ani);
|
|
|
}
|
|
|
private void LoadAniCompleted(object sender, EventArgs e)
|
|
|
{
|
|
|
if (_preLoadStrokes == null) return;
|
|
|
MainInkCanvas.Strokes = _preLoadStrokes;
|
|
|
//Display("画板加载完成");
|
|
|
_saved = true;
|
|
|
ClearHistory();
|
|
|
//MainInkCanvas.BeginAnimation(OpacityProperty, new DoubleAnimation(1, Duration3));
|
|
|
}
|
|
|
|
|
|
private static string[] GetSavePathList()
|
|
|
{
|
|
|
return Directory.GetFiles("Save", "*.fdw");
|
|
|
}
|
|
|
private static string GetFileNameFromPath(string path)
|
|
|
{
|
|
|
return System.IO.Path.GetFileName(path);
|
|
|
}
|
|
|
#endregion
|
|
|
#region /---------Helper---------/
|
|
|
private string _staticInfo = "";
|
|
|
private bool _displayingInfo;
|
|
|
|
|
|
private async void Display(string info)
|
|
|
{
|
|
|
//InfoBox.Text = info;
|
|
|
_displayingInfo = true;
|
|
|
//await InfoDisplayTimeUp(new Progress<string>(box => InfoBox.Text = box));
|
|
|
}
|
|
|
private Task InfoDisplayTimeUp(IProgress<string> box)
|
|
|
{
|
|
|
return Task.Run(() =>
|
|
|
{
|
|
|
Task.Delay(2000).Wait();
|
|
|
box.Report(_staticInfo);
|
|
|
_displayingInfo = false;
|
|
|
});
|
|
|
}
|
|
|
private void SetStaticInfo(string info)
|
|
|
{
|
|
|
_staticInfo = info;
|
|
|
//if (!_displayingInfo)
|
|
|
// InfoBox.Text = _staticInfo;
|
|
|
}
|
|
|
|
|
|
private static Stream SaveDialog(string initFileName, string fileExt = ".fdw", string filter = "Free Draw Save (*.fdw)|*fdw")
|
|
|
{
|
|
|
var dialog = new Microsoft.Win32.SaveFileDialog()
|
|
|
{
|
|
|
DefaultExt = fileExt,
|
|
|
Filter = filter,
|
|
|
FileName = initFileName,
|
|
|
InitialDirectory = Directory.GetCurrentDirectory() + "Save"
|
|
|
};
|
|
|
return dialog.ShowDialog() == true ? dialog.OpenFile() : Stream.Null;
|
|
|
}
|
|
|
private static Stream OpenDialog(string fileExt = ".fdw", string filter = "Free Draw Save (*.fdw)|*fdw")
|
|
|
{
|
|
|
var dialog = new Microsoft.Win32.OpenFileDialog()
|
|
|
{
|
|
|
DefaultExt = fileExt,
|
|
|
Filter = filter,
|
|
|
};
|
|
|
return dialog.ShowDialog() == true ? dialog.OpenFile() : Stream.Null;
|
|
|
}
|
|
|
#endregion
|
|
|
#region /---------Judge--------/
|
|
|
|
|
|
private bool _saved = false;
|
|
|
|
|
|
private bool IsUnsaved()
|
|
|
{
|
|
|
return MainInkCanvas.Strokes.Count != 0 && !_saved;
|
|
|
}
|
|
|
|
|
|
private bool PromptToSave()
|
|
|
{
|
|
|
if (!IsUnsaved())
|
|
|
return true;
|
|
|
var r = MessageBox.Show("You have unsaved work, do you want to save it now?", "Unsaved data",
|
|
|
MessageBoxButton.YesNoCancel);
|
|
|
if (r == MessageBoxResult.Yes || r == MessageBoxResult.OK)
|
|
|
{
|
|
|
QuickSave();
|
|
|
return true;
|
|
|
}
|
|
|
if (r == MessageBoxResult.No || r == MessageBoxResult.None)
|
|
|
return true;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
private void Window_Loaded(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
FixupToolBarOverflowArrow(tbDraw);
|
|
|
if(this.DestImage != null)
|
|
|
{
|
|
|
MainInkCanvas.Width = this.DestImage.Width;
|
|
|
MainInkCanvas.Height = this.DestImage.Height;
|
|
|
|
|
|
img.Source = this.DestImage;
|
|
|
}
|
|
|
else if (!string.IsNullOrEmpty(ImageFile))
|
|
|
{
|
|
|
MemoryStream ms = null;
|
|
|
using (var stream = File.Open(ImageFile, FileMode.Open))
|
|
|
{
|
|
|
using (var reader = new BinaryReader(stream))
|
|
|
{
|
|
|
byte[] datas = reader.ReadBytes((int)stream.Length);
|
|
|
ms = new MemoryStream(datas);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
BitmapImage imgSource = new BitmapImage();
|
|
|
imgSource.BeginInit();
|
|
|
imgSource.StreamSource = ms;
|
|
|
imgSource.EndInit();
|
|
|
|
|
|
MainInkCanvas.Width = imgSource.Width;
|
|
|
MainInkCanvas.Height = imgSource.Height;
|
|
|
|
|
|
img.Source = imgSource;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
MainInkCanvas.Width = 500;
|
|
|
MainInkCanvas.Height = 400;
|
|
|
}
|
|
|
|
|
|
// 默认红色画笔
|
|
|
this.btnColorRed.IsChecked = true;
|
|
|
this.btnColorRed.RaiseEvent(new RoutedEventArgs(RadioButton.ClickEvent));
|
|
|
this.rbtnPenWidthMin.IsChecked = true;
|
|
|
|
|
|
// 默认绘图圆
|
|
|
this.btnCircle.IsChecked = true;
|
|
|
this.btnCircle.RaiseEvent(new RoutedEventArgs(RadioButton.ClickEvent));
|
|
|
}
|
|
|
|
|
|
private static void FixupToolBarOverflowArrow(ToolBar toolBar)
|
|
|
{
|
|
|
Action fixup = () =>
|
|
|
{
|
|
|
var overflowButton = toolBar.Template.FindName("OverflowButton", toolBar) as System.Windows.Controls.Primitives.ButtonBase;
|
|
|
if (overflowButton != null)
|
|
|
{
|
|
|
overflowButton.SetBinding(
|
|
|
VisibilityProperty,
|
|
|
new Binding("IsEnabled")
|
|
|
{
|
|
|
RelativeSource = RelativeSource.Self,
|
|
|
Converter = new BooleanToVisibilityConverter()
|
|
|
});
|
|
|
}
|
|
|
};
|
|
|
|
|
|
if (toolBar.IsLoaded)
|
|
|
{
|
|
|
fixup();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
RoutedEventHandler handler = null;
|
|
|
handler = (sender, e) =>
|
|
|
{
|
|
|
fixup();
|
|
|
toolBar.Loaded -= handler;
|
|
|
};
|
|
|
|
|
|
toolBar.Loaded += handler;
|
|
|
}
|
|
|
}
|
|
|
/// <summary>
|
|
|
/// 选择颜色.
|
|
|
/// </summary>
|
|
|
/// <param name="sender">The sender.</param>
|
|
|
/// <param name="e">The e.</param>
|
|
|
private void ColorButton_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
//要获得所选方块的颜色,需要将SolidColorBrush取Color
|
|
|
//_selectedColor.Foreground = (SolidColorBrush)((Rectangle)(sender as RadioButton).Content).Fill;
|
|
|
Color color = ((SolidColorBrush)((Rectangle)(sender as RadioButton).Content).Fill).Color;
|
|
|
this.SetForeColor(color);
|
|
|
}
|
|
|
#region /---------Ink---------/
|
|
|
private readonly Stack<StrokesHistoryNode> _history;
|
|
|
private readonly Stack<StrokesHistoryNode> _redoHistory;
|
|
|
private bool _ignoreStrokesChange;
|
|
|
|
|
|
private void Undo()
|
|
|
{
|
|
|
if (!CanUndo()) return;
|
|
|
var last = Pop(_history);
|
|
|
_ignoreStrokesChange = true;
|
|
|
if (last.Type == StrokesHistoryNodeType.Added)
|
|
|
MainInkCanvas.Strokes.Remove(last.Strokes);
|
|
|
else
|
|
|
MainInkCanvas.Strokes.Add(last.Strokes);
|
|
|
_ignoreStrokesChange = false;
|
|
|
Push(_redoHistory, last);
|
|
|
}
|
|
|
private void Redo()
|
|
|
{
|
|
|
if (!CanRedo()) return;
|
|
|
var last = Pop(_redoHistory);
|
|
|
_ignoreStrokesChange = true;
|
|
|
if (last.Type == StrokesHistoryNodeType.Removed)
|
|
|
MainInkCanvas.Strokes.Remove(last.Strokes);
|
|
|
else
|
|
|
MainInkCanvas.Strokes.Add(last.Strokes);
|
|
|
_ignoreStrokesChange = false;
|
|
|
Push(_history, last);
|
|
|
}
|
|
|
|
|
|
private static void Push(Stack<StrokesHistoryNode> collection, StrokesHistoryNode node)
|
|
|
{
|
|
|
collection.Push(node);
|
|
|
}
|
|
|
private static StrokesHistoryNode Pop(Stack<StrokesHistoryNode> collection)
|
|
|
{
|
|
|
return collection.Count == 0 ? null : collection.Pop();
|
|
|
}
|
|
|
private bool CanUndo()
|
|
|
{
|
|
|
return _history.Count != 0;
|
|
|
}
|
|
|
private bool CanRedo()
|
|
|
{
|
|
|
return _redoHistory.Count != 0;
|
|
|
}
|
|
|
private void StrokesChanged(object sender, StrokeCollectionChangedEventArgs e)
|
|
|
{
|
|
|
if (_ignoreStrokesChange) return;
|
|
|
_saved = false;
|
|
|
if (e.Added.Count != 0)
|
|
|
Push(_history, new StrokesHistoryNode(e.Added, StrokesHistoryNodeType.Added));
|
|
|
if (e.Removed.Count != 0)
|
|
|
Push(_history, new StrokesHistoryNode(e.Removed, StrokesHistoryNodeType.Removed));
|
|
|
ClearHistory(_redoHistory);
|
|
|
}
|
|
|
|
|
|
private void ClearHistory()
|
|
|
{
|
|
|
ClearHistory(_history);
|
|
|
ClearHistory(_redoHistory);
|
|
|
}
|
|
|
private static void ClearHistory(Stack<StrokesHistoryNode> collection)
|
|
|
{
|
|
|
collection?.Clear();
|
|
|
}
|
|
|
private void Clear()
|
|
|
{
|
|
|
MainInkCanvas.Children.Clear();
|
|
|
MainInkCanvas.Strokes.Clear();
|
|
|
ClearHistory();
|
|
|
}
|
|
|
|
|
|
private void AnimatedClear()
|
|
|
{
|
|
|
//no need any more
|
|
|
//var ani = new DoubleAnimation(0, Duration3);
|
|
|
//ani.Completed += ClearAniComplete; ;
|
|
|
//MainInkCanvas.BeginAnimation(OpacityProperty, ani);
|
|
|
}
|
|
|
private void ClearAniComplete(object sender, EventArgs e)
|
|
|
{
|
|
|
Clear();
|
|
|
//Display("画板清除完成");
|
|
|
//MainInkCanvas.BeginAnimation(OpacityProperty, new DoubleAnimation(1, Duration3));
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
/// <summary>
|
|
|
/// 绘制圆.
|
|
|
/// </summary>
|
|
|
/// <param name="sender">The source of the event.</param>
|
|
|
/// <param name="e">The <see cref="RoutedEventArgs"/> instance containing the event data.</param>
|
|
|
private void BtnCircle_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
SetEnable(true, DrawMode.Circle);
|
|
|
}
|
|
|
/// <summary>
|
|
|
/// 任意画笔.
|
|
|
/// </summary>
|
|
|
/// <param name="sender">The source of the event.</param>
|
|
|
/// <param name="e">The <see cref="RoutedEventArgs"/> instance containing the event data.</param>
|
|
|
private void BtnPen_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
SetEnable(true, DrawMode.Pen);
|
|
|
}
|
|
|
/// <summary>
|
|
|
/// 绘制直线
|
|
|
/// </summary>
|
|
|
/// <param name="sender"></param>
|
|
|
/// <param name="e"></param>
|
|
|
private void BtnLine_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
SetEnable(true, DrawMode.Line);
|
|
|
}
|
|
|
|
|
|
private void BtnRectangle_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
SetEnable(true, DrawMode.Rectangle);
|
|
|
}
|
|
|
|
|
|
private void BtnText_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
SetEnable(true, DrawMode.Text);
|
|
|
}
|
|
|
|
|
|
private void BtnSelect_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
SetEnable(true, DrawMode.Select);
|
|
|
}
|
|
|
private void BtnArrow_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
SetEnable(true, DrawMode.Arrow);
|
|
|
}
|
|
|
private void BtnNone_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
SetEnable(false, DrawMode.None);
|
|
|
}
|
|
|
|
|
|
private void BtnFontIncrease_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
_drawerTextBox.FontSize = _drawerTextBox.FontSize + 2;
|
|
|
_drawerTextBox.FontSize = Math.Min(60, _drawerTextBox.FontSize);
|
|
|
}
|
|
|
|
|
|
private void BtnFontDecrease_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
_drawerTextBox.FontSize = _drawerTextBox.FontSize - 2;
|
|
|
|
|
|
_drawerTextBox.FontSize = Math.Max(14, _drawerTextBox.FontSize);
|
|
|
}
|
|
|
/// <summary>
|
|
|
/// 撤销.
|
|
|
/// </summary>
|
|
|
/// <param name="sender">The source of the event.</param>
|
|
|
/// <param name="e">The <see cref="RoutedEventArgs"/> instance containing the event data.</param>
|
|
|
private void BtnUndo_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
Undo();
|
|
|
}
|
|
|
/// <summary>
|
|
|
/// 重做.
|
|
|
/// </summary>
|
|
|
/// <param name="sender">The source of the event.</param>
|
|
|
/// <param name="e">The <see cref="RoutedEventArgs"/> instance containing the event data.</param>
|
|
|
private void BtnRedo_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
Redo();
|
|
|
}
|
|
|
|
|
|
private void PenWidth_Checked(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
double dWidth = ((Ellipse)(sender as RadioButton).Content).Width;
|
|
|
SetBrushSize(dWidth);
|
|
|
}
|
|
|
/// <summary>
|
|
|
/// 保存.
|
|
|
/// </summary>
|
|
|
/// <param name="sender">The source of the event.</param>
|
|
|
/// <param name="e">The <see cref="RoutedEventArgs"/> instance containing the event data.</param>
|
|
|
private void BtnSave_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
if (MainInkCanvas.Strokes.Count == 0)
|
|
|
{
|
|
|
Display("没有笔迹要保存");
|
|
|
return;
|
|
|
}
|
|
|
QuickSave();
|
|
|
|
|
|
this.Close();
|
|
|
}
|
|
|
/// <summary>
|
|
|
/// 退出.
|
|
|
/// </summary>
|
|
|
/// <param name="sender">The source of the event.</param>
|
|
|
/// <param name="e">The <see cref="RoutedEventArgs"/> instance containing the event data.</param>
|
|
|
private void BtnExit_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
this.Close();
|
|
|
}
|
|
|
|
|
|
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
|
|
{
|
|
|
if (IsUnsaved())
|
|
|
{
|
|
|
if (MessageBox.Show(this, "尚未保存,是否退出?", "退出"
|
|
|
, MessageBoxButton.YesNo, MessageBoxImage.Question
|
|
|
, MessageBoxResult.No) == MessageBoxResult.Yes)
|
|
|
{
|
|
|
//Application.Current.Shutdown();
|
|
|
//MessageBox.Show("退出");
|
|
|
return;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
e.Cancel = true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|