Hey I'm glad you all enjoy this code, but I don't recognize it at the moment. For people like me who are confused, the problem is that if a hBitmap has transparent pixels, they will be converted to black using the built in function.
Problem: Transparent pixels are 0x00000000. Alpha = 0x00, Red = 0x00, Green = 0x00, Blue = 0x00. In conversion, I assume the alpha channel is dropped. So the transparent pixel becomes 0x000000 which is black.
Solution: We know a Bitmap can hold 32 bits of color. So we should be able to preserve transparency.
ARGB = Alpha, Red, Green, Blue. Also known as 32-bit color.
pARGB = premultiplied ARGB. This is 24-bit data. The alpha channel has been multiplied into the color channels. Red = Alpha x Red. Green = Alpha x Green. Blue = Alpha * Blue.
Bitmap = pBitmap, pointer to a bitmap. This has two layers, one layer of struct data, and one layer of actual pixel colors. This is a flexable approach to holding information because the 1st layer can describe what information it is holding, and the second layer can be any data.
hBitmap = handle to a bitmap. This also has 2 layers, but the 1st layer contains limited information, only enough to convert a 1-dimensional array of pixels into a 2D image by storing the width
, and some other small numbers. The reason for this is because an hBitmap is for displaying a graphical user interface, so your monitor only has red, green, and blue pixels. It does not have an "alpha" pixel. So the pixel format of all hBitmaps is pARBB, and is 24-bit data.
pBits = pointer to the bits. This is the layer of actual pixel data.
DIB = Device independent bitmap = This is a hBitmap
with the pixel data stored on memory. Memory = your computer's RAM. A long time ago, memory was expensive. So your computer monitor has a buffer (memory) that it uses to hold the image briefly before drawing onto the screen. Why not use your monitor's memory and save money? That's called a device dependent bitmap or DDB for short. Today, your printer still uses DDB. And you can access the memory buffer on your printer from your computer!
1) We have a handle to our image, and we want to convert that handle to a pointer. The reason why we do this is because a pointer is global (can be transferred to more processes) while a handle is opaque, and probably has some limitations.
2) Extract some information from our handle. Call GetObject
and get width, height, and bpp. (bits per pixel)
3) Now the most straightforward approach would be to find where the pixel values are in our hBitmap and memcpy the array of pixels to the pBitmap. This fails. Reasons #1 - The hBitmap could be bottom-up or top-down
. #2 - hBitmap is pARGB (24-bit data) and ARGB is 32-bit data.
4) We could just code everything in C, but we are going to avoid super low level stuff and be smart. We'll use some tricks. The first trick is to determine whether a bitmap is bottom-up or top-down
. The standard approach is to change the value of the first pixel value in our data to a salmon colored pink and call GetPixel
. If we get a salmon colored pink, then it's top down. If not it's bottom up. We won't do this. Instead, we'll create a new DIB (Device independent bitmap) which is the same thing as an hBitmap where the pixels are stored on local memory, and copy the pixels using BitBlt
. If our newly created DIB has is initialized with a negative height, then the pixels will be stored top-down. top-down is what a normal person should expect, like reading a book. Imagine reading a book from the last sentence on the page and making your way up. That's called bottom-up and how windows normally stores pixel data. That's because some mathematicians were really concerned about purity and ideology. You know how a xy graph looks like? x-axis and y-axis? Yeah, the mathematicians wanted to use that model.
5) For the second trick we'll use something special, a hidden flag in GdipBitmapLockBits
that let's us determine where the return data will be buffered. You see, we get to choose where the output of the function will be, and I choose to put it where my DIB is. Importantly, I set another flag to display the output data as PARGB, the same format as a DIB.
6) Do the following in order: Create a DIB. Create a Bitmap. Lock the Bitmap, and point the output to the DIB. Use Trick #1 - copy the pixels to the DIB, erasing any top-down/bottom-up distinctions. Use Trick #2 - Unlock the bits, which copies the 24-bit pARGB data in a DIB into 32-bit ARGB pixels.
7) Clean up and return the bitmap.