66using Avalonia . Media ;
77using Avalonia . Media . Imaging ;
88using Avalonia . Platform . Storage ;
9+ using Avalonia . Threading ;
910using DatasetCrop . MVVM ;
1011using MessageBox . Avalonia ;
1112using MessageBox . Avalonia . Enums ;
@@ -134,7 +135,7 @@ private async Task BrowseInputAsync()
134135 {
135136 var result = await StorageProvider . OpenFolderPickerAsync ( new FolderPickerOpenOptions ( ) { AllowMultiple = false , Title = "Choose dataset images directory" } ) ;
136137 if ( result . Count > 0 && result [ 0 ] . TryGetUri ( out Uri ? directory ) )
137- InputPath = directory . AbsolutePath ;
138+ InputPath = directory . LocalPath ;
138139 }
139140
140141 /// <summary>
@@ -144,7 +145,7 @@ private async Task BrowseOutputAsync()
144145 {
145146 var result = await StorageProvider . OpenFolderPickerAsync ( new FolderPickerOpenOptions ( ) { AllowMultiple = false , Title = "Choose cropped images directory" } ) ;
146147 if ( result . Count > 0 && result [ 0 ] . TryGetUri ( out Uri ? directory ) )
147- OutputPath = directory . AbsolutePath ;
148+ OutputPath = directory . LocalPath ;
148149 }
149150
150151 /// <summary>
@@ -162,78 +163,113 @@ private async Task RefreshImagePreviewsAsync()
162163 int column = 0 ;
163164 int row = 0 ;
164165 int margin = 5 ;
165- // iterate all files in the input directory
166- foreach ( string file in Directory . GetFiles ( InputPath ! ) )
167- {
168- string ext = Path . GetExtension ( file ) . ToLower ( ) ;
169- if ( ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp" ) // only process images
166+ await Task . Run ( async ( ) =>
167+ {
168+ // iterate all files in the input directory
169+ var filePaths = Directory . GetFiles ( InputPath ! , "*.jpg" )
170+ . Concat ( Directory . GetFiles ( InputPath ! , "*.jpeg" ) )
171+ . Concat ( Directory . GetFiles ( InputPath ! , "*.png" ) )
172+ . Concat ( Directory . GetFiles ( InputPath ! , "*.bmp" ) ) ;
173+ var loadTasks = filePaths . Select ( async file =>
170174 {
171- // for each image file, create a grid that contains an image and a drag panel, and add it to the previews list
172- Grid container = new ( ) ;
173- container . Width = previewWidth ;
174- container . Height = previewHeight ;
175- container . Margin = new Thickness ( column * ( previewWidth + margin ) , row * ( previewWidth + margin ) , 0 , 0 ) ;
176- container . HorizontalAlignment = HorizontalAlignment . Left ;
177- container . VerticalAlignment = VerticalAlignment . Top ;
178- container . Background = new SolidColorBrush ( Avalonia . Media . Color . FromRgb ( 0 , 0 , 0 ) , 0.1 ) ;
179-
180- var bitmap = new Bitmap ( file ) ;
181- // for each image, ensure the drag panel is not in any way bigger than the visible area of the scaled down image preview
182- if ( ! ValidateCropPanelSize ( bitmap . Size . Width , bitmap . Size . Height , cropWidth , cropHeight , previewWidth , previewHeight ) )
183- {
184- await MessageBoxManager . GetMessageBoxStandardWindow ( "Error!" , "Specified crop size exceeds the bounds of the scaled image!" + Environment . NewLine + file , ButtonEnum . Ok , MessageBox . Avalonia . Enums . Icon . Error ) . ShowDialog ( this ) ;
185- ClearImagePreviews ( ) ;
186- return ;
187- }
188- Avalonia . Controls . Image image = new ( ) ;
189- image . Source = bitmap ;
190- image . Width = previewWidth ;
191- image . Height = previewHeight ;
192- image . Margin = new Thickness ( 0 ) ;
193- image . HorizontalAlignment = HorizontalAlignment . Left ;
194- image . VerticalAlignment = VerticalAlignment . Top ;
195- image . Tag = file ; // store the path of the original image file
196- ToolTip . SetTip ( image , file ) ;
197- container . Children . Add ( image ) ;
198-
199- Panel dragPanel = new ( ) ;
200- if ( usesOriginalScaleSizes )
175+ using ( var tempImage = await SixLabors . ImageSharp . Image . LoadAsync ( file ) )
201176 {
202- // calculate the uniform scaling factor based on the largest dimension
203- double scaleFactor = Math . Max ( bitmap . Size . Width / previewWidth , bitmap . Size . Height / previewHeight ) ;
204- // scale down the crop size and position uniformly
205- dragPanel . Width = cropWidth / scaleFactor ;
206- dragPanel . Height = cropHeight / scaleFactor ;
207- dragPanel . Margin = new Thickness ( cropX / scaleFactor , cropY / scaleFactor , 0 , 0 ) ;
177+ var originalSize = new Avalonia . Size ( tempImage . Width , tempImage . Height ) ;
178+ var resizedImage = await LoadResizedImageAsync ( tempImage , previewWidth , previewHeight ) ;
179+ return new { FilePath = file , Image = resizedImage , OriginalSize = originalSize } ;
208180 }
209- else
210- {
211- dragPanel . Width = cropWidth ;
212- dragPanel . Height = cropHeight ;
213- dragPanel . Margin = new Thickness ( cropX , cropY , 0 , 0 ) ;
214- }
215- dragPanel . HorizontalAlignment = HorizontalAlignment . Left ;
216- dragPanel . VerticalAlignment = VerticalAlignment . Top ;
217- dragPanel . Background = new SolidColorBrush ( Avalonia . Media . Color . FromRgb ( 255 , 255 , 255 ) , 0.3 ) ;
218- dragPanel . Cursor = new Cursor ( StandardCursorType . SizeAll ) ;
219- dragPanel . PointerMoved += DragPanel_PointerMoved ; // subscribe the event handlers used for dragging
220- dragPanel . PointerPressed += DragPanel_PointerPressed ;
221- dragPanel . PointerReleased += DragPanel_PointerReleased ;
222- dragPanel . Tag = true ; // true = "selected" (will be cropped), false = "deselected" (will be ignored when cropping)
223-
224- container . Children . Add ( dragPanel ) ;
225- grdImages . Children . Add ( container ) ;
226-
227- // increment column until the remaining horizontal space can no longer fit a whole image preview, then reset it and increment the row
228- if ( ( column + 2 ) * ( previewWidth + margin ) < Width - 12 ) // 12: 6 pixels for margin on each side for the images dragPanel
229- column ++ ;
230- else
181+ } ) ;
182+
183+ var loadedImages = await Task . WhenAll ( loadTasks ) ;
184+ foreach ( var loadedImage in loadedImages )
185+ {
186+ await Dispatcher . UIThread . InvokeAsync ( async ( ) =>
231187 {
232- column = 0 ;
233- row ++ ;
234- }
188+ // for each image file, create a grid that contains an image and a drag panel, and add it to the previews list
189+ Grid container = new ( ) ;
190+ container . Width = previewWidth ;
191+ container . Height = previewHeight ;
192+ container . Margin = new Thickness ( column * ( previewWidth + margin ) , row * ( previewHeight + margin ) , 0 , 0 ) ;
193+ container . HorizontalAlignment = HorizontalAlignment . Left ;
194+ container . VerticalAlignment = VerticalAlignment . Top ;
195+ container . Background = new SolidColorBrush ( Avalonia . Media . Color . FromRgb ( 0 , 0 , 0 ) , 0.1 ) ;
196+ // for each image, ensure the drag panel is not in any way bigger than the visible area of the scaled down image preview
197+ if ( ! ValidateCropPanelSize ( loadedImage . OriginalSize . Width , loadedImage . OriginalSize . Height , cropWidth , cropHeight , previewWidth , previewHeight ) )
198+ {
199+ await MessageBoxManager . GetMessageBoxStandardWindow ( "Error!" , "Specified crop size exceeds the bounds of the scaled image!" + Environment . NewLine + loadedImage . FilePath , ButtonEnum . Ok , MessageBox . Avalonia . Enums . Icon . Error ) . ShowDialog ( this ) ;
200+ ClearImagePreviews ( ) ;
201+ return ;
202+ }
203+ Avalonia . Controls . Image image = new ( ) ;
204+ //image.Source = bitmap;
205+ image . Width = previewWidth ;
206+ image . Height = previewHeight ;
207+ image . Source = loadedImage . Image ;
208+ image . Margin = new Thickness ( 0 ) ;
209+ image . HorizontalAlignment = HorizontalAlignment . Left ;
210+ image . VerticalAlignment = VerticalAlignment . Top ;
211+ image . Tag = loadedImage . FilePath ; // store the path of the original image file
212+ ToolTip . SetTip ( image , loadedImage . FilePath ) ;
213+ container . Children . Add ( image ) ;
214+
215+ Panel dragPanel = new ( ) ;
216+ if ( usesOriginalScaleSizes )
217+ {
218+ // calculate the uniform scaling factor based on the largest dimension
219+ double scaleFactor = Math . Max ( loadedImage . OriginalSize . Width / previewWidth , loadedImage . OriginalSize . Height / previewHeight ) ;
220+ // scale down the crop size and position uniformly
221+ dragPanel . Width = cropWidth / scaleFactor ;
222+ dragPanel . Height = cropHeight / scaleFactor ;
223+ dragPanel . Margin = new Thickness ( cropX / scaleFactor , cropY / scaleFactor , 0 , 0 ) ;
224+ }
225+ else
226+ {
227+ dragPanel . Width = cropWidth ;
228+ dragPanel . Height = cropHeight ;
229+ dragPanel . Margin = new Thickness ( cropX , cropY , 0 , 0 ) ;
230+ }
231+ dragPanel . HorizontalAlignment = HorizontalAlignment . Left ;
232+ dragPanel . VerticalAlignment = VerticalAlignment . Top ;
233+ dragPanel . Background = new SolidColorBrush ( Avalonia . Media . Color . FromRgb ( 255 , 255 , 255 ) , 0.3 ) ;
234+ dragPanel . Cursor = new Cursor ( StandardCursorType . SizeAll ) ;
235+ dragPanel . PointerMoved += DragPanel_PointerMoved ; // subscribe the event handlers used for dragging
236+ dragPanel . PointerPressed += DragPanel_PointerPressed ;
237+ dragPanel . PointerReleased += DragPanel_PointerReleased ;
238+ dragPanel . Tag = true ; // true = "selected" (will be cropped), false = "deselected" (will be ignored when cropping)
239+
240+ container . Children . Add ( dragPanel ) ;
241+ grdImages . Children . Add ( container ) ;
242+
243+ // increment column until the remaining horizontal space can no longer fit a whole image preview, then reset it and increment the row
244+ if ( ( column + 2 ) * ( previewWidth + margin ) < Width - 12 ) // 12: 6 pixels for margin on each side for the images dragPanel
245+ column ++ ;
246+ else
247+ {
248+ column = 0 ;
249+ row ++ ;
250+ }
251+ } ) ;
235252 }
236- }
253+ } ) ;
254+ }
255+
256+ /// <summary>
257+ /// Loads an image and returns a scaled down version of it, as Bitmap
258+ /// </summary>
259+ /// <param name="image">The image to load</param>
260+ /// <param name="targetWidth">The width of the scaled down bitmap</param>
261+ /// <param name="targetHeight">The height of the scaled down bitmap</param>
262+ /// <returns>A scaled down bitmap of the original image</returns>
263+ public static async Task < Bitmap > LoadResizedImageAsync ( SixLabors . ImageSharp . Image image , int targetWidth , int targetHeight )
264+ {
265+ // Calculate scale ratio to maintain aspect ratio
266+ var scale = Math . Min ( targetWidth / ( float ) image . Width , targetHeight / ( float ) image . Height ) ;
267+
268+ image . Mutate ( x => x . Resize ( ( int ) ( image . Width * scale ) , ( int ) ( image . Height * scale ) ) ) ;
269+ var memoryStream = new MemoryStream ( ) ;
270+ await image . SaveAsBmpAsync ( memoryStream ) ;
271+ memoryStream . Seek ( 0 , SeekOrigin . Begin ) ;
272+ return new Bitmap ( memoryStream ) ;
237273 }
238274
239275 /// <summary>
0 commit comments