Click here to Skip to main content
Licence Public Domain
First Posted 3 Sep 2002
Views 190,353
Downloads 2,132
Bookmarked 64 times

How to replace a color in an HBITMAP

By | 5 Feb 2012 | Article
Replacing a color by another in transparent bitmaps.

Replace color screenshot

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); // replace blue by green
DeleteObject(hBmp2);

// Use your modified Bitmap here 

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)

//-------------------------------------------------------------------------------
// ReplaceColor
//
// Author    : Dimitri Rochette drochette@coldcat.fr
// Specials Thanks to Joe Woodbury for his comments and code corrections
//
// Includes  : Only <windows.h>

//
// hBmp         : Source Bitmap
// cOldColor : Color to replace in hBmp
// cNewColor : Color used for replacement
// hBmpDC    : DC of hBmp ( default NULL ) could be NULL if hBmp is not selected
//
// Retcode   : HBITMAP of the modified bitmap or NULL for errors
//
//-------------------------------------------------------------------------------
HBITMAP ReplaceColor(HBITMAP hBmp,COLORREF cOldColor,COLORREF cNewColor,HDC hBmpDC)
{
    HBITMAP RetBmp=NULL;
    if (hBmp)
    {
        HDC BufferDC=CreateCompatibleDC(NULL);    // DC for Source Bitmap
        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);
            // here BufferDC contains the bitmap
            
            HDC DirectDC=CreateCompatibleDC(NULL); // DC for working
            if (DirectDC)
            {
                // Get bitmap size
                BITMAP bm;
                GetObject(hBmp, sizeof(bm), &bm);

                // create a BITMAPINFO with minimal initilisation 
                // for the CreateDIBSection
                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;

                // pointer used for direct Bitmap pixels access
                UINT * ptPixels;    

                HBITMAP DirectBitmap = CreateDIBSection(DirectDC, 
                                       (BITMAPINFO *)&RGB32BitsBITMAPINFO, 
                                       DIB_RGB_COLORS,
                                       (void **)&ptPixels, 
                                       NULL, 0);
                if (DirectBitmap)
                {
                    // here DirectBitmap!=NULL so ptPixels!=NULL no need to test
                    HGDIOBJ PreviousObject=SelectObject(DirectDC, DirectBitmap);
                    BitBlt(DirectDC,0,0,
                                   bm.bmWidth,bm.bmHeight,
                                   BufferDC,0,0,SRCCOPY);

                       // here the DirectDC contains the bitmap

                    // Convert COLORREF to RGB (Invert RED and BLUE)
                    cOldColor=COLORREF2RGB(cOldColor);
                    cNewColor=COLORREF2RGB(cNewColor);

                    // After all the inits we can do the job : Replace Color
                    for (int i=((bm.bmWidth*bm.bmHeight)-1);i>=0;i--)
                    {
                        if (ptPixels[i]==cOldColor) ptPixels[i]=cNewColor;
                    }
                    // little clean up
                    // Don't delete the result of SelectObject because it's 
                    // our modified bitmap (DirectBitmap)
                       SelectObject(DirectDC,PreviousObject);

                    // finish
                    RetBmp=DirectBitmap;
                }
                // clean up
                DeleteDC(DirectDC);
            }            
            if (hTmpBitmap)
            {
                SelectObject(hBmpDC, hBmp);
                DeleteObject(hTmpBitmap);
            }
            SelectObject(BufferDC,PreviousBufferObject);
            // BufferDC is now useless
            DeleteDC(BufferDC);
        }
    }
    return RetBmp;
}

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication

About the Author

Dimitri Rochette

Software Developer (Senior)

