How to create pattern brush using PNG image - c++

I have image with black dots in a white background, which i use to create a patten brush and change the color of the black dots using SetTextColor. So I created a CBitmap using PNG image(i used CPngImage to get HBITMAP and attached it to CBitmap ). Now i created PatternBrush using(CreatePatternBrush) with that bitmap, the problem is that the color of the brush doesn't change if i use PNG image. But it changes when i use image in bmp format.
here is some code
CBitmap bmp;
if (bmp.LoadBitmap(IDB_BITMAP1))
{
if (brush.CreatePatternBrush(&bmp))
pOldBrush = pDC->SelectObject(&brush);
}
pDC->SetTextColor(color);
This Works
CBitmap bmp;
CPngImage pngImage;
HBITMAP hBitmap = NULL;
if ( pngImage.Load(MAKEINTRESOURCE(IDB_PNG1)) )
{
hBitmap = (HBITMAP)pngImage.Detach();
}
if ( hBitmap )
bmp.Attach(hBitmap);
if (brush.CreatePatternBrush(&bmp))
pOldBrush = pDC->SelectObject(&brush);
pDC->SetTextColor(color);
This Doesn't work

Related

How to get a 32bpp bitmap/image from a GDI Device Context?

I am using code from this Project http://www.codeproject.com/Articles/9064/Yet-Another-Transparent-Static-Control in order to draw transparent button images from my subclassed Button control onto my CDialogEx.
This code is meant for legacy 24bpp GDI functions:
BOOL CTransparentStatic2::OnEraseBkgnd(CDC* pDC)
{
if (m_Bmp.GetSafeHandle() == NULL)
{
CRect Rect;
GetWindowRect(&Rect);
CWnd *pParent = GetParent();
ASSERT(pParent);
pParent->ScreenToClient(&Rect); //convert our corrdinates to our parents
//copy what's on the parents at this point
CDC *pDC = pParent->GetDC();
CDC MemDC;
MemDC.CreateCompatibleDC(pDC);
m_Bmp.CreateCompatibleBitmap(pDC,Rect.Width(),Rect.Height());
CBitmap *pOldBmp = MemDC.SelectObject(&m_Bmp);
MemDC.BitBlt(0,0,Rect.Width(),Rect.Height(),pDC,Rect.left,Rect.top,SRCCOPY);
MemDC.SelectObject(pOldBmp);
pParent->ReleaseDC(pDC);
}
else //copy what we copied off the parent the first time back onto the parent
{
CRect Rect;
GetClientRect(Rect);
CDC MemDC;
MemDC.CreateCompatibleDC(pDC);
CBitmap *pOldBmp = MemDC.SelectObject(&m_Bmp);
pDC->BitBlt(0,0,Rect.Width(),Rect.Height(),&MemDC,0,0,SRCCOPY);
MemDC.SelectObject(pOldBmp);
}
return TRUE;
}
However the background of my CDialogEx is being drawn with GDI+ 32bpp rendering like this:
BOOL CParentDialogEx::OnEraseBkgnd(CDC* pDC)
{
// Get GDI+ Graphics for the current Device Context
Graphics gr(*pDC);
// Get the client area
CRect clientRect;
GetClientRect(&clientRect);
// Draw the dialog background
// PLEASE NOTE: m_imgDlgBkgnd is a Gdiplus::Image with PNG format ==> 32bpp Image
gr.DrawImage(m_imgDlgBkgnd, 0, 0, clientRect.Width(), clientRect.Height());
}
This causes the first code snippet to make a backup of a black rectangle instead of the 32bpp drawn content to it. This again causes my button control to always have a black background.
To make my problem clear, please see the pictures below:
Button images are being drawn onto the CDialogEx background (normally):
Button images are being drawn with the first code snippet
As you can see GDI 24bpp cannot see the dialog background. It just assumes plain black background. Only GDI+ could see it. However I was not able to find a way to get a bitmap from a Gdiplus::Graphics object.
How can I get a 32bpp background backup in order to draw my transparent images correctly?
Using no backup images at all causes the alpha blending of GDI+ to blur the background more and more on every draw.
After 3 days of figuring around I found something by testing. This can actually be done way easier and with 32bpp rendering!
// Get the client area on the parent
CRect Rect;
GetWindowRect(&Rect);
CWnd *pParent = GetParent();
ASSERT(pParent);
pParent->ScreenToClient(&Rect);
// Get the Parent's DC
CDC *parentDC = pParent->GetDC();
// GDI Code (only 24bpp support)
//CDC MemDC;
//CBitmap bmp;
//MemDC.CreateCompatibleDC(parentDC);
//bmp.CreateCompatibleBitmap(parentDC,Rect.Width(),Rect.Height());
//CBitmap *pOldBmp = MemDC.SelectObject(&bmp);
//MemDC.BitBlt(0,0,Rect.Width(),Rect.Height(),parentDC,Rect.left,Rect.top,SRCCOPY);
//MemDC.SelectObject(pOldBmp);
// GDI+ Code with 32 bpp support (here comes the important change)
Bitmap* bmp = new Bitmap(Rect.Width(), Rect.Height());
Graphics bmpGraphics(bmp);
HDC hBmpDC = bmpGraphics.GetHDC();
BitBlt(hBmpDC, 0, 0, Rect.Width(), Rect.Height(), parentDC->GetSafeHdc(), Rect.left, Rect.top, SRCCOPY); // BitBlt is actually capable of doing 32bpp blts
// Release DCs
bmpGraphics.ReleaseDC(hBmpDC);
pParent->ReleaseDC(parentDC);
Here you go! 4 lines of code and you get a 32bpp Gdiplus::Bitmap! Which you can draw later on in OnEraseBkgnd:
// Get client Area
CRect rect;
GetClientRect(&rect);
// Draw copied background back onto the parent
Graphics gr(*pDC);
gr.DrawImage(bmp, 0, 0, rect.Width(), rect.Height());
Please note that the Bitmap should be a member variable for performance increase. It also has to be freed on the controls destruction.
Instead of Gdiplus::Image, use Gdiplus::Bitmap which has GetHBITMAP
In this example the background is set to red for png image with transparent background:
Gdiplus::Bitmap gdi_bitmap(L"c:\\test\\filename.png");
HBITMAP hbitmap = NULL;
gdi_bitmap.GetHBITMAP(Gdiplus::Color(128, 0, 0), &hbitmap);
CBitmap bmp;
bmp.Attach(hbitmap);
CDC memdc;
memdc.CreateCompatibleDC(pDC);
memdc.SelectObject(&bmp);
pDC->StretchBlt(0, 0, clientRect.Width(), clientRect.Height(), &memdc, 0, 0,
gdi_bitmap.GetWidth(), gdi_bitmap.GetHeight(), SRCCOPY);
Alternatively, you can use a brush to handle the painting:
CBrush m_Brush;
BOOL CMyDialog::OnInitDialog()
{
CDialogEx::OnInitDialog();
Gdiplus::Bitmap bitmap(L"c:\\test\\filename.png");
HBITMAP hbitmap = NULL;
bitmap.GetHBITMAP(Gdiplus::Color(128, 0, 0), &hbitmap);
CBitmap bmp;
bmp.Attach(hbitmap);
m_Brush.CreatePatternBrush(&bmp);
return 1;
}
HBRUSH CMyDialog::OnCtlColor(CDC* pDC, CWnd* wnd, UINT nCtlColor)
{
//uncomment these two lines if you only want to change dialog background
//if (wnd != this)
// return CDialogEx::OnCtlColor(pDC, wnd, nCtlColor);
if (nCtlColor == CTLCOLOR_STATIC)
{
pDC->SetTextColor(RGB(0, 128, 128));
pDC->SetBkMode(TRANSPARENT);
}
CPoint pt(0, 0);
if (wnd != this)
MapWindowPoints(wnd, &pt, 1);
pDC->SetBrushOrg(pt);
return m_Brush;
}
BOOL CMyDialog::OnEraseBkgnd(CDC* pDC)
{//don't do anything
return CDialogEx::OnEraseBkgnd(pDC);
}

