Click here to Skip to main content
6,935,055 members and growing! (14,056 online)
Email Password   helpLost your password?
Multimedia » General Graphics » Bitmaps     Intermediate License: The Code Project Open License (CPOL)

Bitmap Alpha Layer Editor

By pierre poliakoff

This progam allows to add an alpha layer to an existing bitmap.
C#1.0, C#2.0, C#3.0, .NET, GDI+, Dev
Revision:2 (See All)
Posted:26 Jul 2009
Views:6,777
Bookmarked:21 times
printPrint Friendly   add Share
      Discuss Discuss   Broken Article?Report  
10 votes for this article.
Popularity: 4.95 Rating: 4.95 out of 5

1

2

3
1 vote, 10.0%
4
9 votes, 90.0%
5

Introduction

While I was developping an XNA game, I found that XNA sprites use alpha blending to define the transparent parts in the sprite bitmap. Since I had no tool to edit the alpha layer of a bitmap, I wrote this small utility in C#.

Background

Most of the image editors (e.g., Microsoft Paint) allow you to edit bitmaps and consequently choose a color for each pixel. This color is defined by three values: Red, Green, Blue.

Examples:

  • Red=255, Green=0, Blue=0 is a pure red pixel,
  • Red=0, Green=0, Blue=0 is a black pixel,

and so on.

For applications that use alpha blending, a fourth value is added to each pixel. The alpha value defines the transparency:

  • Alpha=255 is opaque
  • Alpha=127 is partially transparent
  • Alpha=0 is fully transparent

A bitmap file (BMP or PNG) may include an alpha layer, but most of the time it does not.

Using the application

The application window shows three bitmaps:

  1. The top left image is the original bitmap as loaded from the disk
  2. The top right image is the mask that will be used to define the alpha layer (important: this bitmap must have the same size as the original bitmap)
  3. The bottom image is the result of applying the mask on the original

The mask dark parts are transparent and the light parts are opaque. If your mask was drawn the other way, use the "invert mask" checkbox to perform an inversion of the mask image.

If you don't have a separate mask file, use the checkbox "Use Loaded Image as Mask", and the program will automatically create a mask by making a gray level bitmap from the loaded image. You can always edit this automatic mask by saving it (press the "Save mask" button), editing it with your favorite bitmap editor, unchecking the checkbox "Use Loaded Image as Mask", and loading back your edited mask.

Most of the time, I don't use partial transparence. So, I uncheck the "Allow partial opacity" check box. This forces the mask to become a two color (black and white) image instead of a grey scale image. The threshold that defines the limit between black and white is adjustable.

How it works

Writing into the bitmap alpha layer

The programs first copies the original image into the object bitmap maskedImage and then performs a copy pixel per pixel of mask (the object bitmap maskImage) into the alpha layer of maskedImage.

To access the pixels, we need a managed byte vector of both the bitmap pixels.

BitmapData bmpData1 = maskedImage.LockBits(new Rectangle(0, 0, maskedImage.Width, 
                            maskedImage.Height), 
                            System.Drawing.Imaging.ImageLockMode.ReadWrite, 
                            maskedImage.PixelFormat);

byte[] maskedImageRGBAData = new byte[bmpData1.Stride * bmpData1.Height];

System.Runtime.InteropServices.Marshal.Copy(bmpData1.Scan0, 
                                            maskedImageRGBAData,
                                            0, 
                                            maskedImageRGBAData.Length);

This creates the byte vector bmpData1 that contains the pixel data for the bitmap maskedImage.

BitmapData bmpData2 = maskImage.LockBits(new Rectangle(0, 0, maskImage.Width,
                          maskImage.Height),
                          System.Drawing.Imaging.ImageLockMode.ReadOnly, 
                          maskImage.PixelFormat);

byte[] maskImageRGBAData = new byte[bmpData2.Stride * bmpData2.Height];

System.Runtime.InteropServices.Marshal.Copy(bmpData2.Scan0,
                                            maskImageRGBAData,
                                            0, 
                                            maskImageRGBAData.Length);

This creates the byte vector bmpData2 that contains the pixel data for the bitmap maskImage.

Important note: the internal pixels storage used by the .NET Bitmap objects when the pixel format is PixelFormat.Format32bppArgb has the following unusual sequence:

byte 0: Blue value of pixel 1
Byte 1: Green value of pixel 1
Byte 2: Red value of pixel 1
Byte 3: Alpha value of pixel 1
byte 4: Blue value of pixel 2
Byte 5: Green value of pixel 2
Byte 6: Red value of pixel 2
Byte 7: Alpha value of pixel 2
...

Once we have the two vectors, we simply copy the value of the blue component from the mask vector to the alpha component of the masked bitmap vector:

//copy the mask to the Alpha layer

for (int i = 0; i + 2 < maskedImageRGBAData.Length; i += 4)
{
    maskedImageRGBAData[i + 3] = maskImageRGBAData[i];
}

Then, we copy back the pixel information to the masked image and we won't forget to unlock the unmanaged internal part of the bitmap objects.

System.Runtime.InteropServices.Marshal.Copy(maskedImageRGBAData, 0, 
               bmpData1.Scan0, maskedImageRGBAData.Length);
this.maskedImage.UnlockBits(bmpData1);
this.maskImage.UnlockBits(bmpData2);

Converting any bitmap to the 32 bits per pixel RGBA format

