
Introduction
When I wrote this function my problem was to replace one color by another on transparent bitmaps. My images were resources bitmaps, which I store in an ImageList
for easy transparency.
There is no easy way to directly access a bitmap's pixel on Win32. If you're interested in doing that, this article may help you understand the usage of CreateDIBSection
.
- If you have to load a bitmap from a resource and make many color replacements, or if you have to change a color in a
HBITMAP
, this function is for you.
- If you have a bitmap in a resource and want to replace one or more colors on load, it's better to use
CreateMappedBitmap
. You can find in the sample
program a ReplaceColor
function which uses CreateMappedBitmap
.
I've made the same code using only BitBlt
. BitBlt
is really fast but the creation of the mask bitmap is so slow that the whole
function is twice slower than the code using CreateDIBSection
(sources are in the sample).
Limitations
My code always return a 32 bit bitmap. If you need to keep your original bitmap bit per pixels, you'll have two options:
- You can rewrite it with
GetDIBits
. You will need special code for monochromes to 32 bit bitmaps.
- You can
BitBlt
the 32 bit bitmap to your less color DC. It's easy, slow, and you will lose some colors in the process.
Sample program
The sample program you can download does the following:
- Creates a window in
WinMain
.
- Loads a bitmap from resources
WM_NCCREATE
.
- Adds this bitmap to an
ImageList
WM_NCCREATE
.
- Uses
ReplaceColor
to get a copy of the bitmap with one color replaced by another (4 times) WM_NCCREATE
.
- Displays all the stored images in the window
WM_PAINT
.
- Cleans everything
WM_DESTROY
.
Easy to use
A short sample of the usage of ReplaceColor
:
HBITMAP hBmp2 = LoadBitmap(g_hinstance,MAKEINTRESOURCE(IDB_SAMPLEBITMAP));
HBITMAP hBmp = ReplaceColor(hBmp2,0xff0000,0x00ff00); DeleteObject(hBmp2);
DeleteObject(hBmp);
ReplaceColor source code
The ReplaceColor
function is a pure Win32 function. It doesn't make any use of MFC. Its code is standalone, you can cut and paste it without modifications to your code.
#define COLORREF2RGB(Color) (Color & 0xff00) | ((Color >> 16) & 0xff) \
| ((Color << 16) & 0xff0000)
HBITMAP ReplaceColor(HBITMAP hBmp,COLORREF cOldColor,COLORREF cNewColor,HDC hBmpDC)
{
HBITMAP RetBmp=NULL;
if (hBmp)
{
HDC BufferDC=CreateCompatibleDC(NULL); if (BufferDC)
{
HBITMAP hTmpBitmap = (HBITMAP) NULL;
if (hBmpDC)
if (hBmp == (HBITMAP)GetCurrentObject(hBmpDC, OBJ_BITMAP))
{
hTmpBitmap = CreateBitmap(1, 1, 1, 1, NULL);
SelectObject(hBmpDC, hTmpBitmap);
}
HGDIOBJ PreviousBufferObject=SelectObject(BufferDC,hBmp);
HDC DirectDC=CreateCompatibleDC(NULL); if (DirectDC)
{
BITMAP bm;
GetObject(hBmp, sizeof(bm), &bm);
BITMAPINFO RGB32BitsBITMAPINFO;
ZeroMemory(&RGB32BitsBITMAPINFO,sizeof(BITMAPINFO));
RGB32BitsBITMAPINFO.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
RGB32BitsBITMAPINFO.bmiHeader.biWidth=bm.bmWidth;
RGB32BitsBITMAPINFO.bmiHeader.biHeight=bm.bmHeight;
RGB32BitsBITMAPINFO.bmiHeader.biPlanes=1;
RGB32BitsBITMAPINFO.bmiHeader.biBitCount=32;
UINT * ptPixels;
HBITMAP DirectBitmap = CreateDIBSection(DirectDC,
(BITMAPINFO *)&RGB32BitsBITMAPINFO,
DIB_RGB_COLORS,
(void **)&ptPixels,
NULL, 0);
if (DirectBitmap)
{
HGDIOBJ PreviousObject=SelectObject(DirectDC, DirectBitmap);
BitBlt(DirectDC,0,0,
bm.bmWidth,bm.bmHeight,
BufferDC,0,0,SRCCOPY);
cOldColor=COLORREF2RGB(cOldColor);
cNewColor=COLORREF2RGB(cNewColor);
for (int i=((bm.bmWidth*bm.bmHeight)-1);i>=0;i--)
{
if (ptPixels[i]==cOldColor) ptPixels[i]=cNewColor;
}
SelectObject(DirectDC,PreviousObject);
RetBmp=DirectBitmap;
}
DeleteDC(DirectDC);
}
if (hTmpBitmap)
{
SelectObject(hBmpDC, hBmp);
DeleteObject(hTmpBitmap);
}
SelectObject(BufferDC,PreviousBufferObject);
DeleteDC(BufferDC);
}
}
return RetBmp;
}