10
10
using ReactiveUI . Fody . Helpers ;
11
11
using SixLabors . ImageSharp ;
12
12
using SixLabors . ImageSharp . PixelFormats ;
13
+ using SixLabors . ImageSharp . Processing ;
13
14
using System ;
14
15
using System . Collections . Generic ;
15
16
using System . Collections . ObjectModel ;
@@ -94,6 +95,9 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel
94
95
[ Reactive ]
95
96
public ICommand ExportImagesCommand { get ; set ; }
96
97
98
+ [ Reactive ]
99
+ public ICommand CropAllImagesCommand { get ; set ; }
100
+
97
101
[ Reactive ]
98
102
public int Zoom { get ; set ; } = 1 ;
99
103
@@ -142,6 +146,7 @@ public ImageTableViewModel(IHasG1Elements g1ElementProvider, IImageTableNameProv
142
146
ImportImagesCommand = ReactiveCommand . Create ( ImportImages ) ;
143
147
ExportImagesCommand = ReactiveCommand . Create ( ExportImages ) ;
144
148
ReplaceImageCommand = ReactiveCommand . Create ( ReplaceImage ) ;
149
+ CropAllImagesCommand = ReactiveCommand . Create ( CropAllImages ) ;
145
150
146
151
CreateSelectionModel ( ) ;
147
152
@@ -340,6 +345,62 @@ public async Task ExportImages()
340
345
}
341
346
}
342
347
348
+ public void CropAllImages ( )
349
+ {
350
+ for ( var i = 0 ; i < Images . Count ; ++ i )
351
+ {
352
+ var image = Images [ i ] ;
353
+
354
+ var cropRegion = FindCropRegion ( image ) ;
355
+
356
+ if ( cropRegion . Width <= 0 || cropRegion . Height <= 0 )
357
+ {
358
+ image . Mutate ( i => i . Crop ( new Rectangle ( 0 , 0 , 1 , 1 ) ) ) ;
359
+ UpdateImage ( image , i , 0 , 0 ) ;
360
+ }
361
+ else
362
+ {
363
+ image . Mutate ( i => i . Crop ( cropRegion ) ) ;
364
+ var currG1 = G1Provider . G1Elements [ i ] ;
365
+
366
+ // set to bitmaps
367
+ UpdateImage ( image , i , ( short ) ( currG1 . XOffset + cropRegion . Left ) , ( short ) ( currG1 . YOffset + cropRegion . Top ) ) ;
368
+ }
369
+ }
370
+
371
+ this . RaisePropertyChanged ( nameof ( Bitmaps ) ) ;
372
+ this . RaisePropertyChanged ( nameof ( SelectedG1Element ) ) ;
373
+ }
374
+
375
+ static Rectangle FindCropRegion ( Image < Rgba32 > image )
376
+ {
377
+ var minX = image . Width ;
378
+ var maxX = 0 ;
379
+ var minY = image . Height ;
380
+ var maxY = 0 ;
381
+
382
+ for ( var y = 0 ; y < image . Height ; y ++ )
383
+ {
384
+ for ( var x = 0 ; x < image . Width ; x ++ )
385
+ {
386
+ var pixel = image [ x , y ] ;
387
+
388
+ if ( pixel . A > 0 )
389
+ {
390
+ minX = Math . Min ( minX , x ) ;
391
+ maxX = Math . Max ( maxX , x ) ;
392
+ minY = Math . Min ( minY , y ) ;
393
+ maxY = Math . Max ( maxY , y ) ;
394
+ }
395
+ }
396
+ }
397
+
398
+ // Calculate the crop area. Ensure it is within image bounds.
399
+ var width = Math . Max ( 0 , Math . Min ( maxX - minX + 1 , image . Width - minX ) ) ;
400
+ var height = Math . Max ( 0 , Math . Min ( maxY - minY + 1 , image . Height - minY ) ) ;
401
+ return new Rectangle ( minX , minY , width , height ) ;
402
+ }
403
+
343
404
public async Task ReplaceImage ( )
344
405
{
345
406
if ( SelectedImageIndex == - 1 )
@@ -368,6 +429,11 @@ public async Task ReplaceImage()
368
429
}
369
430
370
431
void UpdateImage ( Image < Rgba32 > img , int index , SpriteOffset ? offset = null )
432
+ {
433
+ UpdateImage ( img , index , offset ? . X , offset ? . Y ) ;
434
+ }
435
+
436
+ void UpdateImage ( Image < Rgba32 > img , int index , short ? xOffset , short ? yOffset )
371
437
{
372
438
if ( index == - 1 )
373
439
{
@@ -381,8 +447,8 @@ void UpdateImage(Image<Rgba32> img, int index, SpriteOffset? offset = null)
381
447
Height = ( int16_t ) img . Height ,
382
448
Flags = currG1 . Flags ,
383
449
ImageData = PaletteMap . ConvertRgba32ImageToG1Data ( img , currG1 . Flags ) ,
384
- XOffset = offset ? . X ?? currG1 . XOffset ,
385
- YOffset = offset ? . Y ?? currG1 . YOffset ,
450
+ XOffset = xOffset ?? currG1 . XOffset ,
451
+ YOffset = yOffset ?? currG1 . YOffset ,
386
452
} ;
387
453
G1Provider . G1Elements [ index ] = currG1 ;
388
454
Images [ index ] = img ;
0 commit comments