I have used DWM API to create a Aero glass window by calling DwmExtendFrameIntoClientArea.
void CMainFrame::OnActivate(UINT nState,CWnd* pWndOther,BOOL bMinimized )
{
CFrameWnd::OnActivate(nState,pWndOther,bMinimized);
BOOL fDwmEnabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&fDwmEnabled)))
{
if(nState == WA_ACTIVE )
{
MARGINS margins ={-1};
HRESULT hr = DwmExtendFrameIntoClientArea(m_hWnd, &margins);
if (!SUCCEEDED(hr))
TRACE0("Failed to DwmExtendFrameIntoClientArea\n");
}
}
}
And then, I draw a bitmap image on the window (I also have tried to call DrawThemeIcon and CImageList::Draw to draw the image).
void CMainFrame::DisplayBitmap( CBitmap *p, CDC *pDC)
{
CDC dcMemory;
BITMAP bm;
dcMemory.CreateCompatibleDC(pDC);
dcMemory.SelectObject(p);
p->GetBitmap(&bm);
pDC->BitBlt(100,100,bm.bmWidth,bm.bmHeight,&dcMemory,0,0,SRCCOPY);
}
void CMainFrame::OnNcPaint(){
CFrameWnd::OnNcPaint();
CDC* dc = GetWindowDC();
CRect rct;
GetWindowRect(&rct);
dc->FillSolidRect(0, 0, rct.right - rct.left, rct.bottom - rct.top, RGB(0, 0, 0));
DisplayBitmap(&bmpBtn,dc);
ReleaseDC(dc);
}
I found out the image is ugly and translucency. How to draw a opaque image on the Aero glass window?
Update:
I still need somebody who can provide another solution without using GDI+ library for me. Thanks!
Solution 1:
CMainFrame::CMainFrame()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
//Initialize GDI+.
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
m_pImage = m_pImage->FromFile (_T("lena.bmp"));
}
void CMainFrame::OnNcPaint(){
CFrameWnd::OnNcPaint();
CDC* dc = GetWindowDC();
CRect rct;
GetWindowRect(&rct);
dc->FillSolidRect(0, 0, rct.right - rct.left, rct.bottom - rct.top, RGB(0, 0, 0));
CPaintDC gdc(this);
Graphics g(gdc);
//I don't why the image will disappear sometimes when I move the window.
g.DrawImage ( m_pImage, 0, 0 );
ReleaseDC(dc);
}
The Result:
Related
I am a beginner, and I have made a program in direct2d using c++ in visual studio 2015, just a very basic program that draws a white square on a black background , and it does show the square in a small window. But the problem is that if the user maximizes that window the vertical dimension of the drawing becomes elongated and the square becomes rectangle.
This is the screenshot of the window before maximizing it:
enter image description here
And this is the screenshot of the window after maximizing it:
enter image description here
What am I doing wrong ?
here is my code and thanks in advance:
ID2D1HwndRenderTarget* pRT = NULL;
ID2D1Factory* pD2DFactory = NULL;
ID2D1SolidColorBrush* pBlackBrush = NULL;
ID2D1SolidColorBrush* pRedBrush = NULL;
HRESULT hr;
RECT rc;
POINT pt;
void draw(){
pRT->BeginDraw();
pRT->FillRectangle( D2D1::RectF((FLOAT)rc.left,
(FLOAT)rc.top,(FLOAT)rc.right, (FLOAT)rc.bottom),pBlackBrush);
pRT->FillRectangle( D2D1::RectF((FLOAT)pt.x,
(FLOAT)pt.y,pt.x + 50.0f ,pt.y + 50.0f),pRedBrush);
hr = pRT->EndDraw();
}
int APIENTRY wWinMain(/* wWinMain parameters */){
/*
...
window making
...
*/
hr = D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,&pD2DFactory );
// Obtain the size of the drawing area.
GetClientRect(hWnd, &rc);
// Create a Direct2D render target
hr =pD2DFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(
hWnd,D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)
),
&pRT
);
if (SUCCEEDED(hr)){
pRT->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::White),
&pRedBrush
);
pRT->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Black),
&pBlackBrush
);
}
pt.x = 0;
pt.y = 0;
while (1) {
PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE);
if (msg.message == WM_QUIT) break;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
draw();
}
}
It seems like you don't resize the render target when your window resizes. I expect you run into similar problems not just from maximizing, but when you resize your window too? See https://learn.microsoft.com/en-us/windows/desktop/LearnWin32/dpi-and-device-independent-pixels#resizing-the-render-target.
Code snippet from the linked document:
void Resize()
{
if (pRenderTarget != NULL)
{
RECT rc;
GetClientRect(m_hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right, rc.bottom);
pRenderTarget->Resize(size);
InvalidateRect(m_hwnd, NULL, FALSE);
}
}
You'll want to call this by catching WM_SIZE.
I want to use a bitmap to read the current view window, make some changes to it and output the bitmap back to the view. The program is supposed to read a white window and draw a square against this background. However, the image that I see in the final window is a square in a black background.
void CScribbleView::OnLButtonDown(UINT, CPoint point)
{
//CClientDC dc(this);
//OnPrepareDC(&dc);
HBITMAP initialMap;
RECT t;
BITMAP bmpScreen;
GetClientRect(&t);
initialMap = CreateCompatibleBitmap(GetDC()->m_hDC,t.right-t.left,t.bottom-t.top);
HDC tempDC = CreateCompatibleDC(GetDC()->m_hDC);
SelectObject(tempDC,initialMap);
SelectObject(tempDC,getStockObject(BLACK_PEN);
SelectObject(tempDC,GetStockObject(WHITE_BRUSH));
Rectangle(tempDC,100,100,200,200);
BitBlt(GetDC()->m_hDC,clientR.left,clientR.top,clientR.right-clientR.left,t.bottom-clientR.top,tempDC,0,0,SRCCOPY);
}
This is the output:
I suspect that the bitmap was not properly read from my original DC. So I later decided to use a different way of retrieving DC, the CClientDC, instead of GetClientDC()->m_hDC.
void CScribbleView::OnLButtonDown(UINT, CPoint point)
{
CClientDC dc(this);
OnPrepareDC(&dc);
HBITMAP initialMap;
RECT t;
BITMAP bmpScreen;
GetClientRect(&t);
initialMap = CreateCompatibleBitmap(dc.m_hDC,t.right-t.left,t.bottom-t.top);
HDC tempDC = CreateCompatibleDC(dc.m_hDC);
SelectObject(tempDC,initialMap);
SelectObject(tempDC,getStockObject(BLACK_PEN);
SelectObject(tempDC,GetStockObject(WHITE_BRUSH));
Rectangle(tempDC,100,100,200,200);
BitBlt(dc.m_hDC,clientR.left,clientR.top,clientR.right-clientR.left,t.bottom-clientR.top,tempDC,0,0,SRCCOPY);
}
Now, the new program doesn't show anything; it is the same white background as I had originally started with. What is the difference between these two DC's and how can I fix my problem?
These WinAPI functions all require cleanup: ::CetDC, ::CreateCompatibleDC, ::CreateCompatibleBitmap. See documentation for each of them. Without cleanup, your program could quickly use its 10,000 GDI resource limit and crash.
You don't have to worry about cleanup if you use the MFC version of these WinAPI functions (you should still see the docs to make sure). This is MFC example for double-buffering:
void foo::OnLButtonDown(UINT nFlags, CPoint point)
{
CWnd::OnLButtonDown(nFlags, point);
CClientDC dc(this);
CRect rect;
GetClientRect(&rect);
//create memory dc
CDC memdc;
memdc.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
memdc.SelectObject(bitmap);
//some random drawings:
memdc.SelectObject(GetStockObject(BLACK_PEN));
memdc.SelectObject(GetStockObject(GRAY_BRUSH));
memdc.Rectangle(10, 10, 100, 100);
//draw memory DC to destination DC
dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memdc, 0, 0, SRCCOPY);
};
Your goal is probably to draw on screen buffer, as suggested in comments. In which case you declare memdc and bitmap as member data:
//declare member data
CDC m_memdc;
CBitmap m_bitmap;
CRect m_rect;
CPoint m_point;
void foo::initialize_once()
{
ASSERT(IsWindow(m_hWnd));
GetClientRect(&m_rect);
//create memdc
m_memdc.CreateCompatibleDC(0);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&m_memdc, m_rect.Width(), m_rect.Height());
m_memdc.SelectObject(bitmap);
//initialize memdc background color
m_memdc.FillSolidRect(m_rect, RGB(255,255,255));
}
void foo::OnLButtonDown(UINT nFlags, CPoint point)
{
__super::OnLButtonDown(nFlags, point);
m_point = point;
m_memdc.MoveTo(point);
};
void OnMouseMove(UINT nFlags, CPoint point)
{
__super::OnMouseMove(nFlags, point);
if (!(nFlags & MK_LBUTTON)) return;
m_point = point;
m_memdc.LineTo(point);
CClientDC dc(this);
dc.BitBlt(0, 0, m_rect.Width(), m_rect.Height(), &m_memdc, 0, 0, SRCCOPY);
}
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);
}
Well simply what I need to do is to create a grid of rectangle and fill any selected case of that grid at the user click. thus I save the parameters (color, position..). The problem is when I try to fill the case at the click event I can't any changes maybe because the device context is changed even if I use GetDC() method. So is there any way to save the current created on OnDraw() method for exmaple and use it somewhere in another function, I tried to use SaveDC() and RestoreDC() but in vain.
Here's some of my code:
void CXThiefManView::OnDraw(CDC* pDC)
{
CXThiefManDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CRect rcClient;
GetClientRect(&rcClient);
// Get the background color of the board
COLORREF clr = pDoc->GetBoardSpace(-1, -1);
// Draw the background first
pDC->FillSolidRect(&rcClient, clr);
// Create the brush for drawing
CBrush br;
br.CreateStockObject(HOLLOW_BRUSH);
CBrush* pbrOld = pDC->SelectObject(&br);
// Draw the squares
for (int row = 0; row < pDoc->GetRows(); row++)
{
for (int col = 0; col < pDoc->GetColumns(); col++)
{
// Get the color for this board space
clr = pDoc->GetBoardSpace(row, col);
// Calculate the size and position of this space
CRect rcBlock;
rcBlock.top = row * pDoc->GetHeight();
rcBlock.left = col * pDoc->GetWidth();
rcBlock.right = rcBlock.left + pDoc->GetWidth();
rcBlock.bottom = rcBlock.top + pDoc->GetHeight();
// Fill in the block with the correct color
pDC->FillSolidRect(&rcBlock, clr);
// Draw the block outline
pDC->Rectangle(&rcBlock);
}
}
saveState = pDC->SaveDC();
DrawItem(pDC, pDoc->GetThiefRow(), pDoc->GetThiefCol(), pDoc->GetThiefColor());
}
void CXThiefManView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CView::OnLButtonDown(nFlags, point);
CXThiefManDoc* pDoc = GetDocument();
// the draw item here to fill the case
DrawItem(GetDC(), 5, 5, RGB(0, 0, 0));
}
Underneath all those MFC function, the WINAPI functions of the same name are being called. In WINAPI, GetDC returns a new device context. SaveDC & RestoreDC allow you to save and restore the state (brush, font etc...) of a specific DC handle, but that doesn't cross to other handles.
There's nothing MFC can do about it, nor can you. You have to reconfigure the new DC handle for your needs.
Handle from GetDC has to be released by ReleaseDC. Please see GetDC
Or use CClientDC dc(this) which has automatic cleanup, it's usage is similar, for example Draw(&dc,...);.
As to your main question, you can save the drawing on a bitmap. The bitmap can be saved.
define:
CBitmap m_bitmap;
CDC m_memdc;
initialize the bitmap and dc:
CClientDC dc(this);
m_bitmap.CreateCompatibleBitmap(&dc, 2000, 1200);
//this creates a bitmap as big as the screen...
m_memdc.CreateCompatibleDC(&dc);
m_memdc.SelectObject(m_bitmap);
m_memdc.FillSolidRect(0, 0, 2000, 1200, RGB(255, 255, 255));
//draw on m_memdc, then draw memdc on to dc
Usage:
void CMyWindow::OnPaint()
{
CWnd::OnPaint();
CClientDC dc(this);
CRect rc;
GetClientRect(&rc);
dc.BitBlt(0, 0, rc.Width(), rc.Height(), &m_memdc, 0, 0, SRCCOPY);
}
void CMyWindow::OnMouseMove(UINT k, CPoint p)
{
CClientDC dc(this);
CRect rc;
GetClientRect(&rc);
if (k & MK_LBUTTON)
{
//draw on m_memdc, (instead of drawing on dc)
m_memdc.SetPixel(p.x, p.y, RGB(0,0,0));
m_memdc.SetPixel(p.x, p.y+1, RGB(0,0,255));
//when finished drawing, draw m_memdc on to dc
dc.BitBlt(0, 0, rc.Width(), rc.Height(), &m_memdc, 0, 0, SRCCOPY);
}
}
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);
// ...