65.9K
CodeProject is changing. Read more.
Home

Applying a texture to a bitmap

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.96/5 (20 votes)

Aug 31, 2003

viewsIcon

91973

Shows you how to apply a texture to a bitmap.

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:

   ' REMEMBER to add this to the class file that contains 
    ' this code: Imports System.Drawing.Imaging

    ' Modifies the ORIGINAL bitmap
    ' textureTransparency  has to be between 0 and 1, 
    ' with 0 being the least transparent, and 1 the most transparent 
    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

        ' Convert the texture to grayscale before using it
        MakeImageGrayscale(texture)

        Dim x, y, alpha As Integer

        ' Adjust the alpha value of each pixel in the texture bitmap.
        ' The white-most pixels will have the the 
        ' most transparency (highest alpha), 
        ' and the dark-most pixels will have the least transparency.
        For x = 0 To texture.Width - 1
            For y = 0 To texture.Height - 1
                Dim c As Color = texture.GetPixel(x, y)
                ' c.R -> all of the RGB values are the same 
                ' since the image is in grayscale
                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)

        ' Draw the texture over the original bitmap
        gr.FillRectangle(myBrush, bmp.GetBounds(GraphicsUnit.Pixel))
        myBrush.Dispose()
        gr.Dispose()
    End Sub

    ' Converts the image to grayscale
    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)
        ' Apply the grayscale image attribute
        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:

// REMEMBER to add this to the class file that contains your code
// using System.Drawing.Imaging;

// Modifies the ORIGINAL bitmap
// textureTransparency  has to be between 0 and 1, 
// with 0 being the least transparent, and 1 the most transparent 
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.");

    // Convert the texture to grayscale before using it
    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; 
    // Adjust the alpha value of each pixel in the texture bitmap.
    // The white-most pixels will have the the 
    // most transparency (highest alpha), 
    // and the dark-most pixels will have the least transparency.
    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] is alpha  -  array is BGRA (blue, green , 
                // red, alpha)
                p[3] = (byte)(p[0] * textureTransparency);
                p += 4; // Move to the next pixel
            }
            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();
}



// Converts the image to grayscale
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);
    // Apply the grayscale image attribute
    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 :)