using AbleClassRoom.Mvvm; using Microsoft.Data.Sqlite; using System; using System.Collections.Generic; using System.Data; using System.Drawing.Imaging; using System.IO; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; namespace AbleClassRoom.AbleControl { enum EventFlag { Zoom, Crop, Pan, None } /// /// TextBookViewer.xaml에 대한 상호 작용 논리 /// /// public partial class TextBookViewer : BindableBaseControl { /// /// 교재 확대 완료 처리 이벤트 대리연산자 /// /// /// public delegate void ZoomedCompleted(object sender, ZoomedCompleteEventArgs args); public event ZoomedCompleted OnZoomedCompleted; /// /// 교재 잘라내기 (이미지 부분 캡쳐) 완료 처리 이벤트 대리연산자 /// /// /// public delegate void CroppedBitmapCompleted(object sender, CroppedBitmapCompleteEventArgs args); public event CroppedBitmapCompleted OnCroppedBitmapCompleted; /// /// 교재의 Nagative 처리여부 /// private bool _isNagative; public bool IsNagative { get => _isNagative; set { SetProperty(ref _isNagative, value); } } System.Drawing.Bitmap CurrentTextBook; /// /// 활성 교재 페이지 설정 /// /// /// private Uri _textBookUri; // = null; // { get; set; } public Uri TextBookUri { get => _textBookUri; set { //_textBookUri = value; Dispatcher.Invoke(new Action(() => { if (SetProperty(ref _textBookUri, value)) { CurrentTextBook = new System.Drawing.Bitmap(_textBookUri.AbsolutePath); inkCanvasImage.ImageSource = _isNagative ? ConvertBitmapToImageSource(Transform(CurrentTextBook)) : ConvertBitmapToImageSource(CurrentTextBook); } })); } } private List _editControlCollection; public List EditControlCollection { get => _editControlCollection; set { SetProperty(ref _editControlCollection, value); } } /// /// 현재 교재의 뷰어(InkCanvas) 객체 /// 강의실에서 제어를 위하여 접근하여 가져 갈 수 있게 처리 /// public InkCanvas RenderingInkCanvas { get => inkCanvas; } Point mouseDownPos; // 교재확대 및 캡쳐 등을 위한 마우스 최초 선택 영역 Point originTransformPos; // 교재확대 후 이동 처리에서 교재의 Transform에 대한 최초 위치값 bool isMouseDown = false; // 교재확대 및 캡쳐 등을 위한 마우스 제어 여부 (클릭 여부) Point TextBookDownPoint = new Point(); Point ScrollViewerDownPoint = new Point(); Point EditControlLocationPos = new Point(); Point EditControlMouseDownPos = new Point(); ImageAttributes imageAttributes = new ImageAttributes(); // 교재의 Nagative 처리를 위한 이미지 속성 ColorMatrix colorMatrix; // 교재의 Nagative 처리를 위한 ColorMatrix 정의 EventFlag eventFlag; public TextBookViewer() { InitializeComponent(); // create the negative color matrix colorMatrix = new ColorMatrix(new float[][] { new float[] {-1, 0, 0, 0, 0}, new float[] {0, -1, 0, 0, 0}, new float[] {0, 0, -1, 0, 0}, new float[] {0, 0, 0, 1, 0}, new float[] {1, 1, 1, 0, 1} }); imageAttributes.SetColorMatrix(colorMatrix); inkCanvas.DefaultDrawingAttributes.FitToCurve = false; eventFlag = EventFlag.None; inkCanvas.MouseLeftButtonDown += TextBookViewer_MarqueeZoomMouseLeftButtonDown; inkCanvas.MouseMove += TextBookViewer_MarqueeZoomMouseMove; inkCanvas.MouseLeftButtonUp += TextBookViewer_MarqueeZoomMouseLeftButtonUp; this.Loaded += TextBookViewer_Loaded; } private void TextBookViewer_Loaded(object sender, RoutedEventArgs e) { var img = inkCanvasImage.ImageSource as BitmapSource; double original_aspect = inkCanvasImage.ImageSource.Width / inkCanvasImage.ImageSource.Height; double bound_aspect = (double)inkCanvas.ActualWidth / inkCanvas.ActualHeight; Size size = new Size { Width = original_aspect > bound_aspect ? inkCanvas.ActualWidth : (int)Math.Round(inkCanvas.ActualHeight * original_aspect), Height = original_aspect > bound_aspect ? (int)Math.Round(inkCanvas.ActualWidth / original_aspect) : inkCanvas.ActualHeight }; var offsetX = (inkCanvas.ActualWidth / 2) - (size.Width / 2); var offsetY = (inkCanvas.ActualHeight / 2) - (size.Height / 2); var scaleWidth = (img.PixelWidth) / (size.Width); var scaleHeight = (img.PixelHeight) / (size.Height); if(EditControlCollection.Count > 0) { foreach(var model in EditControlCollection) { var CONTROL_TYPE = model.CustomControlnfo.CONTROL_TYPE; var WIDTH = model.CustomControlnfo.WIDTH / scaleWidth; var HEIGHT = model.CustomControlnfo.HEIGHT / scaleHeight; var X = (model.CustomControlnfo.X / scaleWidth) + offsetX; var Y = (model.CustomControlnfo.Y / scaleHeight) + offsetY; var SOURCE = model.CustomControlnfo.SOURCE; BindableBaseControl childItem = model.EditControl as BindableBaseControl; switch (CONTROL_TYPE) { case "BLANK": childItem = new AbleControl.BlankBox(); (childItem as AbleControl.BlankBox).DeleteButtonVisibility = Visibility.Hidden; break; case "VIDEO": case "SOUND": case "IMAGE": childItem = new AbleControl.MediaBox(); (childItem as AbleControl.MediaBox).MediaSource = SOURCE; (childItem as AbleControl.MediaBox).MediaType = CONTROL_TYPE; (childItem as AbleControl.MediaBox).DeleteButtonVisibility = Visibility.Hidden; break; } childItem.Width = WIDTH; childItem.Height = HEIGHT; Canvas.SetLeft(childItem, X); Canvas.SetTop(childItem, Y); ItemsCanvas.Children.Add(childItem); } } } private void ClearItemSelected() { foreach (var item in ItemsCanvas.Children) { if (item.GetType().Equals(typeof(AbleControl.BlankBox))) { (item as AbleControl.BlankBox).IsSelected = false; } else if (item.GetType().Equals(typeof(AbleControl.MediaBox))) { (item as AbleControl.MediaBox).IsSelected = false; } } } #region 교재 Nagative 처리 public static ImageSource ConvertBitmapToImageSource(System.Drawing.Bitmap bitmap) { BitmapImage bitmapImage = new BitmapImage(); using (MemoryStream memory = new MemoryStream()) { bitmap.Save(memory, ImageFormat.Jpeg); memory.Position = 0; bitmapImage.BeginInit(); bitmapImage.StreamSource = memory; bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit(); } return bitmapImage; } public System.Drawing.Bitmap Transform(System.Drawing.Bitmap source) { //get a graphics object from the new image System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(source); // create the negative color matrix ColorMatrix colorMatrix = new ColorMatrix(new float[][] { new float[] {-1, 0, 0, 0, 0}, new float[] {0, -1, 0, 0, 0}, new float[] {0, 0, -1, 0, 0}, new float[] {0, 0, 0, 1, 0}, new float[] {1, 1, 1, 0, 1} }); imageAttributes.SetColorMatrix(colorMatrix); g.DrawImage(source, new System.Drawing.Rectangle(0, 0, source.Width, source.Height), 0, 0, source.Width, source.Height, System.Drawing.GraphicsUnit.Pixel, imageAttributes); //dispose the Graphics object g.Dispose(); return source; } #endregion private void ResetEvent() { inkCanvas.RemoveRoutedEventHandlers(InkCanvas.MouseLeftButtonDownEvent); inkCanvas.RemoveRoutedEventHandlers(InkCanvas.MouseMoveEvent); inkCanvas.RemoveRoutedEventHandlers(InkCanvas.MouseLeftButtonUpEvent); } public void Command(LectureCommnad lectureCommnad) { Task.Delay(1); eventFlag = EventFlag.None; switch (lectureCommnad) { case LectureCommnad.PenMode: inkCanvas.EditingMode = InkCanvasEditingMode.Ink; break; case LectureCommnad.MoveMode: inkCanvas.EditingMode = InkCanvasEditingMode.None; eventFlag = scaleTransform.ScaleX > 1.0d ? EventFlag.Pan : EventFlag.None; break; case LectureCommnad.ZoomMode: inkCanvas.EditingMode = InkCanvasEditingMode.None; selectionCanvas.Visibility = Visibility.Visible; eventFlag = EventFlag.Zoom; break; case LectureCommnad.FillMode: FiilAndReset(); break; case LectureCommnad.CroppedBlackBoardMode: eventFlag = EventFlag.Crop; break; case LectureCommnad.Nagative: inkCanvasImage.ImageSource = ConvertBitmapToImageSource(Transform(CurrentTextBook)); break; } } /// /// 교재 확대 취소(1:1 Fill 처리) 및 이벤트 Clear /// private void FiilAndReset() { var scaleAnimation = new DoubleAnimation() { From = scaleTransform.ScaleX, To = 1.0, Duration = TimeSpan.FromMilliseconds(300), FillBehavior = FillBehavior.Stop }; var translateXAnimation = new DoubleAnimation() { From = translateTransform.X, To = 0.0, Duration = TimeSpan.FromMilliseconds(300), FillBehavior = FillBehavior.Stop }; var translateYAnimation = new DoubleAnimation() { From = translateTransform.Y, To = 0.0, Duration = TimeSpan.FromMilliseconds(300), FillBehavior = FillBehavior.Stop }; scaleAnimation.Completed += (s, e) => { scaleTransform.ScaleX = 1.0; scaleTransform.ScaleY = 1.0; scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, null); }; translateYAnimation.Completed += (s, e) => { translateTransform.X = 0.0; translateTransform.Y = 0.0; translateTransform.BeginAnimation(TranslateTransform.XProperty, null); translateTransform.BeginAnimation(TranslateTransform.YProperty, null); }; scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation); scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation); translateTransform.BeginAnimation(TranslateTransform.XProperty, translateXAnimation); translateTransform.BeginAnimation(TranslateTransform.YProperty, translateYAnimation); } #region 교재 확대 처리 이벤트 private void TextBookViewer_MarqueeZoomMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { // Capture and track the mouse. isMouseDown = true; mouseDownPos = e.GetPosition(inkCanvas); inkCanvas.CaptureMouse(); switch (eventFlag) { case EventFlag.Zoom: case EventFlag.Crop: // Initial placement of the drag selection box. Canvas.SetLeft(selectionBox, mouseDownPos.X); Canvas.SetTop(selectionBox, mouseDownPos.Y); selectionBox.Width = 0; selectionBox.Height = 0; // Make the drag selection box visible. selectionBox.Visibility = System.Windows.Visibility.Visible; break; case EventFlag.Pan: originTransformPos = new Point(translateTransform.X, translateTransform.Y); break; } } private void TextBookViewer_MarqueeZoomMouseMove(object sender, MouseEventArgs e) { if (isMouseDown) { Point mousePos = e.GetPosition(inkCanvas); switch (eventFlag) { case EventFlag.Zoom: case EventFlag.Crop: selectionBox.SetValue(Canvas.LeftProperty, Math.Min(mousePos.X, mouseDownPos.X)); selectionBox.SetValue(Canvas.TopProperty, Math.Min(mousePos.Y, mouseDownPos.Y)); selectionBox.Width = Math.Abs(mousePos.X - mouseDownPos.X); selectionBox.Height = Math.Abs(mousePos.Y - mouseDownPos.Y); if (selectionBox.Visibility != Visibility.Visible) selectionBox.Visibility = Visibility.Visible; Rect selectRect = new Rect(Canvas.GetLeft(selectionBox), Canvas.GetTop(selectionBox), selectionBox.Width, selectionBox.Height); Dispatcher.Invoke(() => { Point inkCanvasTop = new Point(0, 0); selectionBoxOutterTop.Rect = new Rect(inkCanvasTop.X, inkCanvasTop.Y, inkCanvas.ActualWidth, selectRect.Top); selectionBoxOutterLeft.Rect = new Rect(inkCanvasTop.X, inkCanvasTop.Y, selectRect.Left, inkCanvas.ActualHeight); selectionBoxOutterBottom.Rect = new Rect(inkCanvasTop.X, selectRect.Bottom, inkCanvas.ActualWidth, inkCanvas.ActualHeight); selectionBoxOutterRight.Rect = new Rect(selectRect.TopRight.X, selectRect.TopRight.Y, inkCanvas.ActualWidth, inkCanvas.ActualHeight); }); break; case EventFlag.Pan: Vector vector = mouseDownPos - mousePos; if(!vector.Equals(new Vector(0, 0))) { translateTransform.X = originTransformPos.X - vector.X; translateTransform.Y = originTransformPos.Y - vector.Y; originTransformPos.X = translateTransform.X; originTransformPos.Y = translateTransform.Y; } break; } } } private void TextBookViewer_MarqueeZoomMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (isMouseDown) { // Release the mouse capture and stop tracking it. isMouseDown = false; inkCanvas.ReleaseMouseCapture(); Rect selectRect = new Rect(Canvas.GetLeft(selectionBox), Canvas.GetTop(selectionBox), selectionBox.Width, selectionBox.Height); //if (sizeX < 100 || sizeY < 100) //{ // return; //} // Hide the drag selection box. selectionBox.Visibility = System.Windows.Visibility.Collapsed; switch(eventFlag) { case EventFlag.Crop: var img = inkCanvasImage.ImageSource as BitmapSource; double original_aspect = inkCanvasImage.ImageSource.Width / inkCanvasImage.ImageSource.Height; double bound_aspect = (double)inkCanvas.ActualWidth / inkCanvas.ActualHeight; Size size = new Size { Width = original_aspect > bound_aspect ? inkCanvas.ActualWidth : (int)Math.Round(inkCanvas.ActualHeight * original_aspect), Height = original_aspect > bound_aspect ? (int)Math.Round(inkCanvas.ActualWidth / original_aspect) : inkCanvas.ActualHeight }; var offsetX = (inkCanvas.ActualWidth / 2) - (size.Width / 2); var offsetY = (inkCanvas.ActualHeight / 2) - (size.Height / 2); var scaleWidth = (img.PixelWidth) / (size.Width); var scaleHeight = (img.PixelHeight) / (size.Height); var rcFrom = new Int32Rect() { X = (int)((selectRect.X - offsetX) * scaleWidth), Y = (int)((selectRect.Y - offsetY) * scaleHeight), Width = (int)(selectRect.Width * scaleWidth), Height = (int)(selectRect.Height * scaleHeight) }; rcFrom.X = rcFrom.X < 0 ? 0 : rcFrom.X; rcFrom.Y = rcFrom.Y < 0 ? 0 : rcFrom.Y; rcFrom.Width = (rcFrom.X + rcFrom.Width) > img.PixelWidth ? img.PixelWidth - rcFrom.X : rcFrom.Width; rcFrom.Height = (rcFrom.Y + rcFrom.Height) > img.PixelHeight ? img.PixelHeight - rcFrom.Y : rcFrom.Height; if (OnCroppedBitmapCompleted != null) { CroppedBitmap croppedBitmap = new CroppedBitmap(img, rcFrom); OnCroppedBitmapCompleted(null, new CroppedBitmapCompleteEventArgs { croppedBitmap = croppedBitmap, SizeX = croppedBitmap.PixelWidth, SizeY = croppedBitmap.PixelHeight, SelectWidth = selectRect.Width * scaleWidth, SelectHeight = selectRect.Height * scaleHeight }); } break; case EventFlag.Zoom: Point center = new Point(selectRect.Left + (selectRect.Width / 2), selectRect.Top + ((selectRect.Height) / 2)); double scaleX = inkCanvas.ActualWidth / selectRect.Width; scaleX = Math.Abs(Math.Truncate(scaleX) - scaleX) > 0.5 ? Math.Truncate(scaleX) + 0.5 : Math.Truncate(scaleX); Point TransPoint = new Point((inkCanvas.ActualWidth / 2) - center.X, (inkCanvas.ActualHeight / 2) - center.Y); double scaleY = Math.Truncate(inkCanvas.ActualHeight / selectRect.Height); scaleY = Math.Abs(Math.Truncate(scaleY) - scaleY) > 0.5 ? Math.Truncate(scaleY) + 0.5 : Math.Truncate(scaleY); double scale = Math.Min(scaleX, scaleY); scale = scale > 5.0d ? 5.0d : scale; double absoluteX = center.X * scaleTransform.ScaleX + translateTransform.X; double absoluteY = center.Y * scaleTransform.ScaleY + translateTransform.Y; var scaleAnimation = new DoubleAnimation() { From = 1, To = scale, Duration = TimeSpan.FromMilliseconds(300), FillBehavior = FillBehavior.Stop }; scaleAnimation.Completed += (s, e) => { scaleTransform.ScaleX = scale; scaleTransform.ScaleY = scale; scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, null); }; var translateXAnimation = new DoubleAnimation() { From = translateTransform.X, To = (absoluteX - center.X * scale) + TransPoint.X, Duration = TimeSpan.FromMilliseconds(300), FillBehavior = FillBehavior.Stop //HandoffBehavior.SnapshotAndReplace }; translateXAnimation.Completed += (s, e) => { translateTransform.X = (absoluteX - center.X * scale) + TransPoint.X; translateTransform.BeginAnimation(TranslateTransform.XProperty, null); if (OnZoomedCompleted != null) { OnZoomedCompleted(null, new ZoomedCompleteEventArgs { Completed = true }); } }; var translateYAnimation = new DoubleAnimation() { From = translateTransform.Y, To = (absoluteY - center.Y * scale) + TransPoint.Y, Duration = TimeSpan.FromMilliseconds(300), FillBehavior = FillBehavior.Stop }; translateYAnimation.Completed += (s, e) => { translateTransform.Y = (absoluteY - center.Y * scale) + TransPoint.Y; translateTransform.BeginAnimation(TranslateTransform.YProperty, null); if (OnZoomedCompleted != null) { OnZoomedCompleted(null, new ZoomedCompleteEventArgs { Completed = true }); } }; scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation); scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation); translateTransform.BeginAnimation(TranslateTransform.XProperty, translateXAnimation); translateTransform.BeginAnimation(TranslateTransform.YProperty, translateYAnimation); eventFlag = EventFlag.Pan; //ResetEvent(); // //inkCanvas.MouseLeftButtonDown += TextBookViewer_PanMouseLeftButtonDown; //inkCanvas.MouseMove += TextBookViewer_PanMouseMove; //inkCanvas.MouseLeftButtonUp += TextBookViewer_PanMouseLeftButtonUp; break; case EventFlag.Pan: //inkCanvas.ReleaseMouseCapture(); break; } } } #endregion } }