Since the bitmap loaded can have any pixel format and since the program expects a PixelFormat.Format32bppArgb format, we have to convert the bitmap format. This is done by creating a new bitmap of the same size and drawing the original bitmap into it.

// declare the new image 
Bitmap returnedImage = new Bitmap(tmpImage.Width,
                                  tmpImage.Height,
                                  PixelFormat.Format32bppArgb);

// create a graphics instance to draw the original image in the new one
Rectangle rect = new Rectangle(0, 0, tmpImage.Width, tmpImage.Height)
Graphics g = Graphics.FromImage(returnedImage);

// draw the original image 
g.DrawImage(tmpImage, rect, 0, 0, tmpImage.Width, tmpImage.Height,GraphicsUnit.Pixel);

//Release the graphics object
g.Dispose();

Note: This function has a positive side effect: for optimization reasons, when you create a bitmap instance from a file, the .NET framework keeps the file open as long as the bitmap is used. This mean you get an exception when you try to write back to the bitmap file. Since the function above creates a totally new bitmap independent of the original bitmap loaded from the file, the .NET framework closes the file and it becomes possible to overwrite it.

Extracting a mask from a bitmap

The mask is a black and white or grayscale image made from a loaded image. This is achieved by directly manipulating the pixel information. This is done as explained above in the section "Writing into the bitmap alpha layer", except that here we set the red, green, blue values to the greyValue obtained by the following code:

for (int i = 0; i + 2 < maskImageRGBData.Length; i += 4)
{
     //convert to gray scale R:0.30 G=0.59 B 0.11
     greyLevel = (byte)(0.3 * maskImageRGBData[i + 2] + 0.59 * 
        maskImageRGBData[i + 1] + 0.11 * maskImageRGBData[i]);

     if (opaque)
     {
         greyLevel = (greyLevel < OpacityThreshold) ? byte.MinValue : byte.MaxValue;
     }
     if (invertedMask)
     {
         greyLevel = (byte)(255 - (int)greyLevel);
     }

     maskImageRGBData[i] = greyLevel;
     maskImageRGBData[i + 1] = greyLevel;
     maskImageRGBData[i + 2] = greyLevel;
}

Note: This loop is executed tons of times. It must execute as fast as possible. That's why we use the variables opaque and invertedMask instead of losing precious microseconds in calling the accessors this.checkBoxAllowPartialOpacity.Checked and this.checkBoxInvertMask.Checked on each iteration.

Forcing the alpha layer to zero

When the opened bitmap files already have an alpha layer defined, we need to reset it to full opacity. We could have done it as explained above by changing each byte of the alpha layer to 255, but the .NET framework offers a nicer solution: the class ImageAttributes allows to perform the following operation on each pixel:

  • Red = M00*Red + M10*Green + M20*Blue + M30*Alpha + M40 *255
  • Green = M01*Red + M11*Green + M21*Blue + M30*Alpha + M41 *255
  • Blue = M02*Red + M12*Green + M22*Blue + M30*Alpha + M42 *255
  • Alpha = M03*Red + M13*Green + M23*Blue + M30*Alpha + M43 *255

This means that the following matrix will keep the colors unchanged and force the alpha layer values to 255.

float[][] colorMatrixElements = { 
                    new float[] {1,0,0,0,0},
                    new float[] {0,1,0,0,0},
                    new float[] {0,0,1,0,0},
                    new float[] {0,0,0,0,0},
                    new float[] {0,0,0,1,1}};

To use this matrix, we associate it to a ColorMatrix and then we associate this matrix to an ImageAttribute:

ColorMatrix colorMatrix = new ColorMatrix(colorMatrixElements);
ImageAttributes imageAttributes = new ImageAttributes();

imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default,
                               ColorAdjustType.Bitmap);

And then, use the imageAttribute to specify the way the image must be drawn:

g.DrawImage(tmpImage, rect, 0, 0, tmpImage.Width, tmpImage.Height, 
            GraphicsUnit.Pixel, imageAttributes);

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

pierre poliakoff


Member

Occupation: Team Leader
Location: Belgium Belgium

Other popular General Graphics articles:

  • A flexible charting library for .NET
    Looking for a way to draw 2D line graphs with C#? Here's yet another charting class library with a high degree of configurability, that is also easy to use.
  • CxImage
    CxImage is a C++ class to load, save, display, transform BMP, JPEG, GIF, PNG, TIFF, MNG, ICO, PCX, TGA, WMF, WBMP, JBG, J2K images.
  • 3D Pie Chart
    A class library for drawing 3D pie charts.
  • Barcode Image Generation Library
    This library was designed to give an easy class for developers to use when they need to generate barcode images from a string of data.
  • ImageStone
    An article on a library for image manipulation.
Article Top
 
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 4 of 4 (Total in Forum: 4) (Refresh)FirstPrevNext
Generalgood effort Pinmembera_pess4:54 5 Sep '09  
GeneralCopy & paste PinmemberAlberto Bencivenni3:40 28 Jul '09  
GeneralIt's nice PingroupMd. Marufuzzaman8:35 26 Jul '09  
GeneralRe: It's nice PinsupporterPaul Selormey15:43 26 Jul '09  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+PgUp/PgDown to switch pages.

PermaLink | Privacy | Terms of Use
Last Updated: 26 Jul 2009
Editor: Smitha Vijayan
Copyright 2009 by pierre poliakoff
Everything else Copyright © CodeProject, 1999-2010
Web11 | Advertise on the Code Project