Code archives/Graphics/BlitzComp

This code has been declared by its author to be Public Domain code.

Download source code

BlitzComp by Pongo2007
This allows you to mix two images together using assorted blending modes, similar to those found in image editing programs. (add,multiply,overlay,alpha) It also allows you to supply a mask for smooth composites, or will also work in clip mode. Additionally, you can tint the top layer at the time of composite. This could be useful for generating a user specified color on a portion of a texture.

Here is an example with some images.
; Composite modes, by Pongo 10-26-07
; This code will let you composite multiple images together, using blending modes.
; There are different functions for the different modes, but they all work the same way

; Simply call the function with 2 images (front image first) or optionally use a 3rd image for an alpha mask.
; If you do not specify a mask image, the composite will work in "clip" mode, And cut out 0,0,0 black.

; Additionally, you can supply extra RGB values, and the top image will be tinted to that color. Run this example to see it all work.

; Note: This code requires all of the images to be the same pixel size.

; last updated 10-26 cleaned up a bit and fixed a problem with non-square images.

Graphics 640,480,0,2
SetBuffer BackBuffer()
SeedRnd MilliSecs()

;set the initial image
img_comp = compimage_mask("image1.png", "image2.png","mask.png")
info$ = "3) mask mode"

While Not KeyHit(1)

	If KeyHit (2) ; 1 key
		img_comp =  compimage_mask("image1.png", "image2.png")
		info$ = "1) clip mode"
	EndIf 

	If KeyHit (3) ; 2 key
		img_comp =  compimage_mask("image1.png", "image2.png",0,Rnd(255),Rnd(255),Rnd(255) )
		info$ = "2) clip mode tinted with random color"
	EndIf 

	If KeyHit (4) ; 3 key
		img_comp = compimage_mask("image1.png", "image2.png","mask.png")
		info$ = "3) mask mode"
	EndIf 

	If KeyHit (5) ;4 key
		img_comp =  compimage_mask("image1.png", "image2.png","mask.png",Rnd(255),Rnd(255),Rnd(255) )
		info$ = "4) mask mode tinted with random color"
	EndIf

	If KeyHit (6) ; 5 key
		img_comp = compimage_add("image1.png", "image2.png","mask.png")
		info$ = "5) add mode"
	EndIf 

	If KeyHit (7) ;6 key
		img_comp =  compimage_add("image1.png", "image2.png","mask.png",Rnd(255),Rnd(255),Rnd(255) )
		info$ = "6) add mode tinted with random color"
	EndIf 

	If KeyHit (8) ; 7 key
		img_comp = compimage_multiply("image1.png", "image2.png","mask.png")
		info$ = "7) multiply mode"
	EndIf 

	If KeyHit (9) ;8 key
		img_comp =  compimage_multiply("image1.png", "image2.png","mask.png",Rnd(255),Rnd(255),Rnd(255) )
		info$ = "8) multiply mode tinted with random color"
	EndIf 

	If KeyHit (10) ; 9 key
		img_comp = compimage_overlay("image1.png", "image2.png","mask.png")
		info$ = "9) overlay mode"
	EndIf 

	If KeyHit (11) ;0 key
		img_comp =  compimage_overlay("image1.png", "image2.png","mask.png",Rnd(255),Rnd(255),Rnd(255) )
		info$ = "0) overlay mode tinted with random color"
	EndIf 

	DrawImage img_comp,50,30

	Text 325,15,"Press 1-0 to change blend modes"
	Text  325,35,"1) Clip Mode"
	Text  325,50,"2) Clip Mode Tinted"
	Text  325,65,"3) Mask Mode"
	Text  325,80,"4) Mask Mode Tinted"
	Text  325,95,"5) Add Mode"
	Text  325,110,"6) Add Mode Tinted"
	Text  325,125,"7) Multiply Mode"
	Text  325,140,"8) Multiply Mode Tinted"
	Text  325,155,"9) Overlay Mode"
	Text  325,170,"0) Overlay Mode Tinted"

	Text 40,350,"current mode: " + info$
	Flip
	Cls 
Wend  ; end main loop

End ;end program

Function minmax#(value#,min#,max#)
	If value > max Return max
	If value < min Return min
	Return value
End Function

