1616using Brush = System . Windows . Media . Brush ;
1717using Brushes = System . Windows . Media . Brushes ;
1818using Button = System . Windows . Controls . Button ;
19- using Cursors = System . Windows . Input . Cursors ;
19+ using Color = System . Windows . Media . Color ;
2020using Control = System . Windows . Controls . Control ;
2121using Cursors = System . Windows . Input . Cursors ;
2222using KeyEventArgs = System . Windows . Input . KeyEventArgs ;
2323using MouseEventArgs = System . Windows . Input . MouseEventArgs ;
2424using Path = System . Windows . Shapes . Path ;
2525using Point = System . Windows . Point ;
26- using Brush = System . Windows . Media . Brush ;
26+ using RadioButton = System . Windows . Controls . RadioButton ;
2727using Rectangle = System . Windows . Shapes . Rectangle ;
2828using SaveFileDialog = Microsoft . Win32 . SaveFileDialog ;
2929using TextBox = System . Windows . Controls . TextBox ;
@@ -68,6 +68,7 @@ public enum ScreenCutMouseType
6868 DrawArrow ,
6969 DrawText ,
7070 DrawInk ,
71+ DrawMosaic
7172 }
7273 [ TemplatePart ( Name = CanvasTemplateName , Type = typeof ( Canvas ) ) ]
7374 [ TemplatePart ( Name = LeftRectangleTemplateName , Type = typeof ( Rectangle ) ) ]
@@ -83,6 +84,7 @@ public enum ScreenCutMouseType
8384 [ TemplatePart ( Name = EllipseRadioButtonTemplateName , Type = typeof ( RadioButton ) ) ]
8485 [ TemplatePart ( Name = ArrowRadioButtonTemplateName , Type = typeof ( RadioButton ) ) ]
8586 [ TemplatePart ( Name = InkRadioButtonTemplateName , Type = typeof ( RadioButton ) ) ]
87+ [ TemplatePart ( Name = MosaicRadioButtonTemplateName , Type = typeof ( RadioButton ) ) ]
8688 [ TemplatePart ( Name = TextRadioButtonTemplateName , Type = typeof ( RadioButton ) ) ]
8789 [ TemplatePart ( Name = PopupTemplateName , Type = typeof ( Popup ) ) ]
8890 [ TemplatePart ( Name = BorderPopupTemplateName , Type = typeof ( Border ) ) ]
@@ -104,6 +106,7 @@ public class ScreenCut : Window, IDisposable
104106 private const string EllipseRadioButtonTemplateName = "PART_EllipseRadioButton" ;
105107 private const string ArrowRadioButtonTemplateName = "PART_ArrowRadioButton" ;
106108 private const string InkRadioButtonTemplateName = "PART_InkRadioButton" ;
109+ private const string MosaicRadioButtonTemplateName = "PART_MosaicRadioButton" ;
107110 private const string TextRadioButtonTemplateName = "PART_TextRadioButton" ;
108111 private const string PopupTemplateName = "PART_Popup" ;
109112 private const string BorderPopupTemplateName = "PART_BorderPopup" ;
@@ -126,6 +129,7 @@ public class ScreenCut : Window, IDisposable
126129 _radioButtonEllipse ,
127130 _arrowRadioButton ,
128131 _inkRadioButton ,
132+ _mosaicRadioButton ,
129133 _textRadioButton ;
130134
131135 private Rectangle _leftRectangle , _topRectangle , _rightRectangle , _bottomRectangle ;
@@ -192,6 +196,11 @@ public class ScreenCut : Window, IDisposable
192196 public static int CaptureScreenID = - 1 ;
193197 private Bitmap _screenCapture ;
194198 private ScreenDPI _screenDPI ;
199+ private RenderTargetBitmap _imageSnapshot ;
200+ private Path _currentStrokeContainer = null ;
201+ private List < Rectangle > _currentStrokeRectangles = new List < Rectangle > ( ) ;
202+ private Stack < UIElement > _strokeHistory = new Stack < UIElement > ( ) ;
203+
195204 public ScreenCut ( int index )
196205 {
197206 _screenIndex = index ;
@@ -251,6 +260,9 @@ public override void OnApplyTemplate()
251260 _inkRadioButton = GetTemplateChild ( InkRadioButtonTemplateName ) as RadioButton ;
252261 if ( _inkRadioButton != null )
253262 _inkRadioButton . Click += RadioButtonInk_Click ;
263+ _mosaicRadioButton = GetTemplateChild ( MosaicRadioButtonTemplateName ) as RadioButton ;
264+ if ( _mosaicRadioButton != null )
265+ _mosaicRadioButton . Click += RadioButtonMosaic_Click ;
254266 _textRadioButton = GetTemplateChild ( TextRadioButtonTemplateName ) as RadioButton ;
255267 if ( _textRadioButton != null )
256268 _textRadioButton . Click += RadioButtonText_Click ;
@@ -277,6 +289,7 @@ protected override void OnClosed(EventArgs e)
277289 private void ScreenCut_Loaded ( object sender , RoutedEventArgs e )
278290 {
279291 _canvas . Background = new ImageBrush ( ImagingHelper . CreateBitmapSourceFromBitmap ( CopyScreen ( ) ) ) ;
292+ TakeSnapshot ( ) ;
280293 }
281294
282295 private ScreenDPI GetScreenDPI ( int screenIndex )
@@ -318,6 +331,11 @@ private void RadioButtonInk_Click(object sender, RoutedEventArgs e)
318331 RadioButtonChecked ( _inkRadioButton , ScreenCutMouseType . DrawInk ) ;
319332 }
320333
334+ private void RadioButtonMosaic_Click ( object sender , RoutedEventArgs e )
335+ {
336+ RadioButtonChecked ( _mosaicRadioButton , ScreenCutMouseType . DrawMosaic ) ;
337+ }
338+
321339 private void RadioButtonText_Click ( object sender , RoutedEventArgs e )
322340 {
323341 RadioButtonChecked ( _textRadioButton , ScreenCutMouseType . DrawText ) ;
@@ -355,6 +373,11 @@ private void RadioButtonChecked(RadioButton radioButton, ScreenCutMouseType scre
355373 _border . Cursor = Cursors . Arrow ;
356374 if ( _popup . PlacementTarget != null && _popup . IsOpen )
357375 _popup . IsOpen = false ;
376+ if ( screenCutMouseTypeRadio != ScreenCutMouseType . DrawMosaic )
377+ {
378+ _popup . PlacementTarget = radioButton ;
379+ _popup . IsOpen = true ;
380+ }
358381 DisposeControl ( ) ;
359382 }
360383 else
@@ -474,6 +497,11 @@ protected override void OnPreviewKeyDown(KeyEventArgs e)
474497 }
475498 else if ( e . KeyStates == Keyboard . GetKeyStates ( Key . Z ) && Keyboard . Modifiers == ModifierKeys . Control )
476499 {
500+ if ( _screenCutMouseType == ScreenCutMouseType . DrawMosaic )
501+ {
502+ UndoLastStroke ( ) ;
503+ return ;
504+ }
477505 if ( _canvas . Children . Count > 0 )
478506 _canvas . Children . Remove ( _canvas . Children [ _canvas . Children . Count - 1 ] ) ;
479507
@@ -504,6 +532,10 @@ protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
504532 _editBar . Visibility = Visibility . Hidden ;
505533 _pointEnd = _pointStart ;
506534 _rect = new Rect ( _pointStart . Value , _pointEnd . Value ) ;
535+ if ( _screenCutMouseType == ScreenCutMouseType . DrawMosaic )
536+ {
537+ _currentStrokeRectangles . Clear ( ) ;
538+ }
507539 }
508540 else
509541 {
@@ -639,6 +671,183 @@ protected override void OnPreviewMouseMove(MouseEventArgs e)
639671 case ScreenCutMouseType . DrawInk :
640672 DrwaInkControl ( current ) ;
641673 break ;
674+ case ScreenCutMouseType . DrawMosaic :
675+ if ( ( current - _pointStart . Value ) . Length < 10 )
676+ return ;
677+ _pointStart = current ;
678+ DrawMosaicBlock ( current , 10 , 20 ) ;
679+ break ;
680+ }
681+ }
682+ }
683+
684+ private void TakeSnapshot ( )
685+ {
686+ _canvas . Measure ( new System . Windows . Size ( _canvas . ActualWidth , _canvas . ActualHeight ) ) ;
687+ _canvas . Arrange ( new Rect ( 0 , 0 , _canvas . ActualWidth , _canvas . ActualHeight ) ) ;
688+
689+ _imageSnapshot = new RenderTargetBitmap (
690+ ( int ) _canvas . ActualWidth ,
691+ ( int ) _canvas . ActualHeight ,
692+ 96 , 96 , PixelFormats . Pbgra32 ) ;
693+
694+ _imageSnapshot . Render ( _canvas ) ;
695+ }
696+
697+ private void DrawMosaicBlock ( Point center , int blockSize , int brushSize )
698+ {
699+ if ( _imageSnapshot == null ) return ;
700+
701+ int mosaicSize = blockSize ;
702+ int blocksPerRow = brushSize / mosaicSize ;
703+
704+ for ( int i = 0 ; i < blocksPerRow ; i ++ )
705+ {
706+ for ( int j = 0 ; j < blocksPerRow ; j ++ )
707+ {
708+ double x = center . X - brushSize / 2 + i * mosaicSize ;
709+ double y = center . Y - brushSize / 2 + j * mosaicSize ;
710+
711+ Point blockCenter = new Point ( x + mosaicSize / 2 , y + mosaicSize / 2 ) ;
712+ Color color = GetAreaAverageColor ( blockCenter , mosaicSize ) ;
713+
714+ var block = new Rectangle
715+ {
716+ Width = mosaicSize ,
717+ Height = mosaicSize ,
718+ Fill = new SolidColorBrush ( color ) ,
719+ IsHitTestVisible = false
720+ } ;
721+
722+ Canvas . SetLeft ( block , x ) ;
723+ Canvas . SetTop ( block , y ) ;
724+
725+ _canvas . Children . Add ( block ) ;
726+
727+ _currentStrokeRectangles . Add ( block ) ;
728+ }
729+ }
730+ }
731+
732+ private void CompleteCurrentStroke ( )
733+ {
734+ if ( _currentStrokeRectangles . Count == 0 ) return ;
735+ RemoveTemporaryRectangles ( ) ;
736+ CreateStrokeContainer ( ) ;
737+ _canvas . Children . Add ( _currentStrokeContainer ) ;
738+ _strokeHistory . Push ( _currentStrokeContainer ) ;
739+ _currentStrokeContainer = null ;
740+ _currentStrokeRectangles . Clear ( ) ;
741+ }
742+
743+ private void CreateStrokeContainer ( )
744+ {
745+ if ( _currentStrokeRectangles . Count == 0 ) return ;
746+
747+ double minX = double . MaxValue ;
748+ double minY = double . MaxValue ;
749+ double maxX = double . MinValue ;
750+ double maxY = double . MinValue ;
751+
752+ foreach ( var rect in _currentStrokeRectangles )
753+ {
754+ double x = Canvas . GetLeft ( rect ) ;
755+ double y = Canvas . GetTop ( rect ) ;
756+
757+ minX = Math . Min ( minX , x ) ;
758+ minY = Math . Min ( minY , y ) ;
759+ maxX = Math . Max ( maxX , x + rect . Width ) ;
760+ maxY = Math . Max ( maxY , y + rect . Height ) ;
761+ }
762+
763+ double width = maxX - minX ;
764+ double height = maxY - minY ;
765+
766+ var roundedRect = CreateRoundedRectangleGeometry ( width , height ) ;
767+
768+ var container = new Path
769+ {
770+ Data = roundedRect ,
771+ IsHitTestVisible = false ,
772+ Fill = CreateMosaicVisualBrush ( minX , minY , width , height )
773+ } ;
774+
775+ Canvas . SetLeft ( container , minX ) ;
776+ Canvas . SetTop ( container , minY ) ;
777+
778+ _currentStrokeContainer = container ;
779+ }
780+
781+ private Geometry CreateRoundedRectangleGeometry ( double width , double height )
782+ {
783+ bool isVertical = height > width * 1.5 ;
784+ double cornerRadius = isVertical ? Math . Min ( width / 2 , 30 ) : Math . Min ( height / 2 , 30 ) ;
785+ return new RectangleGeometry ( new Rect ( 0 , 0 , width , height ) , cornerRadius , cornerRadius ) ;
786+ }
787+
788+ private Brush CreateMosaicVisualBrush ( double left , double top , double width , double height )
789+ {
790+ var drawingVisual = new DrawingVisual ( ) ;
791+
792+ using ( var context = drawingVisual . RenderOpen ( ) )
793+ {
794+ foreach ( var rect in _currentStrokeRectangles )
795+ {
796+ double relativeX = Canvas . GetLeft ( rect ) - left ;
797+ double relativeY = Canvas . GetTop ( rect ) - top ;
798+
799+ var rectGeometry = new RectangleGeometry (
800+ new Rect ( relativeX , relativeY , rect . Width , rect . Height ) ) ;
801+
802+ context . DrawGeometry ( rect . Fill , null , rectGeometry ) ;
803+ }
804+ }
805+ return new VisualBrush ( drawingVisual )
806+ {
807+ Stretch = Stretch . None ,
808+ AlignmentX = AlignmentX . Left ,
809+ AlignmentY = AlignmentY . Top
810+ } ;
811+ }
812+
813+ private void RemoveTemporaryRectangles ( )
814+ {
815+ foreach ( var rect in _currentStrokeRectangles )
816+ {
817+ _canvas . Children . Remove ( rect ) ;
818+ }
819+ }
820+
821+ private Color GetAreaAverageColor ( Point center , int areaSize )
822+ {
823+ try
824+ {
825+ double scaleX = _imageSnapshot . PixelWidth / _canvas . ActualWidth ;
826+ double scaleY = _imageSnapshot . PixelHeight / _canvas . ActualHeight ;
827+ int pixelX = ( int ) ( center . X * scaleX ) ;
828+ int pixelY = ( int ) ( center . Y * scaleY ) ;
829+ int halfSize = areaSize / 2 ;
830+ int totalR = 0 , totalG = 0 , totalB = 0 ;
831+ int count = 0 ;
832+ for ( int dx = - halfSize ; dx <= halfSize ; dx ++ )
833+ {
834+ for ( int dy = - halfSize ; dy <= halfSize ; dy ++ )
835+ {
836+ int x = pixelX + dx ;
837+ int y = pixelY + dy ;
838+
839+ if ( x >= 0 && x < _imageSnapshot . PixelWidth &&
840+ y >= 0 && y < _imageSnapshot . PixelHeight )
841+ {
842+ byte [ ] pixels = new byte [ 4 ] ;
843+ _imageSnapshot . CopyPixels ( new Int32Rect ( x , y , 1 , 1 ) , pixels , 4 , 0 ) ;
844+
845+ totalR += pixels [ 2 ] ;
846+ totalG += pixels [ 1 ] ;
847+ totalB += pixels [ 0 ] ;
848+ count ++ ;
849+ }
850+ }
642851 }
643852 if ( count == 0 ) return Colors . Gray ;
644853 return Color . FromRgb (
@@ -1020,7 +1229,7 @@ protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
10201229 &&
10211230 _inkRadioButton . IsChecked != true
10221231 &&
1023- _radioButtonInk . IsChecked != true )
1232+ _mosaicRadioButton . IsChecked != true )
10241233 _screenCutMouseType = ScreenCutMouseType . Default ;
10251234 else
10261235 DisposeControl ( ) ;
0 commit comments