Code archives/Graphics/StackPixmapsPrecise & StackPixmapsFast

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

Download source code

StackPixmapsPrecise & StackPixmapsFast by Medicine Storm2009
Use the StackPixmapsPrecise or StackPixmapsFast functions to combine two pixmaps into one as if
the two were drawn one on top of the other. The transparency and color of the top pixmap can
also be modified. Also, the top pixmap can be flipped vertically and horizontally as well as
rotated in increments of 90 degrees. For example, The following 2 lines:

. pixNew = StackPixmapsPrecise(pixBottom, pixTop, a, r, g, b, x, y, XFlip, YFlip, RotateMultipleOf90)
. DrawPixmap(pixNew, 0, 0)

Will look identical to:

. DrawPixmap(pixBottom, 0, 0)
. If YFlip Then pixTop = YFlipPixmap(pixTop)
. If XFlip Then pixTop = XFlipPixmap(pixTop)
. SetColor(r, g, b)
. SetAlpha(a/255)
. SetRotation(RotateMultipleOf90)
. DrawPixmap(pixTop, x, y)

The StackPixmapsFast function does the same thing as StackPixmapsPrecise, but it runs about
5 times faster. The more pixels you are processing with it, the faster the performance gains are
between StackPixmapsFast and StackPixmapsPrecise. The only drawback to using StackPixmapsFast is
the color accuracy could be plus or minus 0.2%. That is: there is a 35% chance that the color
will be off by 2 points at most. For example: RGB(128, 128, 126) instead of RGB(128, 128, 128).
For most applications (especially when processing massive images) the 0.2% error is acceptable and,
unless you are a cyborg, unnoticeable.