Function compimage_mask(image1$, image2$, mask$=0, tint_R=255, tint_G=255, tint_B=255 )

	Local img1,img2,maskimg,r,g,b,r2,g2,b2,r3,g3,b3,m_rgb,m#

	;load the images and do error checks
	img1 = LoadImage (image1)
	If img1 = 0 Then RuntimeError "Image " + image1$ + " not found."	
	img2 = LoadImage (image2)
	If img2 = 0 Then RuntimeError "Image " + image2$ + " not found."
	If mask <> 0 Then
		maskimg = LoadImage (mask)
		If maskimg = 0 Then RuntimeError "Image " + mask$ + " not found."
	EndIf
	
	;lock the buffers for the pixel operations
	LockBuffer (ImageBuffer(img1))
	LockBuffer (ImageBuffer(img2))
	If mask <> 0 Then LockBuffer (ImageBuffer(maskimg)) ;only lock mask buffer if this exists

	;loop through the pixels
	For y = 0 To ImageHeight(img1) -1
		For x = 0 To ImageWidth(img1) -1
			;read the RGB value of image1 and split the RGB values
			rgb=ReadPixelFast(x,y,ImageBuffer(img1))
			r=((rgb Shr 16) And $ff )
			g=((rgb Shr 8) And $ff )
			b=(rgb And $ff)

			;read the RGB value of image2 and split the RGB values
			rgb2=ReadPixelFast(x,y,ImageBuffer(img2))
			r2=(rgb2 Shr 16) And $ff 
			g2=(rgb2 Shr 8) And $ff 
			b2=rgb2 And $ff		

			If mask = 0 ; no mask, so clip black values
				If r+g+b>0 ;if the pixel is not black then tint it and copy to img2
					r = minmax( r - (128 - tint_R Shr 1),0,255)
					g = minmax( g - (128 - tint_G Shr 1),0,255)
					b =minmax(  b - (128 - tint_B Shr 1),0,255)
					rgb=(b Or (g Shl 8) Or (r Shl 16) Or ($ff000000))
					WritePixelFast(x,y,rgb,ImageBuffer(img2))
				EndIf

			Else ; mask is being used,... tint image1 and fade it into image2 using the mask
				;first tint like before
				r = minmax( r - ( 128 - tint_R Shr 1),0,255)
				g = minmax( g - ( 128 - tint_G Shr 1),0,255)
				b =minmax(  b - ( 128 - tint_B Shr 1),0,255)

				;read the RGB value of the mask image
				m_rgb=ReadPixelFast(x,y,ImageBuffer(maskimg))
				m =(((m_rgb Shr 16) And $ff ) + ((m_rgb Shr 8) And $ff ) + (m_rgb And $ff) )/ 3	;get average of RGB values of matte
				m= m /256 ;convert matte value from 0-255 to 0-1

				;now blend img1 and img2 with the mask value
				r3 = minmax (		r2 + ((r - r2)*m)		,0,255)
				g3 = minmax (		g2 + ((g -g2)*m)	,0,255)
				b3 = minmax (		b2 + ((b -b2)*m)	,0,255)

				rgb=(b3 Or (g3 Shl 8) Or (r3 Shl 16) Or ($ff000000))
				WritePixelFast(x,y,rgb,ImageBuffer(img2))
			EndIf
		Next
	Next 

	UnlockBuffer (ImageBuffer(img1))
	UnlockBuffer (ImageBuffer(img2))
	If mask <> 0 Then UnlockBuffer (ImageBuffer(maskimg))

	Return img2

End Function