MFC: How to copy bitmap using OLE clipboard

Now I have a CImage img, and I wanted to copy the bitmap to the OLE clipboard.
What I did is the following
HBITMAP hImage = img.Detach();
CBitmap *pBitmap = CBitmap::FromHandle(hImage);
BITMAP bm;
pBitmap->GetObject(sizeof(bm), &bm);
HBITMAP hImage = Detach();
// the bitmap src
CBitmap *pBitmap = CBitmap::FromHandle(hImage);
BITMAP bm;
pBitmap->GetObject(sizeof(bm), &bm);
// the copy (dest)
CBitmap bitmap;
bitmap.CreateBitmapIndirect(&bm);
// create mem dc and select src and dest bitmaps
CDC dcMemSrc, dcMemDest;
dcMemSrc.CreateCompatibleDC(NULL);
CBitmap* pOldBitmapSrc = dcMemSrc.SelectObject(pBitmap);
dcMemDest.CreateCompatibleDC(NULL);
CBitmap* pOldBitmapDest = dcMemDest.SelectObject(&bitmap);
// bit-blt
dcMemDest.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcMemSrc,
0, 0, SRCCOPY);
HBITMAP hBitmap = (HBITMAP)bitmap.Detach();
// restore mem dc
dcMemDest.SelectObject(pOldBitmapDest);
dcMemSrc.SelectObject(pOldBitmapSrc);
// Place the copy on the clipboard.
AfxOleInit();
CoInitialize(NULL);
COleDataSource* pods = new COleDataSource;
pods->CacheGlobalData(CF_BITMAP, hBitmap);
pods->SetClipboard();
CoUninitialize();
I tested the program, it just doesn't work, I tried to CTRL+V to the Windows Paint program, but it complained the data cannot be pasted in it. If I use the Legacy Clipboard(just replace the last 6 lines with ::OpenClipboard(hWnd), ::EmptyClipboard(), ::SetClipboardData(CF_BITMAP, hBitmap), ::CloseClipboard() ), it works fine. I have no experience working with COM and OLE, can anyone help? Many thanks.

