Creating a masked sprite

Blitz3D Forums/Blitz3D Beginners Area/Creating a masked sprite

PowerPC603(Posted 2009) [#1]
Hi guys,

I've tried everything I can think of (AFAIK) and cannot find the solution.

I want to make a sprite and a texture where black is transparant and everything else opaque.

; This routine creates a sprite in the center of the playfield with the given text on it
Function CreateCenterSprite(T$)
	; Calculate the width and height of the text
	Local Width = StringWidth(T$) : Local Height = StringHeight(T$)
	; Create the sprite and it's texture
	Local Sprite = CreateSprite()
	; Create a texture
	Local Tex = CreateTexture(128, 64)
	; Put the texture onto the sprite
	EntityTexture Sprite, Tex
	; Position and scale the sprite
	PositionEntity Sprite, 75, 60, -10
	ScaleSprite Sprite, 20, 10

	; Set the drawing buffer to the spritetexture
	SetBuffer TextureBuffer(Tex)
	; Make the background black
	ClsColor 0, 0, 0 : Cls
	; Set drawing color to white
	Color 255, 255, 255
	; Print the text onto the texture (in the center of the texture)
	Text (128 / 2) - (Width / 2), (64 / 2) - (Height / 2), T$
	; Set the drawing buffer back to the backbuffer
	SetBuffer BackBuffer()
	; Mask the texture (make all black pixels see-through)
	MaskTexture(Tex)
	; Free the texture
	FreeTexture Tex

	; Return the sprite to the calling routine
	Return Sprite
End Function

; This routine will give an alpha of 0 (completely see-through) to any black pixels in the given texture (the rest will remain opaque)
Function MaskTexture(Texture)
	Local NewRGB

	; Lock the texturebuffer (required to use ReadPixelFast and WritePixelFast)
	LockBuffer TextureBuffer(Texture)

	; Loop through all pixels in the texture
	For y = 0 To TextureHeight(texture) - 1
		For x = 0 To TextureWidth(texture) - 1
			; Read the RGB value from the texture
			RGB1 = ReadPixelFast(x, y, TextureBuffer(Texture))
			; Separate the Red, Green and Blue values, also Alpha
			Red = (RGB1 And $FF0000) Shr 16
			Green = (RGB1 And $FF00) Shr 8
			Blue = RGB1 And $FF
			Alpha = (RGB1 And $FF000000) Shr 24

			; If the color is black (Red=0, Green=0, Blue=0)
			If (Red = 0) And (Green = 0) And (Blue = 0) Then
				; Set the pixel completely alpha'd (invisible)
				Alpha = 0
			Else
				; Set the pixel opaque (visible)
				Alpha = 255
			EndIf

			; Combine the ARGB back into one value
			NewRGB = (Alpha Shl 24) Or (Red Shl 16) Or (Green Shl 8) Or Blue

			; Re-write the pixel with it's new alpha-value
			WritePixelFast(x, y, NewRGB, TextureBuffer(Texture))
		Next
	Next

	; Unlock the texturebuffer
	UnlockBuffer TextureBuffer(Texture)
End Function


I did some searching and found the MaskTexture function somewhere in the code-archives, but I've simplified it to suit my needs (I didn't need some extra parameters to include tolerance).

In the CreateCenterSprite function, If I use "CreateTexture(128, 64)", then everything remains visible, also the black parts (see picture below).
If I use "CreateTexture(128, 64, 2+1)" or "CreateTexture(128, 64, 4+1)", my texture is completely transparent (I don't see anything of my sprite).

If I use "CreateTexture(128, 64, 2+1)" or "CreateTexture(128, 64, 4+1)", and the call to MaskTexture is commented, then my sprite appears completely black (the text isn't shown anymore, just a black rectangular sprite).

How can I keep my (white) text visible and make the black background completely transparent?

The CreateCenterSprite is used to create the sprite in the middle of the playfield that shows: "Level: 1".
As you can see, it's shown completely. I want the black parts to be transparent.




Warner(Posted 2009) [#2]
The combination of creating the texture using the 1+2 flags and MaskTexture should work. I remember that when using sprites, I encountered the same problem.
I worked around that by loading an empty sprite using LoadImage with the 1+2 flags.
When I wanted to test this out, I noticed my machine doesn't create sprites at all. When I use LoadSprite, I get a MAV on Renderworld.
So I had to test it with a cube instead of a sprite. That seems to work as expected, maybe you can give it a try as well.

If so, you could perhaps decide to use a Quad mesh instead of a sprite.


PowerPC603(Posted 2009) [#3]
I tried creating my own sprite, using the CreateMesh/CreateSurface/AddVertex/AddTriangle method, but the result was the same.

Your example wrote the text to the BackBuffer() and you copyrected the part you needed onto the texture.
This gave me an idea.

I now tried to create an image first, draw my text onto the image and copyrect the image to the texture.
And finally using the MaskTexture function.

This worked flawlessly.

I guess Blitz has problems doing any drawing onto a texture directly.
Especially Text as I've experienced other problems in that area.

Thanks for helping out Warner.

; This routine creates a sprite in the center of the playfield with the given text on it
Function CreateCenterSprite(T$)
	; Calculate the width and height of the text
	Local Width = StringWidth(T$) : Local Height = StringHeight(T$)
	; Create the sprite and it's texture
	Local Sprite = CreateSprite()
	Local Tex = CreateTexture(128, 64, 2+1)
	Local Image = CreateImage(128, 64)
	; Position and scale the sprite
	PositionEntity Sprite, 75, 60, -10
	ScaleSprite Sprite, 20, 10

	; Set the drawing buffer to the image
	SetBuffer ImageBuffer(Image)
	; Make the background black
	ClsColor 0, 0, 0 : Cls
	; Set drawing color to white
	Color 255, 255, 255
	; Print the text onto the image (in the center of the image)
	Text (128 / 2) - (Width / 2), (64 / 2) - (Height / 2), T$
	; Set the drawing buffer back to the backbuffer
	SetBuffer BackBuffer()
	; CopyRect the entire image to the texture
	CopyRect 0, 0, 128, 64, 0, 0, ImageBuffer(Image), TextureBuffer(Tex)
	; Mask the texture (make all black pixels see-through)
	MaskTexture(Tex)
	; Put the texture onto the sprite and free the texture and the image
	EntityTexture Sprite, Tex
	FreeTexture Tex
	FreeImage Image

	; Return the sprite to the calling routine
	Return Sprite
End Function



Ross C(Posted 2009) [#4]
I have a routine in my archive entries. You simply pass across a texture to it and a colour and it masks off any pixels/texels of that colour:

http://www.blitzbasic.com/codearcs/codearcs.php?code=1013


PowerPC603(Posted 2009) [#5]
Yes Ross, I searched the code archives and found your code.

The MaskTexture() function in my first post was originally your code, slightly adjusted to exclude tolerance.


Ross C(Posted 2009) [#6]
Ah sorry :S It was late :D Glad you got it sorted!


PowerPC603(Posted 2009) [#7]
I sorted it out after a few hours of breaking my head over this problem.

The only solution was to create an image, write all text/lines/rects/... to this image and then copyrect that image onto the texture.
And finally using the MaskTexture function to make parts of the texture which use a certain color see-through (alpha'd).

Writing directly onto the texture doesn't work properly.