Function compimage_add(image1$, image2$, mask$=0, tint_R=255, tint_G=255, tint_B=255)

	Local img1,img2,maskimg,r,g,b,r2,g2,b2,r3,g3,b3,m_rgb,m#

	;load the images and do error checks
	img1 = LoadImage (image1)
	If img1 = 0 Then RuntimeError "Image " + image1$ + " not found."	
	img2 = LoadImage (image2)
	If img2 = 0 Then RuntimeError "Image " + image2$ + " not found."
	If mask <> 0 Then
		maskimg = LoadImage (mask)
		If maskimg = 0 Then RuntimeError "Image " + mask$ + " not found."
	EndIf
	
	;lock the buffers for the pixel operations
	LockBuffer (ImageBuffer(img1))
	LockBuffer (ImageBuffer(img2))
	If mask <> 0 Then LockBuffer (ImageBuffer(maskimg)) ;only lock mask buffer if this exists

	;loop through the pixels
	For y = 0 To ImageHeight(img1) -1
		For x = 0 To ImageWidth(img1) -1
			;read the RGB value of image1 and split the RGB values
			rgb=ReadPixelFast(x,y,ImageBuffer(img1))
			r=((rgb Shr 16) And $ff )
			g=((rgb Shr 8) And $ff )
			b=(rgb And $ff)

			;read the RGB value of image2 and split the RGB values
			rgb2=ReadPixelFast(x,y,ImageBuffer(img2))
			r2=(rgb2 Shr 16) And $ff 
			g2=(rgb2 Shr 8) And $ff 
			b2=rgb2 And $ff		

			If mask = 0 ; no mask, so clip black values
				If r+g+b>0 ;if the pixel is not black then tint it and copy to img2
					r = minmax( (r+r2) - (128 - tint_R Shr 1),0,255)
					g = minmax( (g+g2) - (128 - tint_G Shr 1),0,255)
					b =minmax(  (b+b2)- (128 - tint_B Shr 1),0,255)
					rgb=(b Or (g Shl 8) Or (r Shl 16) Or ($ff000000))
					WritePixelFast(x,y,rgb,ImageBuffer(img2))
				EndIf

			Else ; mask is being used,... tint image1 and fade it into image2 using the mask
				;first tint like before
				r = minmax( (r+r2 )- ( 128 - tint_R Shr 1),0,255)
				g = minmax( (g+g2) - ( 128 - tint_G Shr 1),0,255)
				b =minmax(  (b+b2) - ( 128 - tint_B Shr 1),0,255)

				;read the RGB value of the mask image
				m_rgb=ReadPixelFast(x,y,ImageBuffer(maskimg))
				m =(((m_rgb Shr 16) And $ff ) + ((m_rgb Shr 8) And $ff ) + (m_rgb And $ff) )/ 3	;get average of RGB values of matte
				m= m /256 ;convert matte value from 0-255 to 0-1

				;now blend img1 and img2 with the mask value
				r3 = minmax (		r2 + ((r - r2)*m)		,0,255)
				g3 = minmax (		g2 + ((g -g2)*m)	,0,255)
				b3 = minmax (		b2 + ((b -b2)*m)	,0,255)

				rgb=(b3 Or (g3 Shl 8) Or (r3 Shl 16) Or ($ff000000))
				WritePixelFast(x,y,rgb,ImageBuffer(img2))
			EndIf
		Next
	Next 

	UnlockBuffer (ImageBuffer(img1))
	UnlockBuffer (ImageBuffer(img2))
	If mask <> 0 Then UnlockBuffer (ImageBuffer(maskimg))

	Return img2

End Function 

Function compimage_multiply(image1$, image2$, mask$=0, tint_R=255, tint_G=255, tint_B=255)

	Local img1,img2,maskimg,r,g,b,r2,g2,b2,r3,g3,b3,m_rgb,m#

	;load the images and do error checks
	img1 = LoadImage (image1)
	If img1 = 0 Then RuntimeError "Image " + image1$ + " not found."	
	img2 = LoadImage (image2)
	If img2 = 0 Then RuntimeError "Image " + image2$ + " not found."
	If mask <> 0 Then
		maskimg = LoadImage (mask)
		If maskimg = 0 Then RuntimeError "Image " + mask$ + " not found."
	EndIf
	
	;lock the buffers for the pixel operations
	LockBuffer (ImageBuffer(img1))
	LockBuffer (ImageBuffer(img2))
	If mask <> 0 Then LockBuffer (ImageBuffer(maskimg)) ;only lock mask buffer if this exists

	;loop through the pixels
	For y = 0 To ImageHeight(img1) -1
		For x = 0 To ImageWidth(img1) -1
			;read the RGB value of image1 and split the RGB values
			rgb=ReadPixelFast(x,y,ImageBuffer(img1))
			r=((rgb Shr 16) And $ff )
			g=((rgb Shr 8) And $ff )
			b=(rgb And $ff)

			;read the RGB value of image2 and split the RGB values
			rgb2=ReadPixelFast(x,y,ImageBuffer(img2))
			r2=(rgb2 Shr 16) And $ff 
			g2=(rgb2 Shr 8) And $ff 
			b2=rgb2 And $ff		

			If mask = 0 ; no mask, so clip black values
				If r+g+b>0 ;if the pixel is not black then tint it and copy to img2
					r = minmax( (r*r2)Shr 8 - (128 - tint_R Shr 1),0,255)
					g = minmax( (g*g2)Shr 8 - (128 - tint_G Shr 1),0,255)
					b =minmax(  (b*b2)Shr 8 - (128 - tint_B Shr 1),0,255)
					rgb=(b Or (g Shl 8) Or (r Shl 16) Or ($ff000000))
					WritePixelFast(x,y,rgb,ImageBuffer(img2))
				EndIf

			Else ; mask is being used,... tint image1 and fade it into image2 using the mask
				;first tint like before
				r = minmax( (r*r2 )Shr 8- ( 128 - tint_R Shr 1),0,255)
				g = minmax( (g*g2)Shr 8 - ( 128 - tint_G Shr 1),0,255)
				b =minmax(  (b*b2)Shr 8 - ( 128 - tint_B Shr 1),0,255)

				;read the RGB value of the mask image
				m_rgb=ReadPixelFast(x,y,ImageBuffer(maskimg))
				m =(((m_rgb Shr 16) And $ff ) + ((m_rgb Shr 8) And $ff ) + (m_rgb And $ff) )/ 3	;get average of RGB values of matte
				m= m /256 ;convert matte value from 0-255 to 0-1

				;now blend img1 and img2 with the mask value
				r3 = minmax (		r2 + ((r - r2)*m)		,0,255)
				g3 = minmax (		g2 + ((g -g2)*m)	,0,255)
				b3 = minmax (		b2 + ((b -b2)*m)	,0,255)

				rgb=(b3 Or (g3 Shl 8) Or (r3 Shl 16) Or ($ff000000))
				WritePixelFast(x,y,rgb,ImageBuffer(img2))
			EndIf
		Next
	Next 

	UnlockBuffer (ImageBuffer(img1))
	UnlockBuffer (ImageBuffer(img2))
	If mask <> 0 Then UnlockBuffer (ImageBuffer(maskimg))

	Return img2

