Introduction
I thought this code might be useful to someone. I'm basically copy-pasting it from some post in VBForums (my own post). Here's what I do: I convert the texture to grayscale. The white-most pixels (highest r, g, or b value) will have the most transparency. The dark-most pixels will be the least transparent pixels when you are applying the texture.
My code may technically not be right, or not the most efficient way to do this. I just wanted to share the idea of how you can apply a texture. Hope this helps.
Details
Call ApplyTexture()
to apply a texture to your bitmap. Here's the code:
Public Shared Sub ApplyTexture(ByRef bmp As Bitmap, _
ByVal texture As Bitmap, _
ByVal textureTransparency As Single)
If (bmp Is Nothing) OrElse (texture Is Nothing) _
Then Throw New ArgumentNullException
If textureTransparency < 0 OrElse textureTransparency > 1 Then
Throw New ArgumentOutOfRangeException( _
"textureTransparency must be between 0 and 1.")
End If
MakeImageGrayscale(texture)
Dim x, y, alpha As Integer
For x = 0 To texture.Width - 1
For y = 0 To texture.Height - 1
Dim c As Color = texture.GetPixel(x, y)
alpha = CInt(c.R * textureTransparency)
c = Color.FromArgb(alpha, c)
texture.SetPixel(x, y, c)
Next
Next
Dim gr As Graphics = Graphics.FromImage(bmp)
Dim myBrush As New TextureBrush(texture)
gr.FillRectangle(myBrush, bmp.GetBounds(GraphicsUnit.Pixel))
myBrush.Dispose()
gr.Dispose()
End Sub
Public Shared Sub MakeImageGrayscale(ByVal bmp As Bitmap)
Dim cMatrix As New ColorMatrix(New Single()() _
{New Single() {0.299, 0.299, 0.299, 0, 0}, _
New Single() {0.587, 0.587, 0.587, 0, 0}, _
New Single() {0.114, 0.114, 0.114, 0, 0}, _
New Single() {0, 0, 0, 1, 0}, _
New Single() {0, 0, 0, 0, 1}})
Dim imageAttrib As New ImageAttributes
imageAttrib.SetColorMatrix(cMatrix)
Dim gr As Graphics = Graphics.FromImage(bmp)
gr.DrawImage(bmp, New Rectangle(0, 0, bmp.Width, bmp.Height), _
0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, imageAttrib)
gr.Dispose()
End Sub
I don't know how to program in C#, but I managed to convert that to C# and used pointers instead of SetPixel
and GetPixel
. I used one of Christian Graus's articles on CodeProject to figure out how to use pointers to read the RGB data from a bitmap, and changed that a bit around to be able to read ARGB values. I hope I did everything right :)
Here it is, faster than the VB version:
public static void ApplyTexture (Image img, Image texture,
float textureTransparency)
{
if ( (img ==null) || (texture==null) )
throw new ArgumentNullException();
if (textureTransparency < 0 || textureTransparency > 1)
throw new ArgumentOutOfRangeException(
"Value must be between 0 and 1.");
MakeImageGrayscale (texture);
Bitmap txtr = new Bitmap(texture);
BitmapData bmData = txtr.LockBits(new Rectangle(0, 0,
txtr.Width, txtr.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte * p = (byte *)(void *)Scan0;
int nOffset = stride - txtr.Width*4;
for (int y=0; y < txtr.Height; ++y)
{
for (int x=0; x < txtr.Width; ++x)
{
p[3] = (byte)(p[0] * textureTransparency);
p += 4;
}
p += nOffset;
}
}
txtr.UnlockBits(bmData);
Graphics gr = Graphics.FromImage(img);
TextureBrush myBrush = new TextureBrush(txtr);
gr.FillRectangle (myBrush,0,0,img.Width, img.Height);
myBrush.Dispose();
gr.Dispose();
txtr.Dispose();
}
public static void MakeImageGrayscale (Image img)
{
ColorMatrix cMatrix = new ColorMatrix (
new float[][] {
new float[] {0.299F, 0.299F, 0.299F, 0, 0},
new float[] {0.587F, 0.587F, 0.587F, 0, 0},
new float[] {0.114F, 0.114F, 0.114F, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 1, 0}});
ImageAttributes imageAttrib = new ImageAttributes();
imageAttrib.SetColorMatrix(cMatrix);
Graphics gr = Graphics.FromImage (img);
gr.DrawImage (img, new Rectangle(0, 0, img.Width, img.Height),
0, 0, img.Width, img.Height, GraphicsUnit.Pixel,
imageAttrib);
gr.Dispose();
}
Thanks Greg Ingelmo for correcting me :)