Screen capture only returns a black image

I'm trying to take a screen capture of the main dialog in my MFC application and save it as an image file. I tried about every example I could find online and always end up with the same result: the image file has the correct dimensions (I tried this with dialogs other than the main one just to be sure), but it is all black. My most recent solution is using the CBitmap class to transfer the main dialog handle to a CImage. Here is my code:
CWnd* mainWindow;
CDC* mainWindowDC;
CBitmap bitmapToSave;
CImage imageToSave;
CRect windowRect;
//Get main window context and create bitmap from it
mainWindow = AfxGetMainWnd();
mainWindowDC = mainWindow->GetWindowDC();
mainWindow->GetWindowRect(&windowRect);
bitmapToSave.CreateCompatibleBitmap(mainWindowDC, windowRect.Width(), windowRect.Height());
imageToSave.Attach(bitmapToSave);
imageToSave.Save("C:\\Capture\\image.bmp", Gdiplus::ImageFormatBMP);
Here is the way to do it:
HRESULT CaptureScreen(const CString& sImageFilePath)
{
CWnd* pMainWnd = AfxGetMainWnd();
CRect rc;
pMainWnd->GetWindowRect(rc);
CImage img;
img.Create(rc.Width(), rc.Height(), 24);
CDC memdc;
CDC* pDC = pMainWnd->GetWindowDC();
memdc.CreateCompatibleDC(pDC);
CBitmap* pOldBitmap = memdc.SelectObject(CBitmap::FromHandle((HBITMAP)img));
memdc.BitBlt(0, 0, rc.Width(), rc.Height(), pDC, 0, 0, SRCCOPY);
memdc.SelectObject(pOldBitmap);
return img.Save(sImageFilePath, Gdiplus::ImageFormatPNG);
}
Please also take a look at this nice implementation: http://www.codeguru.com/cpp/article.php/c18347/C-Programming-Easy-Screen-Capture-Using-MFCATL.htm
You created the bitmap, but you didn't put anything into it. You need to blit from one DC to another to make a copy of what's on the screen.
// ...
CMemDC dcMem;
dcMem.CreateCompatibleDC(&mainWindowDC);
CBitmap * pOld = dcMem.SelectObject(&bitmapToSave);
dcMem.BitBlt(0, 0, windowRect.Width(), windowRect.Height(), &mainWindowDC, windowRect.left, windowRect.top, SRCCOPY);
dcMem.SelectObject(pOld);
// ...