End Function 

Function compimage_overlay(image1$, image2$, mask$=0, tint_R=255, tint_G=255, tint_B=255 )

	Local img1,img2,r,g,b,r2,g2,b2,r3,g3,b3,tmp,m#

	img1 = LoadImage (image1)
	img2 = LoadImage (image2)
	If mask <> 0 Then maskimg = LoadImage (mask)

	LockBuffer (ImageBuffer(img1))
	LockBuffer (ImageBuffer(img2))
	If mask <> 0 Then LockBuffer (ImageBuffer(maskimg)) ;only lock mask buffer if this exists
	
	For y = 0 To ImageHeight(img1) -1
		For x = 0 To ImageWidth(img1) -1
			rgb=ReadPixelFast(x,y,ImageBuffer(img1))

			r = minmax( ((rgb Shr 16) And $ff) - (255 - tint_R),0,255)
			g = minmax( ((rgb Shr 8) And $ff ) - (255 - tint_G),0,255)
			b =minmax(  (rgb And $ff) - (255 - tint_B),0,255)
			
			rgb2=ReadPixelFast(x,y,ImageBuffer(img2))
			r2=(rgb2 Shr 16) And $ff 
			g2=(rgb2 Shr 8) And $ff 
			b2=rgb2 And $ff

			If mask = 0 ; no mask, so clip black values
				If r+g+b>0 ;if the pixel is not black then tint it and copy to img2
					r3 =minmax(255 - ((255-r) * (255 - r2) Shr 7),0,255)
					g3 =minmax(255 - ((255-g) * (255 - g2) Shr 7),0,255)
					b3 =minmax(255 - ((255-b) * (255 - b2) Shr 7),0,255)
					rgb=(b3 Or (g3 Shl 8) Or (r3 Shl 16) Or ($ff000000))
					WritePixelFast(x,y,rgb,ImageBuffer(img2))
				EndIf

			Else ; mask is being used,... tint image1 and fade it into image2 using the mask

				r3 =minmax(255 - ((255-r) * (255 - r2) Shr 7),0,255)
				g3 =minmax(255 - ((255-g) * (255 - g2) Shr 7),0,255)
				b3 =minmax(255 - ((255-b) * (255 - b2) Shr 7),0,255)

				;read the RGB value of the mask image
				m_rgb=ReadPixelFast(x,y,ImageBuffer(maskimg))
				m =(((m_rgb Shr 16) And $ff ) + ((m_rgb Shr 8) And $ff ) + (m_rgb And $ff) )/ 3	;get average of RGB values of matte
				m= m /256 ;convert matte value from 0-255 to 0-1

				r = minmax (	r2 + ((r3 - r2)*m)		,0,255)
				g = minmax (	g2 + ((g3 -g2)*m)		,0,255)
				b = minmax (	b2 + ((b3 -b2)*m)		,0,255)

				rgb=(b Or (g Shl 8) Or (r Shl 16) Or ($ff000000))
				WritePixelFast(x,y,rgb,ImageBuffer(img2))
			EndIf

		Next
	Next
		
	UnlockBuffer (ImageBuffer(img1))
	UnlockBuffer (ImageBuffer(img2))
	If mask <> 0 Then UnlockBuffer (ImageBuffer(maskimg))
	
	Return img2

End Function

Comments

OverDozing2007
Excellent Stuff! Works on b3d and b+ :)


Pongo2007
Updated,... fixed a bug that did not allow non-square images. (Thanks OverDozing)


Ked2007
Wow. Pretty cool!


Stevie G2007
Very nicely done!


Pongo2007
Thanks for the comments. I tried to make it all as self contained as possible. you should only need the minmax function plus whichever composite mode function(s) to make it all work. Originally all the modes were going to be one big function, but I decided to simplify a bit in the end.

This is all easy stuff to do in an image editing program, but I wanted a way to comp images on the fly, so a user could specify their own color on part of a texture. This could also be usefull when you want to set different team colors from a single base texture.

Enjoy!


Code Archives Forum