These functions were designed for Project Utumno (http://utumno.medicinestorm.com) If you like these
functions or use this code, please, all I ask is that you visit Utumno.MedicineStorm.com and tell
me what you think.
'p_pixBottom: TPixmap imitating the pixmap drawn first, behind the top pixmap
'p_pixTop: TPixmap imitating the pixmap drawn second, on top of the first pixmap
'p_intAlpha: Optional. Value between 0 and 255 indicating transparency in addition to any transparency already in the image. Imitates SetAlpha(a/255) command. Only effects the top pixmap.
'p_intStainRed, p_intStainGreen, and p_intStainBlue: Optional. values between 0 and 255 indicating the colorization to be added to the image. Imitates SetColor(r, g, b) command. Only effects the top pixmap.
'p_intHorizontalOffset and p_intVerticalOffset: Optional. Shifts the top pixmap horizontally and vertically relative to the bottom pixmap. Negative values can be specified. Any areas of the top pixmap that are beyond the width or height of the bottom pixmap will be clipped off.
'p_blnFlipVertical and p_blnMirrorHorizontal: Optional. If "True", the image will be flipped about the X-axis and Y-axis, respectivly. Imitates XFlipPixmap() and YFlipPixmap() commands. only efects the top pixmap.
'p_intRotaiton: Optional. Values of 90, 180, or 270 will rotate the image by 90 degrees, 180 degrees, and 270 degrees, respectivly. Imitates SetRotation() command. Value must be a multiple of 90. Only effects the top pixmap.
Function StackPixmapsPrecise:TPixmap(p_pixBottom:TPixmap, p_pixTop:TPixmap, p_intAlpha:Int = 255, p_intStainRed:Int = 255, p_intStainGreen:Int = 255, p_intStainBlue:Int = 255, p_intHorizontalOffset:Int = 0, p_intVerticalOffset:Int = 0, p_blnFlipVertical:Byte = False, p_blnMirrorHorizontal:Byte = False, p_intRotation:Int = 0)
	'Initialize variables
	Local intLoopX:Int, intLoopY:Int 
	Local intOffsetX:Int, intOffsetY:Int, fltAlphaRatio:Float, fltInverseAlphaRatio:Float

	Local intTopPixel:Int, fltTopRed:Float,	fltTopGreen:Float, fltTopBlue:Float, fltTopAlpha:Float
	Local intBottomPixel:Int, fltBottomRed:Float, fltBottomGreen:Float,	fltBottomBlue:Float, fltBottomAlpha:Float
	Local intCompositePixel:Int, intCompositeRed:Int, intCompositeGreen:Int, intCompositeBlue:Int, intCompositeAlpha:Int

	Local intFMR:Int = FMRIndex(p_blnFlipVertical, p_blnMirrorHorizontal, p_intRotation)
	Local intMaxX:Int = p_pixTop.Width - 1
	Local intMaxY:Int = p_pixTop.Height - 1
	Local intBoundsWidth:Int = p_pixBottom.Width
	Local intBoundsHeight:Int = p_pixBottom.Height
	Local pixComposite:TPixmap = p_pixBottom.Copy()

	'Loop through each pixel
	For intLoopX = 0 To intMaxX
		For intLoopY = 0 To intMaxY

			'calculate offset location for later reference
			intOffsetX = intLoopX + p_intHorizontalOffset
			intOffsetY = intLoopY + p_intVerticalOffset

			'skip this pixel if it lies outside the boundaries of the bottom pixmap
			If intOffsetX < intBoundsWidth And intOffsetY < intBoundsHeight And intOffsetX >= 0 And intOffsetY >= 0

				'Get the top pixel at the current location, taking into account flip, mirror and rotation
				Select intFMR
					Case 0
						intTopPixel = p_pixTop.ReadPixel(intLoopX, intLoopY)

					Case 1
						intTopPixel = p_pixTop.ReadPixel(intMaxX - intLoopX, intLoopY)

					Case 2
						intTopPixel = p_pixTop.ReadPixel(intLoopX, intMaxY - intLoopY)

					Case 3
						intTopPixel = p_pixTop.ReadPixel(intMaxX - intLoopX, intMaxY - intLoopY)

					Case 4
						intTopPixel = p_pixTop.ReadPixel(intLoopY, intMaxX - intLoopX)

					Case 5
						intTopPixel = p_pixTop.ReadPixel(intMaxY - intLoopY, intLoopX)

					Case 6
						intTopPixel = p_pixTop.ReadPixel(intMaxY - intLoopY, intMaxX - intLoopX)

					Default '7
						intTopPixel = p_pixTop.ReadPixel(intLoopY, intLoopX)
				EndSelect 

				'Split pixel into Alpha channel
				fltTopAlpha = (intTopPixel Shr 24) & $FF

				'if no alpha or stain modifications are being done and the top pixel is totally opaque, there is no need to composite pixels
				If (Int(fltTopAlpha) & p_intStainRed & p_intStainGreen & p_intStainBlue & p_intAlpha) = 255
					intCompositePixel = intTopPixel
				Else
					'Split pixel into Red, Green, and Blue channels 
					fltTopRed = (intTopPixel Shr 16) & $FF
					fltTopGreen = (intTopPixel Shr 8) & $FF
					fltTopBlue = intTopPixel & $FF

					'add color stain and alpha modification
					If p_intAlpha < 255 Then fltTopAlpha = (fltTopAlpha * p_intAlpha) / 255
					If p_intStainRed < 255 Then fltTopRed = (fltTopRed * p_intStainRed) / 255
					If p_intStainGreen < 255 Then fltTopGreen = (fltTopGreen * p_intStainGreen) / 255
					If p_intStainBlue < 255 Then fltTopBlue = (fltTopBlue * p_intStainBlue) / 255

					'Get the bottom pixel at the offset location
					intBottomPixel = p_pixBottom.ReadPixel(intOffsetX, intOffsetY)

					'Split pixel into Alpha channel
					fltBottomAlpha = (intBottomPixel Shr 24) & $FF

					'if bottom pixel is totally transparent, there is no need to composite pixels
					If fltBottomAlpha = 0 
						intCompositePixel = (Int(fltTopAlpha) Shl 24) | (Int(fltTopRed) Shl 16) | (Int(fltTopGreen) Shl 8) | Int(fltTopBlue)
					Else
						'precalculate alpha ratios 
						fltAlphaRatio = fltTopAlpha / 255
						fltInverseAlphaRatio = 1 - fltAlphaRatio

						'Split pixel into Red, Green, and Blue channels
						fltBottomRed = (intBottomPixel Shr 16) & $FF
						fltBottomGreen = (intBottomPixel Shr 8) & $FF
						fltBottomBlue = intBottomPixel & $FF

						'Perform alpha-composite 
						intCompositeRed = (fltInverseAlphaRatio * fltBottomRed) + (fltAlphaRatio * fltTopRed)
						intCompositeGreen = (fltInverseAlphaRatio * fltBottomGreen) + (fltAlphaRatio * fltTopGreen )
						intCompositeBlue = (fltInverseAlphaRatio * fltBottomBlue) + (fltAlphaRatio * fltTopBlue)
						intCompositeAlpha = (fltInverseAlphaRatio * fltBottomAlpha) + fltTopAlpha

						'combine all channels back into a pixel
						intCompositePixel = (intCompositeAlpha Shl 24) | (intCompositeRed Shl 16) | (intCompositeGreen Shl 8) | intCompositeBlue
					EndIf
				EndIf
				
				'write the pixel to the pixmap at the offset location
				pixComposite.WritePixel(intOffsetX, intOffsetY, intCompositePixel)
			EndIf
		Next
	Next
	Return pixComposite 
EndFunction

'StackPixmapsFast is much faster than StackPixmapsPrecise, but color values may be off by 0.2% at most.
'p_pixBottom: TPixmap imitating the pixmap drawn first, behind the top pixmap
'p_pixTop: TPixmap imitating the pixmap drawn second, on top of the first pixmap
'p_intAlpha: Optional. Value between 0 and 255 indicating transparency in addition to any transparency already in the image. Imitates SetAlpha(a/255) command. Only effects the top pixmap.
'p_intStainRed, p_intStainGreen, and p_intStainBlue: Optional. values between 0 and 255 indicating the colorization to be added to the image. Imitates SetColor(r, g, b) command. Only effects the top pixmap.
'p_intHorizontalOffset and p_intVerticalOffset: Optional. Shifts the top pixmap horizontally and vertically relative to the bottom pixmap. Negative values can be specified. Any areas of the top pixmap that are beyond the width or height of the bottom pixmap will be clipped off.
'p_blnFlipVertical and p_blnMirrorHorizontal: Optional. If "True", the image will be flipped about the X-axis and Y-axis, respectivly. Imitates XFlipPixmap() and YFlipPixmap() commands. only efects the top pixmap.
'p_intRotaiton: Optional. Values of 90, 180, or 270 will rotate the image by 90 degrees, 180 degrees, and 270 degrees, respectivly. Imitates SetRotation() command. Value must be a multiple of 90. Only effects the top pixmap.
Function StackPixmapsFast:TPixmap(p_pixBottom:TPixmap, p_pixTop:TPixmap, p_intAlpha:Int = 255, p_intStainRed:Int = 255, p_intStainGreen:Int = 255, p_intStainBlue:Int = 255, p_intHorizontalOffset:Int = 0, p_intVerticalOffset:Int = 0, p_blnFlipVertical:Byte = False, p_blnMirrorHorizontal:Byte = False, p_intRotation:Int = 0)
	'Initialize variables
	Local intLoopX:Int, intLoopY:Int 
	Local intOffsetX:Int, intOffsetY:Int, intInverseAlphaRatio:Int
	
	Local intTopPixel:Int, intTopRed:Int, intTopGreen:Int, intTopBlue:Int, intTopAlpha:Int
	Local intBottomPixel:Int, intBottomRed:Int, intBottomGreen:Int,	intBottomBlue:Int, intBottomAlpha:Int
	Local intCompositePixel:Int, intCompositeRed:Int, intCompositeGreen:Int, intCompositeBlue:Int, intCompositeAlpha:Int
	
	Local intFMR:Int = FMRIndex(p_blnFlipVertical, p_blnMirrorHorizontal, p_intRotation)
	Local intMaxX:Int = p_pixTop.Width - 1
	Local intMaxY:Int = p_pixTop.Height - 1
	Local intBoundsWidth:Int = p_pixBottom.Width
	Local intBoundsHeight:Int = p_pixBottom.Height
	Local pixComposite:TPixmap = p_pixBottom.Copy()

	'Loop through each pixel
	For intLoopX = 0 To intMaxX
		For intLoopY = 0 To intMaxY

			'calculate offset location for later reference
			intOffsetX = intLoopX + p_intHorizontalOffset
			intOffsetY = intLoopY + p_intVerticalOffset

			'skip this pixel if it lies outside the boundaries of the bottom pixmap
			If intOffsetX < intBoundsWidth And intOffsetY < intBoundsHeight And intOffsetX >= 0 And intOffsetY >= 0

				'Get the top pixel at the current location, taking into account flip, mirror and rotation
				Select intFMR
					Case 0
						intTopPixel = p_pixTop.ReadPixel(intLoopX, intLoopY)

					Case 1
						intTopPixel = p_pixTop.ReadPixel(intMaxX - intLoopX, intLoopY)

					Case 2
						intTopPixel = p_pixTop.ReadPixel(intLoopX, intMaxY - intLoopY)

					Case 3
						intTopPixel = p_pixTop.ReadPixel(intMaxX - intLoopX, intMaxY - intLoopY)

					Case 4
						intTopPixel = p_pixTop.ReadPixel(intLoopY, intMaxX - intLoopX)

					Case 5
						intTopPixel = p_pixTop.ReadPixel(intMaxY - intLoopY, intLoopX)

					Case 6
						intTopPixel = p_pixTop.ReadPixel(intMaxY - intLoopY, intMaxX - intLoopX)

					Default '7
						intTopPixel = p_pixTop.ReadPixel(intLoopY, intLoopX)
				EndSelect 

				'Split pixel into Alpha channel
				intTopAlpha = (intTopPixel Shr 24) & $FF

				'if no alpha or stain modifications are being done and the top pixel is totally opaque, there is no need to composite pixels
				If (intTopAlpha & p_intStainRed & p_intStainGreen & p_intStainBlue & p_intAlpha) = 255
					pixComposite.WritePixel(intOffsetX, intOffsetY, intTopPixel)
				Else
					'Split pixel into Red, Green, and Blue channels 
					intTopRed = ((intTopPixel Shr 16) & $FF)
					intTopGreen = ((intTopPixel Shr 8) & $FF)
					intTopBlue = (intTopPixel & $FF)

					'add color stain and alpha modification
					If p_intAlpha < 255 Then intTopAlpha = (intTopAlpha * (p_intAlpha + 1)) Shr 8
					If p_intStainRed < 255 Then intTopRed = (intTopRed * (p_intStainRed + 1)) Shr 8
					If p_intStainGreen < 255 Then intTopGreen = (intTopGreen * (p_intStainGreen + 1)) Shr 8
					If p_intStainBlue < 255 Then intTopBlue = (intTopBlue * (p_intStainBlue + 1)) Shr 8 

					'Get the bottom pixel at the offset location
					intBottomPixel = p_pixBottom.ReadPixel(intOffsetX, intOffsetY)

					'Split pixel into Alpha channel
					intBottomAlpha = (intBottomPixel Shr 24) & $FF

					'if bottom pixel is totally transparent, there is no need to composite pixels
					If intBottomAlpha = 0 
						pixComposite.WritePixel(intOffsetX, intOffsetY, (intTopAlpha Shl 24) | (intTopRed Shl 16) | (intTopGreen Shl 8) | intTopBlue)
					Else
						'precalculate alpha ratios 
						intInverseAlphaRatio = (intTopAlpha ~ $FF)

						'Split pixel into Red, Green, and Blue channels
						intBottomRed = ((intBottomPixel Shr 16) & $FF) + 1
						intBottomGreen = ((intBottomPixel Shr 8) & $FF) + 1
						intBottomBlue = (intBottomPixel & $FF) + 1

						'Perform alpha-composite 
						intCompositeRed = ((intInverseAlphaRatio * intBottomRed) + (intTopAlpha * intTopRed)) Shr 8
						intCompositeGreen = ((intInverseAlphaRatio * intBottomGreen) + (intTopAlpha * intTopGreen)) Shr 8
						intCompositeBlue = ((intInverseAlphaRatio * intBottomBlue) + (intTopAlpha * intTopBlue)) Shr 8
						intCompositeAlpha = (intInverseAlphaRatio * intBottomAlpha) + intTopAlpha

						'combine all channels back into a pixel and write it to the composite pixmap
						pixComposite.WritePixel(intOffsetX, intOffsetY, (intCompositeAlpha Shl 24) | (intCompositeRed Shl 16) | (intCompositeGreen Shl 8) | intCompositeBlue)
					EndIf
				EndIf
			EndIf
		Next
	Next
	Return pixComposite 
EndFunction

'Since some combinations of XFlip, YFlip and Rotation are equivalent, this function returns the index for the simplest combination of the three for faster processing.
'p_blnFlipVertical: If "True", indicates a flip about the X-axis.
'p_blnMirrorHorizontal: If "True", indicates a flip about the Y-axis.
'p_intRotation: Indicates a rotation by the specified amount in degrees. Angle must be 0, 90, 180 or 270. Any other angles default to 270 degrees.
Function FMRIndex:Int(p_blnFlipVertical:Byte = False, p_blnMirrorHorizontal:Byte = False, p_intRotation:Int = 0)
	If p_blnFlipVertical
		If p_blnMirrorHorizontal
			Select p_intRotation
				Case 0
					'Flip + Mirror = Rotate 180 Degrees
					Return 3 

				Case 90
					'Flip + Mirror + Rotate 90 degrees = Rotate 270 degrees
					Return 5

				Case 180
					'Flip + Mirror + Rotate 180 degrees = Nothing
					Return 0

				Default '270
					'Flip + Mirror + Rotate 270 degrees = Rotate 90 degrees
					Return 4
			EndSelect
		Else
			Select p_intRotation
				Case 0
					'Flip = Mirror + Rotate 180 Degrees
					Return 2

				Case 90
					'Flip + Rotate 90 degrees = Mirror + Rotate 270 degrees
					Return 7

				Case 180
					'Flip + Rotate 180 degrees = Mirror
					Return 1

				Default '270
					'Flip + Rotate 270 degrees = Mirror + Rotate 90 degrees 
					Return 6
			EndSelect
		EndIf
	Else
		If p_blnMirrorHorizontal
			Select p_intRotation
				Case 0
					'Mirror = Flip + Rotate 180 degrees
					Return 1

				Case 90
					'Mirror + Rotate 90 Degrees = Flip + Rotate 270 degrees
					Return 6

				Case 180
					'Mirror + Rotate 180 Degrees = Flip
					Return 2

				Default '270
					'Mirror + Rotate 270 Degrees = Flip + Rotate 90 degrees
					Return 7
			EndSelect
		Else
			Select p_intRotation
				Case 0
					'Nothing = Flip + Mirror + Rotate 180 degrees
					Return 0

				Case 90
					'Rotate 90 Degrees = Flip + Mirror + Rotate 270 degrees
					Return 4

				Case 180
					'Rotate 180 Degrees = Flip + Mirror
					Return 3

				Default '270
					'Rotate 270 Degrees = Flip + Mirror + Rotate 90 degrees
					Return 5
			EndSelect
		EndIf
	EndIf
EndFunction

Comments

None.

Code Archives Forum