Displaying image in WIN32, Why its not displayed?

I want to load a BitMap image in a pic box I created inside a window...picBoxDisp is created using following mechanism..
picBoxDisp = CreateWindow("STATIC", "image box",
WS_VISIBLE |WS_CHILD | SS_BITMAP |WS_TABSTOP | WS_BORDER,
50, 50, 250, 300, hwnd , (HMENU)10000, NULL, NULL);
Now Next I created a hBitmap object and loaded an image in to it...
hBitmap = (HBITMAP) LoadImage(NULL,szFileName,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE| LR_DEFAULTSIZE);
SendMessage(picBoxDisp,STM_SETIMAGE,(WPARAM) IMAGE_BITMAP,(LPARAM) NULL);
//now assign the new image
//Create a compatible DC for the original size bitmap, for example originalMemDc.
HDC originalDC = GetDC((HWND)hBitmap);
HDC originalMemDC = CreateCompatibleDC(originalDC);
if(originalMemDC==NULL){
MessageBox(NULL,"Problem while creating DC.","Error",MB_OK);
}
//Select hBitmap into originalMemDc.
SelectObject(originalMemDC,hBitmap);
//Create a compatible DC for the resized bitmap, for example resizedMemDc.
HDC picBoxDC = GetDC(picBoxDisp);
HDC resizedMemDC = CreateCompatibleDC(picBoxDC);
//Create a compatible bitmap of the wanted size for the resized bitmap,
HBITMAP hResizedBitmap = CreateCompatibleBitmap(picBoxDC,250,300);
//Select hResizedBitmap into resizedMemDc.
SelectObject(resizedMemDC,hResizedBitmap);
//Stretch-blit from originalMemDc to resizedMemDc.
//BitBlt(resizedMemDC,0,0,250,300,originalMemDC,0,0,SRCCOPY);
BITMAP bmp_old,bmp_new;
GetObject(hBitmap,sizeof(bmp_old),&bmp_old);
GetObject(hResizedBitmap,sizeof(bmp_new),&bmp_new);
StretchBlt ( resizedMemDC,0,0,bmp_new.bmWidth,bmp_new.bmHeight,
originalMemDC,0,0,bmp_old.bmWidth,bmp_new.bmHeight,
SRCCOPY);
////De-select the bitmaps.
if((resizedMemDC==NULL)||(hResizedBitmap == NULL)) {
MessageBox(NULL,"Something is NULL","Error",MB_OK);
}
else
//Set hResizedBitmap as the label image with STM_SETIMAGE
SendMessage(picBoxDisp,STM_SETIMAGE, (WPARAM) IMAGE_BITMAP,(LPARAM) hResizedBitmap);
I just cant understand, why the above code is not working ?
Thanks in advance,
You misunderstood the STM_SETIMAGE usage. Do this:
hBitmap = (HBITMAP)::LoadImage(NULL, szFileName, IMAGE_BITMAP,
0, 0, LR_LOADFROMFILE| LR_DEFAULTSIZE);
if (hBitmap != NULL)
{
::SendMessage(picBoxDisp, STM_SETIMAGE,
(WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
}
EDIT: If you want to resize the bitmap before setting it as the label image, then follow this scheme for the simplest possible way to do it (with sub-optimal quality in the resized image...):
Create a compatible DC for the original size bitmap, for example originalMemDc.
Select hBitmap into originalMemDc.
Create a compatible DC for the resized bitmap, for example resizedMemDc.
Create a compatible bitmap of the wanted size for the resized bitmap, for example hResizedBitmap.
Select hResizedBitmap into resizedMemDc.
Stretch-blit from originalMemDc to resizedMemDc.
De-select the bitmaps.
Set hResizedBitmap as the label image with STM_SETIMAGE
Should work!
The static control won't stretch the image to its size. You could use SS_CENTERIMAGE but it either clips or fills the empty space with the color of the top left pixel (see http://msdn.microsoft.com/en-US/library/b7w5x74z.aspx). You'd have to stretch the bitmap yourself before sending it to the static control.
You're trying to assign an image to a static control, so you don't need to draw the image but to set the image on it.
// set the image
HBITMAP hold = (HBITMAP)SendMessage(hpicBoxDc, STM_SETIMAGE, IMAGE_BITMAP, LPARAM(hBitmap));
// clear the old image
if(hold && hold != hBitmap)
DeleteObject(hold );

Drawing On GDI+ Graphics Object a Bitmap using StretchDIBits for Scaling

I am drawing bitmap images on graphics object using DrawImage method But the Images are in large number so it is taking too much time for Drawing. I have read in this forum that using StretchDIBits takes less time for Drawing.
I am scaling the image by calling Drawimage but i want any other efficent method.
I have a Vector of Bitmap* & i want to draw each Bitmap on graphics.
HDC orghDC = graphics.GetHDC();
CDC *dc = CDC::FromHandle(orghDC);
m_vImgFrames is image vector containg Bitmap*. I have taken HBITMAP from Bitmap*.
HBITMAP hBitmap;
m_vImgFrames[0]->GetHBITMAP(Color(255,0,0),&hBitmap);
Using this HBITMAP i want to draw on orghDC & finally on graphics. So I want to know how StretchDIBits can be used for scaling the Bitmap and finally draw on Graphics Object.
I am new to this forum.Any ideas or code can be helpful
Instead of using StretchDIBits, why not use the GDI+ API directly to scale the bitmap?:
CRect rc( 0, 0, 20, 30 );
graphics.DrawImage( (Image*)m_vImgFrames[0],
rc.left, rc.top, rc.Width(), rc.Height() );
To use StretchDIBits with Gdiplus::Bitmap you could do the following:
// get HBITMAP
HBITMAP hBitmap;
m_vImgFrames[0]->GetHBITMAP( Gdiplus::Color(), &hBitmap );
// get bits and additional info
BITMAP bmp = {};
::GetObject( hBitmap, sizeof(bmp), &bmp );
// prepare BITMAPINFO
BITMAPINFO bminfo = {};
bminfo.bmiHeader.biSize = sizeof( BITMAPINFO );
bminfo.bmiHeader.biWidth = bmp.bmWidth;
bminfo.bmiHeader.biHeight = bmp.bmHeight;
bminfo.bmiHeader.biBitCount = bmp.bmBitsPixel;
bminfo.bmiHeader.biCompression = BI_RGB;
bminfo.bmiHeader.biPlanes = bmp.bmPlanes;
bminfo.bmiHeader.biSizeImage = bmp.bmWidthBytes*bmp.bmHeight*4; // 4 stands for 32bpp
// select stretch mode
::SetStretchBltMode( HALFTONE );
// draw
::StretchDIBits( hDC, 0, 0, new_cx, new_cy, 0, 0,
m_vImgFrames[0]->GetWidth(), m_vImgFrames[0]->GetHeight(),
bmp.bmBits, &bminfo, DIB_RGB_COLORS, SRCCOPY );
But this doesn't looks much faster on my machine than simple Graphics::DrawImage.