France France

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 3 PinmemberMukit, Ataul19:31 5 Feb '12  
GeneralRe: My vote of 3 Pinmembersgllama22:14 5 Feb '12  
GeneralRe: My vote of 3 Pinmembersgllama22:26 5 Feb '12  
GeneralRe: My vote of 3 PinmemberMukit, Ataul22:29 5 Feb '12  
GeneralMy vote of 5 PinmemberAbinash Bishoyi6:11 5 Feb '12  
Questiongreate code Pinmemberszk.sh16:17 7 Dec '11  
QuestionRewrite available for .Net either VB.Net or C#? PinmemberTim8w5:50 2 Jul '08  
GeneralBrilliant PinmemberAli Imran Khan Shirani5:25 29 Jun '08  
Generalcan't build Pinmemberk_meleon22:44 20 Mar '08  
Hello,
 
I try to build but it can't... I don't see the result window. Is anyone can help me?
Thanks
 
Here the build log :
 
Project : RplColor
Compiler : GNU GCC Compiler (called directly)
Directory : C:\Documents and Settings\LDriutti\Bureau\RplColor\
--------------------------------------------------------------------------------
Switching to target: Win32 Release
Compiling: RplColor.rc
Compiling: Main.cpp
Linking executable: C:\Documents and Settings\LDriutti\Bureau\RplColor\Release\RplColor.exe
Release\Main.o:Main.cppFrown | :( text+0x427): undefined reference to `CreateMappedBitmap@20'
Release\Main.o:Main.cppFrown | :( text+0x75e): undefined reference to `ImageList_Destroy@4'
Release\Main.o:Main.cppFrown | :( text+0x8a6): undefined reference to `ImageList_DrawEx@40'
Release\Main.o:Main.cppFrown | :( text+0x97a): undefined reference to `InitCommonControls@0'
Release\Main.o:Main.cppFrown | :( text+0x9fa): undefined reference to `ImageList_Create@20'
Release\Main.o:Main.cppFrown | :( text+0xa11): undefined reference to `ImageList_Add@12'
Release\Main.o:Main.cppFrown | :( text+0xa29): undefined reference to `ImageList_AddMasked@12'
Release\Main.o:Main.cppFrown | :( text+0xa6e): undefined reference to `ImageList_AddMasked@12'
Release\Main.o:Main.cppFrown | :( text+0xab0): undefined reference to `ImageList_AddMasked@12'
Release\Main.o:Main.cppFrown | :( text+0xaf5): undefined reference to `ImageList_AddMasked@12'
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 3 seconds)
GeneralArtist needs help Pinmemberpxfragonard13:32 29 Jan '07  
GeneralProblem on WinCE 3.0 PinmemberAllen25:29 28 Sep '06  
GeneralRe: Problem on WinCE 3.0 PinmemberDimitri Rochette8:58 28 Sep '06  
GeneralA little shorter variant of "BlitReplaceColor" PinmemberYuri Machlin1:07 23 Jun '06  
GeneralRe: A little shorter variant of "BlitReplaceColor" PinmemberDimitri Rochette9:30 24 Jun '06  
Generalconverting values PinmemberdSolariuM11:29 27 Apr '06  
GeneralRe: converting values PinmemberDimitri Rochette23:22 27 Apr '06  
GeneralRe: converting values PinmemberdSolariuM3:15 28 Apr '06  
GeneralBitmap bit access PinmemberGeorgi Petrov2:56 28 Dec '04  
GeneralRe: Bitmap bit access PinmemberDimitri Rochette9:43 28 Dec '04  
GeneralRe: Bitmap bit access PinmemberGeorgi Petrov20:16 28 Dec '04  
QuestionWhy not sent changed bmmp to clipboard? Pinmembersunwolf_00116:16 18 Oct '04  
GeneralLoading bitmap Pinmembervivadotnet8:23 23 Jun '04  
GeneralRe: Loading bitmap PinmemberDimitri Rochette10:30 23 Jun '04  
GeneralThanks for the code snipit PinmemberSAHorowitz4:03 19 Apr '04  
QuestionHow can i change a specific Pixel in a Bitmap to Transparent PinmemberAzhar Javaid1:21 21 Jul '03  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120529.1 | Last Updated 5 Feb 2012
Article Copyright 2002 by Dimitri Rochette
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid