Convert rectangular image with excess border into trimmed square sprite

Share your advanced PureBasic knowledge/code with the community.
Axeman
User
User
Posts: 89
Joined: Mon Nov 03, 2003 5:34 am

Convert rectangular image with excess border into trimmed square sprite

Post by Axeman »

This code allows you to turn an odd-sized image with a black background into a square sprite of whatever size you specify with excess border area trimmed off. The result is best used with additive blending.

If you need an image to test it out, go to http://weavesilk.com/ and create one.

This code isn't as versatile as it could be. I basically created it to turn a Weavesilk image into something I could use as a sprite. Feel free to modify it however you need.

; MakeSpriteImage( filepath.s, size = 128 )
; Loads the image with the specified filepath, trims off any excess black around the edges, and converts it to a square image with the size specified. The image will have a one-pixel border added.
; Note that the output images are best used with additive blending (eg. MaterialBlendingMode( test_material, #PB_Material_Add ) ).

Code: Select all


UsePNGImageDecoder()
UsePNGImageEncoder()



Procedure.i MakeSpriteImage( filepath.s, size = 128 )
; Loads the image with the specified filepath, trims off any excess black around the edges, and converts it to a square image with the size specified. The image will have a one-pixel border added.
; Note that the output images are best used with additive blending (eg. MaterialBlendingMode( test_material, #PB_Material_Add ) ).
; Returns the image number on success and a zero on failure.

If size < 8 : ProcedureReturn 0 : EndIf ; If the specified size is less than 8x8 then abort with a zero return.

source_image = LoadImage( #PB_Any, filepath.s )
If source_image = 0 : ProcedureReturn 0 : EndIf ; If the source image could not be loaded then abort with a zero return.

; Get the dimensions of the source image.
max_x = ImageWidth( source_image ) - 1
max_y = ImageHeight( source_image ) - 1
If max_x = -1 : ProcedureReturn 0 : EndIf ; If the source image was empty then abort with a zero return.

; -- Trim off any black borders. If the source image was blank then this block will clean up and abort with a zero value returned.
StartDrawing( ImageOutput( source_image ) )

trim_left = -1 ; Set the default for trim_left to flag an error if it is not modified.
For x = 0 To max_x
	For y = 0 To max_y
		If ( Point( x, y ) & $00ffffff ) <> $00000000 ; If the color channels are not the color black (ignoring the alpha channel) then we have found our trim point.
			trim_left = x
			Break 2
		EndIf
	Next
Next

If trim_left <> -1 ; If the image was not completely blank then continue and create the 'border_trim_image'. If it was blank then 'border_trim_image' will hold a zero.
	
	; Get trim for top border.
	For y = 0 To max_y
		For x = 0 To max_x
			If ( Point( x, y ) & $00ffffff ) <> $00000000 ; If the color channels are not the color black (ignoring the alpha channel) then we have found our trim point.
				trim_top = y
				Break 2
			EndIf
		Next
	Next
	
	; Get trim for right border.
	For x = max_x To 0 Step -1
		For y = 0 To max_y
			If ( Point( x, y ) & $00ffffff ) <> $00000000 ; If the color channels are not the color black (ignoring the alpha channel) then we have found our trim point.
				trim_right = x
				Break 2
			EndIf
		Next
	Next
	
	; Get trim for bottom border.
	For y = max_y To 0 Step -1
		For x = 0 To max_x
			If ( Point( x, y ) & $00ffffff ) <> $00000000 ; If the color channels are not the color black (ignoring the alpha channel) then we have found our trim point.
				trim_bottom = y
				Break 2
			EndIf
		Next
	Next
	
	If ( trim_left < trim_right ) And ( trim_top < trim_bottom ) ; Double check that the image wasn't blank or that the scan didn't fail for some reason.
		border_trim_image = GrabDrawingImage( #PB_Any, trim_left, trim_top, trim_right - trim_left + 1, trim_bottom - trim_top + 1 ) ; Grab the image with the border trimmed off it.
	EndIf
EndIf
StopDrawing()

FreeImage( source_image ) ; We are finished with the source image either way, so get rid of it.
If border_trim_image = 0 : ProcedureReturn 0 : EndIf ; If the source image was blank or GrabDrawingImage failed then abort with a zero return value.
; --

; Get the dimensions of the border trim image.
width = ImageWidth( border_trim_image )
height = ImageHeight( border_trim_image )

size2 = size - 2 ; Get the size we need for the image resize. This needs to account for a one-pixel border around the outside. The border prevents artifacts when the sprite is transformed.

; Resize the image to fit within our target size. The aspect ratio is preserved.
If ( width = size2 ) And ( height = size2 ) ; Image is already the size we need, so do nothing here.
	
ElseIf width = height ; Image is square.
	result = ResizeImage( border_trim_image, size2, size2 ) ; Resize the image uniformly.
ElseIf width > height ; Width is greater than height.
	size2_float.d = size2 * ( height / width ) ; We need this expression to be a floating point one, so send the output to a float.
	result = ResizeImage( border_trim_image, size2, size2_float.d ) ; Resize the image factoring in that height is narrower than width.
Else ; Height is greater than width.
	size2_float.d = size2 * ( width / height ) ; We need this expression to be a floating point one, so send the output to a float.
	result = ResizeImage( border_trim_image, size2_float.d, size2 ) ; Resize the image factoring in that width is narrower than height.
EndIf
If result = 0 : FreeImage( border_trim_image ) : ProcedureReturn 0 : EndIf ; If something went wrong with the resize then clean up and abort with a zero returned.

output_image = CreateImage( #PB_Any, size, size, 24 ) ; Create the output image. Since we will be using additive blending for our sprites, we don't need the alpha channel.
If output_image = 0 : FreeImage( border_trim_image ) : ProcedureReturn 0 : EndIf ; If something went wrong with the output image creation then clean up and abort with a zero returned.

; Draw the border trim image onto the output image with a one pixel border.
StartDrawing( ImageOutput( output_image ) )
DrawImage( ImageID( border_trim_image ), ( size - ImageWidth( border_trim_image ) ) / 2, ( size - ImageHeight( border_trim_image ) ) / 2 )
StopDrawing()

FreeImage( border_trim_image ) ; Clean up the border trim image.
	
ProcedureReturn output_image ; Returns the new sprite image.
EndProcedure



Procedure.i SaveSpriteImage( image, filepath.s )
; Save the specified sprite image to the specified filepath.
; Returns non-zero on success and a zero on failure.

ProcedureReturn SaveImage( image, filepath.s, #PB_ImagePlugin_PNG )
EndProcedure


; ; -- Test code - Use your own image filepaths --
; image = MakeSpriteImage( "C:\Users\axe73\Desktop\My Files\Business Projects\NFT Art\Glyph Maze 4x4\Glyph Maze 4x4 App\media\tracer-sprite-1.png" )
; If image : Debug "OK" : SaveSpriteImage( image, "C:\Users\axe73\Desktop\My Files\Business Projects\NFT Art\Glyph Maze 4x4\Glyph Maze 4x4 App\media\tracer-sprite-1-2.png" ) : EndIf