|
Hi,
"BlitReplaceColor" function that is available in "Main.cpp" file of the demo project may be realized a little efficiently: with 2 BitBlt calls in place of 4, and without preparation of additional temporary color DC. Here is the realization:
BOOL BlitReplaceBitmapColor(HBITMAP hbm, COLORREF rgbOld, COLORREF rgbNew, HDC hdcBm = NULL)
{
#ifdef _DEBUG
// check the parameters validity
if(::GetObjectType(hbm) != OBJ_BITMAP)
{
ASSERT(0);
return FALSE;
}
if(hdcBm != NULL)
{
if(((HBITMAP)::GetCurrentObject(hdcBm, OBJ_BITMAP)) != hbm)
{
ASSERT(0);
return FALSE;
}
}
#endif // _DEBUG
BOOL bDone = FALSE;
HDC hdcBmSel, hdcMaskBmSel;
HBITMAP hbmMask;
HGDIOBJ hbmDcOrig, hbmMaskDcOrig;
COLORREF rgbDcOrigBack, rgbDcOrigFore;
hdcBmSel = hdcBm;
hdcMaskBmSel = NULL;
hbmMask = NULL;
hbmDcOrig = hbmMaskDcOrig = NULL;
rgbDcOrigBack, rgbDcOrigFore = MAXDWORD;
__try
{
if(hdcBmSel == NULL)
{
if(!(hdcBmSel = ::CreateCompatibleDC(NULL)))
__leave;
if(!(hbmDcOrig = ::SelectObject(hdcBmSel, hbm)))
__leave;
}
// Prepare monochrome mask bitmap from the given 'hbm' so that its
// pixels with 'rgbOld' color will be white pixels of the mask
BITMAP bm;
if(::GetObject(hbm, sizeof(BITMAP), &bm) != sizeof(BITMAP))
__leave;
if(!(hdcMaskBmSel = ::CreateCompatibleDC(NULL)))
__leave;
if(!(hbmMask = ::CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL)))
__leave;
if(!(hbmMaskDcOrig = ::SelectObject(hdcMaskBmSel, hbmMask)))
__leave;
rgbDcOrigBack = ::SetBkColor(hdcBmSel, rgbOld);
::BitBlt(hdcMaskBmSel, 0, 0, bm.bmWidth, bm.bmHeight, hdcBmSel, 0, 0, SRCCOPY);
::SetBkColor(hdcBmSel, (rgbNew ^ rgbOld));
rgbDcOrigFore = ::SetTextColor(hdcBmSel, 0x00000000);
::BitBlt(hdcBmSel, 0, 0, bm.bmWidth, bm.bmHeight, hdcMaskBmSel, 0, 0, SRCINVERT);
bDone = TRUE;
}
__finally
{
if(hdcBm == NULL)
{
if(hdcBmSel != NULL)
{
if(hbmDcOrig != NULL)
::SelectObject(hdcBmSel, hbmDcOrig);
::DeleteDC(hdcBmSel);
}
}
else
{
if(rgbDcOrigBack != MAXDWORD)
::SetBkColor(hdcBm, rgbDcOrigBack);
if(rgbDcOrigFore != MAXDWORD)
::SetTextColor(hdcBm, rgbDcOrigFore);
}
if(hdcMaskBmSel != NULL)
{
if(hbmMaskDcOrig != NULL)
::SelectObject(hdcMaskBmSel, hbmMaskDcOrig);
::DeleteDC(hdcMaskBmSel);
if(hbmMask != NULL)
::DeleteObject(hbmMask);
}
}
return bDone;
}
Thanks, Yuri.
|
|
|
|
|
Lo,
Yes your code may be usefull for some.
My code was written this way to not destroy HBITMAP parameter, and so to be able to use it more than once.
Thanks
|
|
|
|
|
Hi,
Thank you for your article,
it was very helpful.
But I have problem with ptPixels.
I want to send the values to another computer
But I can only send them in char...
Is there any way to send them???
and then use it in StretchDIBits?
Thanks.
Every new thing you learn,Gives you a new personality.
|
|
|
|
|
Lo,
To extract 3 chars from the color pixel
char cRed= (ptPixels[i] & 0xff0000)>>16;
char cGreen= (ptPixels[i] & 0x00ff00)>>8;
char cBlue= (ptPixels[i] & 0x0000ff);
After that you could zip or convert to jpg,PNG your data send it to the other computer. It will be possible to use StretchDIBits on the destination computer if you reconvert your 3 chars to an array with somathing like this ptPixels[i]=(cRed <<16)|(cGreen <<8)|(cBlue);
Have a nice Code.
|
|
|
|
|
Hi,
Thanks for your reply...It's right but it doesn't work for me..
I use these(foolish,I know,It's only for test,Just to be sure about sending)codes to fill the pt array from 3 chars. and it gives
me something like inverted colors. But I am sure that these colors are not inverted.
Please help me.I need your help...
////////////////////////////////////////////////////////////////////////////////////////////////////////
.
.
.
.
bool t=false;
long lc=NULL;
UINT ic=NULL;
UINT *pt=new UINT[1024*768];
void CMfcDlg::OnBnClickedButton2()
{
char cRed,cGreen,cBlue;
for (int i=0;i<=((1024*768));i++)
{
char cRed= (ptPixels[i] & 0xff0000)>>16;
char cGreen= (ptPixels[i] & 0x00ff00)>>8 ;
char cBlue= (ptPixels[i] & 0x0000ff);
myFunc(cRed,cGreen,cBlue);
}
t=true;
myFunc(NULL,NULL,NULL);
}
void CMfcDlg::myFunc(char chR,char chG,char chB)
{
if (!t)
{
lc++;
pt[lc]=(chR <<16)|(chG <<8)|(chB);
}
if (t)
{
CWindowDC dcc(GetDesktopWindow());
BITMAPINFO RGB32BitsBITMAPINFO;
ZeroMemory(&RGB32BitsBITMAPINFO,sizeof(BITMAPINFO));
RGB32BitsBITMAPINFO.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
RGB32BitsBITMAPINFO.bmiHeader.biWidth=1024;
RGB32BitsBITMAPINFO.bmiHeader.biHeight=768;
RGB32BitsBITMAPINFO.bmiHeader.biPlanes=1;
RGB32BitsBITMAPINFO.bmiHeader.biBitCount=32;
StretchDIBits(dcc.GetSafeHdc(),0,0,200,500,0,0,200,500,pt,
(BITMAPINFO*)&RGB32BitsBITMAPINFO,DIB_RGB_COLORS,SRCCOPY);
lc=0;
ic=0;
t=false;
}
}
//////////////////////////////////////////////////////////////////////////////////////
Every new thing you learn,Gives you a new personality.
|
|
|
|
|
Hi,
can you please tell me how to find more about fast bitmap bit access.
I have 32bit bitmaps, and like to use your: ptPixels[i]
I have a bitmap loaded in a CBitmap, and write pixels with cdc.Get, Set pixel but this aproach is too slow.
I'm trying to implement a CCD open code soft for real time median filtering, but have no sucses with speed.
Best,
Georgi
|
|
|
|
|
Lo,
The way to fast access pixel in GDI is to use CreateDIBSection as in the sample. So you must have a DC as source and receive a HBitmap as result.
You'll have to deal with a getDC on the object where your image is selected, or cast your CBitmap to HBITMAP and select it in a BufferDC as done in the sample.
After that you could use a CreateDIBSection and receive a HBitmap.
With CBitmap::FromHandle you can revert a HBITMAP to a CBitmap ( Not really fast but if you did it after a lot of modification it's ok )
hope it will helps you a bit.
|
|
|
|
|
Hello Friend,
Thank you, I'll try your aproach.
It is interesting because in your routine you make a strange bitmap CDC to bitmap CDC exchange? But I'll try to use your routine direct in my applicatiopns.
Best regards, and have a Happy New Year!
Georgi
|
|
|
|
|
Who can help me?
I write programme to get image from window,then I want change hte color of the pixel before send to the slipboard.But when I change color of the HBITMAP object,the clipboard can not get this bitmap.Before I change the color,everything is OK.
same while When I write changed bmp to file ,everything is OK.
|
|
|
|
|
Hello,
In this code bitmap is loaded for recource file. If the bitmap genereate
on fly.How would bitmap get loaded for alteration/changes after it get generated.
for example if I have a button with three different bitmap loaded in it and
I want to get bitmaps displayed on the button as one bitmap?
is this possible?
thanks
|
|
|
|
|
Hi,
First the answer to "is it possible?"
Yes very few things are impossible
This function deal with HBitmaps, so if you store your Bitmap using HBitmaps, its easy to use it. As HBitmap is a standard GDI Object there is certainly a way to get HBitmap from your object, and a solution to put it back. if you have your image in a DC <g>CreateCompatibleBitmap maybe your solution, otherwise seek in the msdn and or internet.
Good luck
-- Sad Mode on
nowadays findings samples of code using windows api or gdi objects is a tough work. Many articles present MFC or .NET as THE solution negleting that 90% of those API are only wrapper to windows core.
-- Sad Mode off
If your seeking a book to learn and understand the core of windows programming Charles Petzold's Book "Windows programming" is a must read.
|
|
|
|
|
Thanks for this code snipit. It is exactly what I needed and I have been looking all over the web for it. I truly appreciate it
|
|
|
|
|
How can i change a pixel color to transparent color.
Azhar Javaid Abbasi
|
|
|
|
|
Transparent color doesn't exist.
The only option is to create a mask which include only the pixels you dont want to draw.
You can use this function to replace all wanted transparent pixel to one not used color in your image. Then create a mask using this color. And finaly use that mask to draw your bitmap.
Read the code mask creation and use is done in sample.
|
|
|
|
|
I've got an imagelist with a lot of bitmap and would like to use this code to change the color of one of them... But i don't know how to get a hbitmap from a bitmap in my imagelist ??
Could someone help me ?
Thanks
|
|
|
|
|
You can create a Dc then draw your selected bitmap in then use this dc to have a bitmap.
But isnt it better to modify your bitmap directly in your imagelist ?
In the function Replacebitmap replace
<br />
for (int i=((bm.bmWidth*bm.bmHeight)-1);i>=0;i--)<br />
{<br />
if (ptPixels[i]==cOldColor) ptPixels[i]=cNewColor;<br />
}<br />
By
<br />
int xFrom=(single_image_width * image_to_modify_position);<br />
int xTo=(single_image_width * (image_to_modify_position+1));<br />
int posxy;<br />
for (int j=0;j<bm.height;j++)<br />
for (int i=xFrom;i<xTo;i++)<br />
{<br />
posxy=i+(j*bm.width); <br />
if (ptPixels[posxy]==cOldColor) ptPixels[posxy]=cNewColor;<br />
}<br />
Easy to make some improvements like removing the j*bm.width from the second for.
Wrote that fast at night and havent tested it. i think that if there's a mistake you should find it fast.
|
|
|
|
|
Hi,
You're right, change the bitmap directly is a good idea and it will help me a lot... thanks you very much
bye
|
|
|
|
|
If you are loading a bitmap from a resource, you can use the CBitmap method LoadMappedBitmap. The following code loads the bitmap and replaces all red and green pixes
#DEFINE COLORS_TO_REPLACE
COLORMAP sColorMap[COLORS_TO_REPLACE];
sColorMap[0].from = RGB(255,0,0);
sColorMap[0].to = ::GetSysColor(COLOR_BTNFACE);
sColorMap[1].from = RGB(0,255,0);
sColorMap[1].to = ::GetSysColor(COLOR_GRAYTEXT);
CBitmap cTest;
cTest.LoadMappedBitmap( ID_MYBITMAP, 0, (COLORMAP*)&sColorMap, COLORS_TO_REPLACE);
Hope this helps
|
|
|
|
|
The "#define" statement needs an integer constant.
e.g.
#define COLORS_TO_REPLACE 10 // or whatever # of color maps "&sColorMap" will be pointing to.
(No offense meant.)
William
|
|
|
|
|
It's been a while since I did extensive work with DIBs and Bitmaps, but a few things about the code presented here bothered me so I referenced some code of mine. I hope I got my comments right:
- If the passed hBmp is already selected into a device context, errors will be generated. My own solution was to wrap the DIB conversion code with the following:
<br />
HBITMAP hTmpBitmap = (HBITMAP) NULL;<br />
if (hBitmap == (HBITMAP)GetCurrentObject(hDC, OBJ_BITMAP))<br />
{<br />
hTmpBitmap = ::CreateBitmap(1, 1, 1, 1, NULL);<br />
::SelectObject(hDC, hTmpBitmap);<br />
}<br />
<br />
<br />
if (hTmpBitmap)<br />
{<br />
::SelectObject(hDC, hBitmap);<br />
::DeleteObject(hTmpBitmap);<br />
}<br />
- Early in the function hBmp is selected into BufferDC. BufferDC is later deleted without unselecting releasing hBmp. This has two problems: First, unless I'm not remembering correctly, this will leak the HBITMAP returned by the original ::SelectObject() call. Second, the developer may not want the original bitmap deleted.
- You could have used ::GetDIBits() to retrieve the image data. This is fast and has the advantage of keeping the DIB in the same bits per pixel as the DIB. For palette mapped dibs it also has the advantage of allowing the transparet color to be changed with a few lines of code. On the negative side, it requires more special cased code for the various bits per pixel. (Most users set their displays to 32-bit color, so some of the advantages listed here no longer apply.)
- If loading a bitmap from a resource, you can use the ::CreateMappedBitmap() call to remap colors.
|
|
|
|
|
Thanks for your comments.
I agree to your first point a HBITMAP could be selected to only one DC at a time. Your code is right, but mine isn't wrong. It's the caller which have to be sure that the bitmap isn't already selected. I'm unable ( or i don't know how ) in my function to detect if the passed bitmap is already selected.
Second point:
the selectobject don't loose a bitmap because the first selectobject after CreateCompatibleDC(NULL) allways return NULL.
The original bitmap is not deleted. i select it into BufferDC then delete BufferDC but not hBmp. After deleting a DC i can reselect the bitmap in another one. If you'd like to you can add just before DeleteDC(BufferDC); a SelectObject(BufferDC,NULL); ( NULL is the result of the first selectobjet ). But i think that will do nothing.
Third point:
i 100% agree. i can use GetDIBits() writing many more code. I think than forcing todays applications to use 32 Bits color for a bitmap isn't a problem.
Last point:
i once more 100% agree with you. If directly loading a bitmap from resource CreateMappedBitmap is better.
For those who are interested
CreateMappedBitmap is 9 times faster than loading a bitmap and use my function to replace color. So my sample in this article is really stupid
|
|
|
|
|
First Point:
Since your function doesn't "know" what the caller has done with the bitmap, you are correct. In my code, the caller was also passing a device context, thus the confusion on my part. (However, it's good to document this so the caller knows about this.)
(As a side note, a little known function is ::GetCurrentObject() which returns the specified current object in the specificed DC without having to select it.)
On the second point:
My tests show that the first SelectObject() returns a non-NULL. I'm curious as to why you're getting a NULL, since a NULL indicates an error (unless you're selecting regions, but that's another story.)
|
|
|
|
|
You're right the first SelectObject() returns a non-NULL. i don't remember why my tests returns NULL.
I made a mistake. I'll re-upload this code with your corrections, and improvements on the already selected bitmap.
Thanks
|
|
|
|
|
Hi this is a quite good sample but I have one remark for your about your 32 Bit color statement:
I had often to deal with images with 10.000 * 10.000 pixel or more. If you use 1 or 4 bit per Pixel the image consumes about
12.5 MB or 50MB of memory which is reasonable to handle.
If you force 32bit per pixel the same image requires 400MB of memory.....
|
|
|
|
|
|