11using System ;
2+ using System . Collections . Generic ;
23using System . IO ;
34using SixLabors . ImageSharp ;
45using SixLabors . ImageSharp . Drawing ;
@@ -25,14 +26,13 @@ public static Image ScaleImage(Image image, double scaleFactor)
2526
2627 public static Image DeepFryImage ( Image image )
2728 {
28- image . Mutate (
29- i =>
30- {
31- i . Contrast ( 5f ) ;
32- i . Brightness ( 1.5f ) ;
33- i . GaussianSharpen ( 5f ) ;
34- i . Saturate ( 5f ) ;
35- } ) ;
29+ image . Mutate ( i =>
30+ {
31+ i . Contrast ( 5f ) ;
32+ i . Brightness ( 1.5f ) ;
33+ i . GaussianSharpen ( 5f ) ;
34+ i . Saturate ( 5f ) ;
35+ } ) ;
3636
3737 return image ;
3838 }
@@ -119,20 +119,19 @@ public static Image<TPixel> CropToCircle<TPixel>(Image<TPixel> image)
119119 imageAsPngWithTransparency = image ;
120120 }
121121
122- Image < TPixel > cloned = imageAsPngWithTransparency . Clone (
123- i =>
122+ Image < TPixel > cloned = imageAsPngWithTransparency . Clone ( i =>
123+ {
124+ var opts = new DrawingOptions
124125 {
125- var opts = new DrawingOptions
126+ GraphicsOptions = new GraphicsOptions
126127 {
127- GraphicsOptions = new GraphicsOptions
128- {
129- Antialias = true ,
130- AlphaCompositionMode = PixelAlphaCompositionMode . DestIn ,
131- } ,
132- } ;
128+ Antialias = true ,
129+ AlphaCompositionMode = PixelAlphaCompositionMode . DestIn ,
130+ } ,
131+ } ;
133132
134- i . Fill ( opts , Color . Black , ellipsePath ) ;
135- } ) ;
133+ i . Fill ( opts , Color . Black , ellipsePath ) ;
134+ } ) ;
136135
137136 return cloned ;
138137 }
@@ -185,32 +184,31 @@ public static Image PetPet(Image<Rgba32> inputImage, int fps = default)
185184 float [ ] squishFactors = [ 0.9f , 0.8f , 0.75f , 0.8f , 0.85f ] ;
186185
187186 // Resize the input image to match the frame size
188- Image < Rgba32 > resizedImage = inputImage . Clone (
189- ctx =>
190- {
191- // Crop the image to a square
192- ctx . Crop (
193- Math . Min ( inputImage . Width , inputImage . Height ) ,
194- Math . Min ( inputImage . Width , inputImage . Height ) ) ;
187+ Image < Rgba32 > resizedImage = inputImage . Clone ( ctx =>
188+ {
189+ // Crop the image to a square
190+ ctx . Crop (
191+ Math . Min ( inputImage . Width , inputImage . Height ) ,
192+ Math . Min ( inputImage . Width , inputImage . Height ) ) ;
195193
196- // Downscale the image to the frame size and then some
197- const int newWidth = ( int ) ( frameSize * scale ) ;
198- const int newHeight = ( int ) ( frameSize * scale ) ;
194+ // Downscale the image to the frame size and then some
195+ const int newWidth = ( int ) ( frameSize * scale ) ;
196+ const int newHeight = ( int ) ( frameSize * scale ) ;
199197
200- ctx . Resize (
201- new ResizeOptions
202- {
203- Size = new Size ( newWidth , newHeight ) ,
204- Sampler = KnownResamplers . Hermite ,
205- } ) ;
206- } ) ;
198+ ctx . Resize (
199+ new ResizeOptions
200+ {
201+ Size = new Size ( newWidth , newHeight ) ,
202+ Sampler = KnownResamplers . Hermite ,
203+ } ) ;
204+ } ) ;
207205
208206 // Optimize transparency by clipping pixels with low alpha values in order to reduce dithering artifacts
209207 resizedImage . ProcessPixelRows ( a => ImageProcessors . ClipTransparencyProcessor ( a , alphaThreshold ) ) ;
210208
211209 for ( var i = 0 ; i < frameCount ; i ++ )
212210 {
213- var overlayFrameRectangle = new Rectangle ( i * overlayWidth , 0 , overlayWidth , overlayHeight ) ;
211+ var overlayFrameRectangle = new Rectangle ( i * overlayWidth , y : 0 , overlayWidth , overlayHeight ) ;
214212
215213 Image < Rgba32 > overlayFrame = overlaySpriteSheet . Clone ( ctx => ctx . Crop ( overlayFrameRectangle ) ) ;
216214
@@ -220,23 +218,24 @@ public static Image PetPet(Image<Rgba32> inputImage, int fps = default)
220218 Image < Rgba32 > squishedFrame =
221219 resizedImage . Clone ( x => x . Resize ( resizedImage . Width , ( int ) ( resizedImage . Height * frameSquishFactor ) ) ) ;
222220#if DEBUG
221+
223222 // temporarily save the frame to disk
224223 squishedFrame . SaveAsPng ( Path . Combine ( Path . GetTempPath ( ) , "kattbot" , $ "a_squished_frame_{ i } .png") ) ;
225224#endif
226225 var outputFrame = new Image < Rgba32 > ( frameSize , frameSize ) ;
227226
228227 // Draw the resized input frame in the bottom right corner
229- outputFrame . Mutate (
230- x =>
231- {
232- int newFrameOffsetX = frameSize - squishedFrame . Width ;
233- int newFrameOffsetY = frameSize - squishedFrame . Height ;
234- x . DrawImage ( squishedFrame , new Point ( newFrameOffsetX , newFrameOffsetY ) , 1f ) ;
235- } ) ;
228+ outputFrame . Mutate ( x =>
229+ {
230+ int newFrameOffsetX = frameSize - squishedFrame . Width ;
231+ int newFrameOffsetY = frameSize - squishedFrame . Height ;
232+ x . DrawImage ( squishedFrame , new Point ( newFrameOffsetX , newFrameOffsetY ) , opacity : 1f ) ;
233+ } ) ;
236234
237235 // Draw the overlay frame
238- outputFrame . Mutate ( x => x . DrawImage ( overlayFrame , new Point ( 0 , overlayY ) , 1f ) ) ;
236+ outputFrame . Mutate ( x => x . DrawImage ( overlayFrame , new Point ( x : 0 , overlayY ) , opacity : 1f ) ) ;
239237#if DEBUG
238+
240239 // temporarily save the frame to disk
241240 outputFrame . SaveAsPng ( Path . Combine ( Path . GetTempPath ( ) , "kattbot" , $ "b_frame_{ i } .png") ) ;
242241#endif
@@ -267,4 +266,82 @@ public static Image PetPet(Image<Rgba32> inputImage, int fps = default)
267266
268267 return outputGif ;
269268 }
269+
270+ public static Image FillMaskWithTiledImage (
271+ Image < Rgba32 > targetImage ,
272+ Image < Rgba32 > maskImage ,
273+ Image < Rgba32 > tileImage )
274+ {
275+ Image < Rgba32 > result = targetImage . Clone ( ) ;
276+ var random = new Random ( ) ;
277+
278+ // Resize tile image based on target image width ratio (1/40 for 32px tiles on 1280px width)
279+ const double tileRatio = 1.0 / 40.0 ;
280+ var desiredTileSize = ( int ) ( targetImage . Width * tileRatio ) ;
281+
282+ // Resize the tile image to maintain square aspect ratio
283+ Image < Rgba32 > resizedTileImage = tileImage . Clone ( ctx =>
284+ ctx . Resize ( desiredTileSize , desiredTileSize , KnownResamplers . Hermite ) ) ;
285+
286+ // Calculate tile placement parameters
287+ int tileWidth = resizedTileImage . Width ;
288+ int tileHeight = resizedTileImage . Height ;
289+ int maxJitterX = tileWidth / 4 ;
290+ int maxJitterY = tileHeight / 4 ;
291+
292+ // Create a list of mask regions to fill
293+ var maskRegions = new List < Point > ( ) ;
294+
295+ // Scan for white pixels in the mask (sampling at intervals)
296+ for ( var y = 0 ; y < maskImage . Height ; y += tileHeight / 2 )
297+ {
298+ for ( var x = 0 ; x < maskImage . Width ; x += tileWidth / 2 )
299+ {
300+ if ( x < maskImage . Width && y < maskImage . Height )
301+ {
302+ Rgba32 pixel = maskImage [ x , y ] ;
303+
304+ // Check if pixel is white (high RGB values)
305+ if ( pixel . R > 200 && pixel . G > 200 && pixel . B > 200 )
306+ {
307+ maskRegions . Add ( new Point ( x , y ) ) ;
308+ }
309+ }
310+ }
311+ }
312+
313+ // Place tiles randomly over mask regions
314+ foreach ( Point region in maskRegions )
315+ {
316+ // Add random jitter to tile placement
317+ int jitterX = random . Next ( - maxJitterX , maxJitterX ) ;
318+ int jitterY = random . Next ( - maxJitterY , maxJitterY ) ;
319+
320+ // Center the tile on the detected mask pixel
321+ int tileX = ( region . X + jitterX ) - ( tileWidth / 2 ) ;
322+ int tileY = ( region . Y + jitterY ) - ( tileHeight / 2 ) ;
323+
324+ // Only draw if the tile center would be within the mask area
325+ int centerX = tileX + ( tileWidth / 2 ) ;
326+ int centerY = tileY + ( tileHeight / 2 ) ;
327+
328+ if ( centerX >= 0 && centerX < maskImage . Width &&
329+ centerY >= 0 && centerY < maskImage . Height )
330+ {
331+ Rgba32 centerPixel = maskImage [ centerX , centerY ] ;
332+ if ( centerPixel . R > 200 && centerPixel . G > 200 && centerPixel . B > 200 )
333+ {
334+ // Ensure tile placement is within target image bounds
335+ tileX = Math . Max ( val1 : 0 , Math . Min ( tileX , targetImage . Width - tileWidth ) ) ;
336+ tileY = Math . Max ( val1 : 0 , Math . Min ( tileY , targetImage . Height - tileHeight ) ) ;
337+
338+ // Draw the tile image at the calculated position
339+ result . Mutate ( ctx => { ctx . DrawImage ( resizedTileImage , new Point ( tileX , tileY ) , opacity : 1f ) ; } ) ;
340+ }
341+ }
342+ }
343+
344+ resizedTileImage . Dispose ( ) ;
345+ return result ;
346+ }
270347}
0 commit comments