|
allmost 6 !!
thanks you vary much.
I have no imidiate work for your class, but i have been able to isolate the very much needed code to create this: HICON CreateGrayscaleIcon(HICON hIcon);
thanks a million.
|
|
|
|
|
You are very welcome
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
"Obviously ??? You're definitely a superstar!!!" - mYkel - 21 Jun '04
"There's not enough blatant self-congratulatory backslapping in the world today..." - HumblePie - 21 Jun '05
Within you lies the power for good - Use it!
|
|
|
|
|
Hi PJ,
in short I want to convert a 32bpp (RGBA) icon to grayscale while runtime without losing the alpha channel. If I drop such an icon into your test app and convert it to grayscale the alpha channel gets lost.
Taking a short look into your code shows that you make at last a RGB copy operation of the grayed pixels. The alpha channel is not set and so the formerly transparent parts become opaque.
Is there a way to convert 32bpp icons to grayscale without losing the alpha channel AND without using GDI+? I have code for doing this with GDI+ but I cannot use it yet in my project.
Thanks in advance!
Regards, mykel
If they give you lined paper, write the other way!
|
|
|
|
|
After some nightly debugging I happily fixed the problem
The following part of CPJAImage::GrayScale() causes the problem:
int nGray = Gray(GetBValue(*dst), GetGValue(*dst), GetRValue(*dst));
*dst = (DWORD)RGB(nGray, nGray, nGray);
The RGB macro fills bit 0-24 with (BYTE)nGray . Bit 25-32 are filled with zeros and afterwards copied back into dst . So at this point the formerly alpha channel byte gets zero which is opaque.
Here is my fix:
Add the following macro at the top of pjaimage.h :
#define RGBA(r,g,b,a) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16))|(((DWORD)(BYTE)(a))<<24)) This macro is similar to the RGB macro but it also accepts an alpha channel byte.
So everything is fine if we do:
int nGray = Gray(GetBValue(*dst), GetGValue(*dst), GetRValue(*dst));
BYTE* pAlpha = ((BYTE*)dst)+3;
*dst = (DWORD)RGBA(nGray, nGray, nGray, *pAlpha);
That's all. Grayscaling a 32bpp icon doesn't kill the alpha channel anymore. Hope you like it!
Regards, mykel
If they give you lined paper, write the other way!
|
|
|
|
|
Thanks Mykel.
Having others contribute bug fixes to bugs that the authors miss is part of what makes CP so great.
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
"Obviously ??? You're definitely a superstar!!!" mYkel - 21 Jun '04
Within you lies the power for good - Use it! Honoured as one of The Most Helpful Members of 2004
|
|
|
|
|
Mykel, not that it really matters, but I changed the code to
*dst = (*dst & 0xff000000) | ((DWORD)RGB(nGray, nGray, nGray) & 0x00ffffff); Saves having to add the RGBA macro to the file.
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
"Obviously ??? You're definitely a superstar!!!" mYkel - 21 Jun '04
Within you lies the power for good - Use it! Honoured as one of The Most Helpful Members of 2004
|
|
|
|
|
Hi PJ,
sounds good to me.
I also forgot to thank you for another nice class!
Have a nice day,
mykel
If they give you lined paper, write the other way!
|
|
|
|
|
Hi,
First all thanks for the class. I found it quite handy drawing images
transparently. I am using it in our latest project at work, but have
come across a problem.
There seems to be some confusion (or my lack of understand) over the use
w and h parameter of the DrawImage function, which lead to what I suspect to
be the weird artifacts generated of drawing two images on the same DC.
What I have tried to do is draw two transparent images adjacently on my view.
I have read that there was fix to a similar (or maybe the same) problem in 2003. I have in my code something like this...
CPJAImage img1;
CPJAImage img2;
img1.SetImage(...);
img2.SetImage(...);
// Note I treat w and h as logical offset of x and y respectively not as the width and height of the rect.
CSize img1Size = img1.GetSize();
CSize img2Size = img2.GetSize();
CPoint offset(10,10); // For example
img1.DrawImage(pDC, offset.x, offset.y, offset.x + img1Size.cx, offset.y + img1Size.cy, CPJA_TRANSPARENT);
img2.DrawImage(pDC, offset.x + img1Size.cx, offset.y + img1Size.cy, offset.x + img1Size.cx + img2Size.cx, offset.y + img1Size.cy + img2Size.cy, CPJA_TRANSPARENT);
// This produces strange artifacts when my view is resized.
// So I changed the code in these places to make it work.
// In the DrawImage function.
//old
BackGroundBitmap.CreateCompatibleBitmap(pDC, w, h);
...
BackGroundDC.BitBlt(0, 0, w, h, pDC, x, y, SRCCOPY);
...
TransparentDC.BitBlt(0, 0, w, h, &BackGroundDC, left, top, SRCCOPY);
//new changes
BackGroundBitmap.CreateCompatibleBitmap(pDC, width, height);
...
BackGroundDC.BitBlt(0, 0, width, height, pDC, x, y, SRCCOPY);
...
TransparentDC.BitBlt(0, 0, width, height, &BackGroundDC, 0, 0, SRCCOPY);
Im not sure how these changes adversely affects the rest of the code.
I have also added my own function to load a picture by a file name.
I can post it if anyone wants it.
Thanks,
Samson
Coding Monkey
|
|
|
|
|
Sawjai wrote:
TransparentDC.BitBlt(0, 0, width, height, &BackGroundDC, 0, 0, SRCCOPY);
This seems to be the only fix that is necessary as the painting is clipped to the supplied rectancle if width and height are bigger than w and h.
Thanks for the bug fix
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
"Obviously ??? You're definitely a superstar!!!" mYkel - 21 Jun '04
Within you lies the power for good - Use it! Honoured as one of The Most Helpful Members of 2004
|
|
|
|
|
Hi PJ Arends,
Oppps, I had a mistake in my DrawImage parameters that I posted.
I will update it now.
Cheers,
Samson
|
|
|
|
|
I am new to VC++ (recently converted from Borland C++ Builder).
Does any one any suggestion/code example on how to make CPJAImage a Drag & Drop Control in VC++ 7.1? I want to replace Image control with CPJAImage. I want to load image during design time (Image property) and also a transparent property.
Len Richter
|
|
|
|
|
CPJAImage is not a windows control (It is not a wrapper around an HWND), so I would say just call CPJAImage::DrawImage() from your regular control's WM_PAINT handler. Or you can do as I did in the demo app, which does support drag 'n drop.
Sonork 100.11743 Chicken Little
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
Within you lies the power for good - Use it!
|
|
|
|
|
I did try using Onpaint but I am missing something because I am getting a black rectangle (ie. like iti si not coping the BMP image from the Image control). Here is my code. Do you see anything I am missing?
private: System::Void ImagePaint(System::Object * sender, System::Windows::Forms::PaintEventArgs * e)
{CPJAImage PJAImage;
PictureBox *pPictureBox = dynamic_cast<picturebox *="">(sender);
// Get handle to device context.
HDC hDC = (HDC)e->Graphics->GetHdc().ToInt32();
CDC* pDC = CDC::FromHandle(hDC);
COLORREF clr = pDC->GetPixel(CPoint(0,0));
PJAImage.SetImage(hDC, PJAI_BITMAP);
PJAImage.SetTransparentColour(clr);//RGB(0,0,0));
/
CDC DC;
DC.CreateCompatibleDC(pDC);
int save = DC.SaveDC();
SelectObject(background);
CBitmap bmp;
bmp.CreateCompatibleBitmap(pDC, pPictureBox->Image->Width, pPictureBox->Image->Height);
DC.SelectObject(bmp);
PJAImage.DrawImage(&DC, 0, 0, pPictureBox->Image->Width, pPictureBox->Image->Height, PJAI_TRANSPARENT | PJAI_STRETCHED);
pDC->BitBlt(0, 0, pPictureBox->Image->Width, pPictureBox->Image->Height, &DC, 0, 0, SRCCOPY);
DC.RestoreDC(save);
DC.DeleteDC();
// Release handle to device context.
e->Graphics->ReleaseHdc(hDC);
//ReleaseDC(pDC);
}
Len
|
|
|
|
|
Len2020 wrote:
HDC hDC = (HDC)e->Graphics->GetHdc().ToInt32();
Len2020 wrote:
PJAImage.SetImage(hDC, PJAI_BITMAP);
Here is your problem, SetImage requires a HBITMAP or HICON as the first parameter, you are passing in a HDC as a HBITMAP.
Sonork 100.11743 Chicken Little
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
Within you lies the power for good - Use it!
|
|
|
|
|
PIA WROTE:
Here is your problem, SetImage requires a HBITMAP or HICON as the first parameter, you are passing in a HDC as a HBITMAP.
I still can not get this to work due to my lack on knowledge between GDI and GDI++. I have searched on the web to get an understanding on DC and HBITMAP by it is still fuzzy to me. The problem is the handle HBITMAP and trying to get it. I did use HBITMAP operator but I am still get a black box only. I am still missing something obvious. Could you by chance correct my code. I would really appreciate it.
private: System::Void ImagePaint(System::Object * sender, System::Windows::Forms::PaintEventArgs * e)
{CPJAImage PJAImage;
PictureBox *pPictureBox = dynamic_cast<picturebox *="">(sender);
// Get handle to device context.
HDC hDC = (HDC)e->Graphics->GetHdc().ToInt32();
CDC *pCDCImage = CDC::FromHandle(hDC);
// Create an in-memory DC compatible with the display DC we're using to paint.
CDC cdcMemory;
cdcMemory.CreateCompatibleDC(pCDCImage);
int save = cdcMemory.SaveDC();
COLORREF clr = cdcMemory.GetPixel(CPoint(0,0));
CBitmap bmp;
bmp.CreateCompatibleBitmap(&cdcMemory, pPictureBox->Image->Width, pPictureBox->Image->Height);
// Select Bitmap into the new Memory DC.
cdcMemory.SelectObject(bmp);
PJAImage.SetImage(bmp.operator HBITMAP(), PJAI_BITMAP);
//PJAImage.SetTransparentColour(clr);//RGB(0,0,0));
PJAImage.DrawImage(&cdcMemory, 0, 0, pPictureBox->Image->Width, pPictureBox->Image->Height, PJAI_TRANSPARENT | PJAI_STRETCHED);
// Copy the bits from the in-memory DC into the on-screen DC to actually do the painting.
pCDCImage->BitBlt(0, 0, pPictureBox->Image->Width, pPictureBox->Image->Height, &cdcMemory, 0, 0, SRCCOPY);
cdcMemory.RestoreDC(save);
cdcMemory.DeleteDC();
// Release handle to device context.
e->Graphics->ReleaseHdc(hDC);
}
Regards,
Len Richter
|
|
|
|
|
Len2020 wrote:
private: System::Void ImagePaint(System::Object * sender, System::Windows::Forms::PaintEventArgs * e)
{CPJAImage PJAImage;
PictureBox *pPictureBox = dynamic_cast(sender);
// Get handle to device context.
HDC hDC = (HDC)e->Graphics->GetHdc().ToInt32();
CDC *pCDCImage = CDC::FromHandle(hDC);
// Create an in-memory DC compatible with the display DC we're using to paint.
CDC cdcMemory;
cdcMemory.CreateCompatibleDC(pCDCImage);
int save = cdcMemory.SaveDC();
OK, everything looks good up to this point. But the cdcMemory device context will only have a 1 x 1 pixel monochrome bitmap selected into at this point.
Len2020 wrote:
COLORREF clr = cdcMemory.GetPixel(CPoint(0,0));
Because cdcMemory contains a monochrome bitmap, clr will be either black or white (probably black).
Len2020 wrote:
CBitmap bmp;
bmp.CreateCompatibleBitmap(&cdcMemory, pPictureBox->Image->Width, pPictureBox->Image->Height);
I would have used the original pCDCImage here instead of the cdcMemory as the cdcMemory is currently monochrome.
bmp.CreateCompatibleBitmap(pCDCImage, ...
Len2020 wrote:
// Select Bitmap into the new Memory DC.
cdcMemory.SelectObject(bmp);
PJAImage.SetImage(bmp.operator HBITMAP(), PJAI_BITMAP);
Your PJAImage object now contains a monchrome bitmap that is initialized with random data, probably just a black box.
Len2020 wrote:
//PJAImage.SetTransparentColour(clr);//RGB(0,0,0));
PJAImage.DrawImage(&cdcMemory, 0, 0, pPictureBox->Image->Width, pPictureBox->Image->Height, PJAI_TRANSPARENT | PJAI_STRETCHED);
Again, the cdcMemory still contains only a 1 x 1 pixel monochrome bitmap as it's drawing surface.
Len2020 wrote:
// Copy the bits from the in-memory DC into the on-screen DC to actually do the painting.
pCDCImage->BitBlt(0, 0, pPictureBox->Image->Width, pPictureBox->Image->Height, &cdcMemory, 0, 0, SRCCOPY);
cdcMemory.RestoreDC(save);
cdcMemory.DeleteDC();
// Release handle to device context.
e->Graphics->ReleaseHdc(hDC);
}
You are seeing a black box because that is what you are telling it to do.
That being said, I am wondering why you are trying to use this class in a .NET app? While it will certainly work, I would think you would be better off using the .NET runtime.
I do not know how you are initializing the Image property of the PictureBox object that you are using here, but System.Drawing.Image is the base class for System.Drawing.Bitmap , and the Bitmap class has a method MakeTransparent(Color) that you could use.
private: System::Void ImagePaint(System::Object * sender, System::Windows::Forms::PaintEventArgs * e)
{
PictureBox *pPictureBox = dynamic_cast< PictureBox * >(sender);
Bitmap *pBitmap = dynamic_cast< Bitmap * >(pPictureBox->Image);
pBitmap->MakeTransparent(pBitmap->GetPixel(0, 0));
e->Graphics->DrawImage(pBitmap, 0, 0);
}
Note: I do not do .NET, so this code is totally untested, but you should get the idea;)
Sonork 100.11743 Chicken Little
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
Within you lies the power for good - Use it!
|
|
|
|
|
Hi Pete.
The problem in using the .Net class example is the PictureBox. It does not support true transparent. What I mean is the Image is transprent but you still show the PictureBox control. Boralnd had a PictureBox but also just a Image control and basically that is what I want. In further thinking about it using OnPaint and your control will not work either because the PictureBox will still be shown. I can not find a way to make the the PictureBox transparent also. I tried Form->TransprentKey and set Control->BackColor but that also make the form underneath transparent and therfeore see the desktop. Man, I though M$ would had a simple drag & drop ImageControl. I do not know how to create my own because still newbie to VS 2003. Do you have and further suggestions?
Len
|
|
|
|
|
Pete I had an Idea of capturing the area underneath the paintbox (form and controls), saving it as bmp and placing it into picturebox->backgroundimage. However I can not get the capture routine to work. Save problem of black box. Do you have any code exple on how to do this?
Len
|
|
|
|
|
As I said before, I do not do .NET, but could you not skip the picture box control and draw your image directly onto the form?
I do not know, maybe you would get a better answer if you took your question over to the managed VC forum.
Sonork 100.11743 Chicken Little
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
Within you lies the power for good - Use it!
|
|
|
|
|
It only works when the background is a single color or I didn't use it correctly? Thanks.
|
|
|
|
|
I'm thinking you're using it wrong, but without seeing your code, I can't be sure.
Sonork 100.11743 Chicken Little
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
Within you lies the power for good - Use it!
|
|
|
|
|
Thanks for the quick reply, I really appreciate it.
Actually, I want to draw a bitmap on another bitmap.
I want the above bitmap has a transparent backgroup.
Do you think this code will work?
Thanks again!
|
|
|
|
|
Ok, you are correct. It does not work the way you want. The DrawImage function uses the brush that is currently selected into the supplied DC as the background for transparent images. I will try to fix this and get back to you. It should not take more than a couple days.
Sonork 100.11743 Chicken Little
"You're obviously a superstar." - Christian Graus about me - 12 Feb '03
Within you lies the power for good - Use it!
|
|
|
|
|
Hi,
Thank you for your article.
Just wanted to ask you if you know how ot generate a bitmap with text on it.
FOr example:
HDC=GetDC(NULL);
TextOut(hdc, 0, 0, "AAA", 3);
............................
Now I want this to be saved as a bitmap.
Thank you.
JJordan
|
|
|
|
|
You can save this text into bmp file using these lines:
HBITMAP hBmp;
HPALETTE hpal;
hBmp = CreateCompatibleBitmap(hDC, rect.right, rect.bottom);
SelectObject(hDC, hBmp);
Bitmap bmp(hBmp, hpal);
CLSID encoderClsid;
GetEncoderClsid(_T("image/bmp"), &encoderClsid); // you can save it in gif simple using "image/gif"
Status stat;
stat = bmp.Save(strFileName, &encoderClsid, NULL);
// That's all !
// My question: How can I save the text in gif with the background made transparent ?
// if you use "image/gif" encoder -> you can get a GF89A file, but I don't know how to set it's background as transparent one !
Cheers,
Mihai
|
|
|
|
|