1
- using System . Numerics ;
2
- using System . Runtime . InteropServices . WindowsRuntime ;
3
- using CommunityToolkit . Mvvm . ComponentModel ;
1
+ using CommunityToolkit . Mvvm . ComponentModel ;
4
2
using CommunityToolkit . Mvvm . Input ;
5
3
using Microsoft . Graphics . Canvas ;
6
4
using Microsoft . UI ;
7
5
using Microsoft . UI . Xaml . Media . Imaging ;
8
6
using ScanStack . Core . Models ;
9
7
using ScanStack . Helpers ;
8
+ using System . Numerics ;
9
+ using System . Runtime . InteropServices . WindowsRuntime ;
10
10
using Windows . Foundation ;
11
11
using Windows . Graphics . Imaging ;
12
12
using Windows . Storage ;
@@ -20,6 +20,7 @@ public partial class ImageEditor : ObservableObject
20
20
21
21
[ ObservableProperty ]
22
22
[ NotifyCanExecuteChangedFor ( nameof ( RotateImageCommand ) ) ]
23
+ [ NotifyCanExecuteChangedFor ( nameof ( MirrorImageCommand ) ) ]
23
24
private FileModel ? _selectedFile ;
24
25
25
26
private bool CanExecute => SelectedFile != null ;
@@ -62,6 +63,89 @@ private async Task RotateImage(object angleParam)
62
63
FileChanged ? . Invoke ( selectedFile , outputFile . Path ) ;
63
64
}
64
65
66
+ [ RelayCommand ( CanExecute = nameof ( CanExecute ) ) ]
67
+ private async Task MirrorImage ( object mirrorHorizontally )
68
+ {
69
+ bool MirrorHorizontally = bool . Parse ( mirrorHorizontally . ToString ( ) ! ) ;
70
+ var selectedFile = SelectedFile ! ;
71
+ var pictures = await StorageLibrary . GetLibraryAsync ( KnownLibraryId . Pictures ) ;
72
+ var savePath = Path . Combine ( pictures . SaveFolder . Path , "ScanStack" ) ;
73
+ if ( ! Path . Exists ( savePath ) )
74
+ {
75
+ Directory . CreateDirectory ( savePath ) ;
76
+ }
77
+
78
+ var saveFolder = await StorageFolder . GetFolderFromPathAsync ( savePath ) ;
79
+ var inputFile = await StorageFile . GetFileFromPathAsync ( selectedFile . Path ) ;
80
+ var fileFormat = BitmapFileFormat . Png ;
81
+ switch ( Path . GetExtension ( selectedFile . Path ) )
82
+ {
83
+ case ".jpg" or ".jpeg" :
84
+ fileFormat = BitmapFileFormat . Jpeg ;
85
+ break ;
86
+ case ".png" :
87
+ fileFormat = BitmapFileFormat . Png ;
88
+ break ;
89
+ case ".tiff" :
90
+ fileFormat = BitmapFileFormat . Tiff ;
91
+ break ;
92
+ case ".bmp" :
93
+ fileFormat = BitmapFileFormat . Bmp ;
94
+ break ;
95
+ default :
96
+ break ;
97
+ }
98
+ var outputFile = await saveFolder . CreateFileAsync ( $ "CopyOf_{ Path . GetFileName ( selectedFile . Path ) } ", CreationCollisionOption . ReplaceExisting ) ;
99
+ using var inputStream = await inputFile . OpenAsync ( FileAccessMode . Read ) ;
100
+ using var outputStream = await outputFile . OpenAsync ( FileAccessMode . ReadWrite ) ;
101
+ await MirrorImageAsync ( inputStream , outputStream , fileFormat , MirrorHorizontally ) ;
102
+ FileChanged ? . Invoke ( selectedFile , outputFile . Path ) ;
103
+ }
104
+ public static async Task MirrorImageAsync ( IRandomAccessStream inputStream , IRandomAccessStream outputStream , BitmapFileFormat bitmapFileFormat , bool mirrorHorizontally = true )
105
+ {
106
+ var device = CanvasDevice . GetSharedDevice ( ) ;
107
+
108
+ using var sourceBitmap = await CanvasBitmap . LoadAsync ( device , inputStream ) ;
109
+ var sourceWidth = ( float ) sourceBitmap . Size . Width ;
110
+ var sourceHeight = ( float ) sourceBitmap . Size . Height ;
111
+
112
+ var center = new Vector2 ( sourceWidth / 2 , sourceHeight / 2 ) ;
113
+
114
+ using var renderTarget = new CanvasRenderTarget ( device , sourceWidth , sourceHeight , sourceBitmap . Dpi ) ;
115
+ using ( var session = renderTarget . CreateDrawingSession ( ) )
116
+ {
117
+ session . Clear ( Colors . Transparent ) ;
118
+
119
+ // Apply mirroring transformations
120
+ var transform = Matrix3x2 . Identity ;
121
+
122
+ if ( mirrorHorizontally )
123
+ {
124
+ transform *= Matrix3x2 . CreateScale ( - 1 , 1 , center ) ;
125
+ }
126
+ else
127
+ {
128
+ transform *= Matrix3x2 . CreateScale ( 1 , - 1 , center ) ;
129
+ }
130
+
131
+ session . Transform = transform ;
132
+ session . DrawImage ( sourceBitmap , new Rect ( 0 , 0 , sourceWidth , sourceHeight ) ) ;
133
+ }
134
+
135
+ var pixelBytes = renderTarget . GetPixelBytes ( ) ;
136
+ var bitmapEncoder = await BitmapEncoder . CreateAsync ( GetEncoderId ( bitmapFileFormat ) , outputStream ) ;
137
+ bitmapEncoder . SetPixelData (
138
+ BitmapPixelFormat . Bgra8 ,
139
+ BitmapAlphaMode . Premultiplied ,
140
+ ( uint ) sourceWidth ,
141
+ ( uint ) sourceHeight ,
142
+ sourceBitmap . Dpi ,
143
+ sourceBitmap . Dpi ,
144
+ pixelBytes
145
+ ) ;
146
+ await bitmapEncoder . FlushAsync ( ) ;
147
+ }
148
+
65
149
public static async Task RotateImageAsync ( IRandomAccessStream inputStream , IRandomAccessStream outputStream , float rotationAngle , BitmapFileFormat bitmapFileFormat )
66
150
{
67
151
var device = CanvasDevice . GetSharedDevice ( ) ;
0 commit comments