I am attempting to load in a selected .bmp file and display it in WM_PAINT. The function is being ran by the code so visual studio does see it.
here is the code that seems to be not working:
case WM_PAINT:
{
_hWindowDC = BeginPaint(_hwnd, &ps);
if (g_bIsFileLoaded)
{
std::wstring wsFileName = g_vecImageFileNames.back();
g_vecImageFileNames.pop_back();
std::string File(wsFileName.length(), ' ');
std::copy(wsFileName.begin(), wsFileName.end(), File.begin());
HBITMAP h_BitMap = (HBITMAP)LoadImageA(g_hInstance, File.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HDC h_HomeDC = CreateCompatibleDC(_hWindowDC);
BITMAP BitMap;
int i_Final = GetObject(reinterpret_cast<HGDIOBJ>(h_BitMap), sizeof(BITMAP), reinterpret_cast<LPVOID>(&BitMap));
HBITMAP h_OldBit = (HBITMAP)::SelectObject(h_HomeDC, h_BitMap);
SetStretchBltMode(_hWindowDC, HALFTONE);
BOOL PaintBit = StretchBlt(_hWindowDC, 1, 1, 50, 50, h_HomeDC, 0, 0, BitMap.bmWidth, BitMap.bmHeight, SRCCOPY);
::SelectObject(h_HomeDC, h_OldBit);
::DeleteDC(h_HomeDC);
::DeleteObject(h_BitMap);
InvalidateRect(_hwnd, NULL, NULL);
g_bIsFileLoaded = false;
}
EndPaint(_hwnd, &ps);
return (0);
}
break;
Related
I'm working on a C++ ATL COM thumbnail/preview/search project, but its bitmap displaying code behavior is monochrome during the Thumbnail process instead of the colored. The Preview process is colored as expected, using the same function.
I used the ATL Wizard to create the IThumbnailProvider and his friends. My small changes are: I replaced the color from black to pink in the document::OnDrawThumbnail and I wrote the document::OnDrawThumbnail into CPreviewCtrl::DoPaint. I've read the "new DC always monochrome" thing in the MS spec but I could not get colored DC even if a changed the original ATL code OnDrawThumbnail(GetDC(NULL), &rcBounds);. The CreateCompatibleDC(NULL) and CreateCompatibleDC(hDrawDC) were dead-end too.
document.cpp (changed)
// Almost the default sample code, but hDrawBrush is changed to pink
void document::OnDrawThumbnail(HDC hDrawDC, LPRECT lprcBounds)
{
HBRUSH hDrawBrush = CreateSolidBrush(RGB(255, 0, 255)); // pink
FillRect(hDrawDC, lprcBounds, hDrawBrush);
HFONT hStockFont = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
LOGFONT lf;
GetObject(hStockFont, sizeof(LOGFONT), &lf);
lf.lfHeight = 34;
HFONT hDrawFont = CreateFontIndirect(&lf);
HFONT hOldFont = (HFONT) SelectObject(hDrawDC, hDrawFont);
CString strText = _T("TODO: implement thumbnail drawing here");
DrawText(hDrawDC, strText, strText.GetLength(), lprcBounds, DT_CENTER | DT_WORDBREAK);
SelectObject(hDrawDC, hDrawFont);
SelectObject(hDrawDC, hOldFont);
DeleteObject(hDrawBrush);
DeleteObject(hDrawFont);
}
PreviewHandler.h (changed, it is called by the Preview)
// CPreviewCtrl implementation
class CPreviewCtrl : public CAtlPreviewCtrlImpl
{
protected:
virtual void DoPaint(HDC hdc)
{
// you can obtain a pointer to IDocument as follows
// CMyDoc* pDoc = (CMyDoc*)m_pDocument;
/*
CString strData = _T("Draw Rich Preview content here.");
TextOut(hdc, 10, 20, strData, strData.GetLength());
*/
RECT rc{};
rc.right = 290;
rc.bottom = 290;
dynamic_cast<document*>(m_pDocument)->OnDrawThumbnail(hdc, &rc);
}
};
atlhandlerimpl.h (unchanged, from VS SDK \atlmfc\include\ which is called be the thumbnail provider)
ATLPREFAST_SUPPRESS(6101)
_Success_(return != FALSE) BOOL GetThumbnail(
_In_ UINT cx,
_Out_ HBITMAP* phbmp,
_Out_ WTS_ALPHATYPE* /* pdwAlpha */)
{
HDC hdc = ::GetDC(NULL);
RECT rcBounds;
SetRect(&rcBounds, 0, 0, cx, cx);
HDC hDrawDC = CreateCompatibleDC(hdc);
if (hDrawDC == NULL)
{
ReleaseDC(NULL, hdc);
return FALSE;
}
HBITMAP hBmp = CreateCompatibleBitmap(hDrawDC, cx, cx);
if (hBmp == NULL)
{
ReleaseDC(NULL, hdc);
DeleteDC(hDrawDC);
return FALSE;
}
HBITMAP hOldBitmap = (HBITMAP) SelectObject(hDrawDC, hBmp);
// Here you need to draw the document's data
OnDrawThumbnail(hDrawDC, &rcBounds);
SelectObject(hDrawDC, hOldBitmap);
DeleteDC(hDrawDC);
ReleaseDC(NULL, hdc);
*phbmp = hBmp;
return TRUE;
}
ATLPREFAST_UNSUPPRESS()
Sample thumbnail in the File Explorer
Github helped me. It is definitely an ATL SDK bug.
BUG report on the VS developer community
Solution on the www.patthoyts.tk
And the github repo which helped me: abhimanyusirohi/ThumbFish
In the atlhandlerimpl.h provided GetThumbnail must be override:
BOOL document::GetThumbnail(
_In_ UINT cx,
_Out_ HBITMAP * phbmp,
_Out_ WTS_ALPHATYPE* /* pdwAlpha */)
{
BOOL br = FALSE;
HDC hdc = ::GetDC(NULL);
HDC hDrawDC = CreateCompatibleDC(hdc);
if (hDrawDC != NULL)
{
void* bits = 0;
RECT rcBounds;
SetRect(&rcBounds, 0, 0, cx, cx);
BITMAPINFO bi = { 0 };
bi.bmiHeader.biWidth = cx;
bi.bmiHeader.biHeight = cx;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biSizeImage = 0;
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biClrUsed = 0;
bi.bmiHeader.biClrImportant = 0;
HBITMAP hBmp = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &bits, NULL, 0);
if (hBmp != NULL)
{
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hDrawDC, hBmp);
OnDrawThumbnail(hDrawDC, &rcBounds);
SelectObject(hDrawDC, hOldBitmap);
*phbmp = hBmp;
br = TRUE;
}
DeleteDC(hDrawDC);
}
ReleaseDC(NULL, hdc);
return br;
}
I am using this code:
m_bmpSwap.LoadBitmap(IDB_BITMAP2);
pMnuPopup->SetMenuItemBitmaps(0, MF_BYPOSITION, &m_bmpSwap, &m_bmpSwap);
It looks like:
It was only a test image:
How exactly do I get my image to look as if it has a transparent background?
It is 24 bit image.
I have seen this but I can't work it out.
I adjusted to a 8 bit image with 192/192/192 as the background and loaded like this:
HBITMAP hBmp;
hBmp = (HBITMAP)::LoadImage(AfxGetResourceHandle(),
MAKEINTRESOURCE(IDB_BITMAP2),
IMAGE_BITMAP,
0, 0, // cx,cy
LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS);
m_bmpSwap.Attach(hBmp);
pMnuPopup->SetMenuItemBitmaps(0, MF_BYPOSITION, &m_bmpSwap, &m_bmpSwap);
That seems better if I am not running WindowsBlinds:
But when I put WindowsBlinds back on and show it again:
I am colourblind myself, but I can tell that the background actually matches the dialog background and not the menu colour background.
Is this the best I can do?
Just how can I have a 24 bit or 32 bit image as a menu bitmap?
Add LR_LOADTRANSPARENT flag as well as LR_LOADMAP3DCOLORS
This will work with 8-bit or 4-bit images (not tested with Windows blind)
Or you can manually change the background color
void swap_color(HBITMAP hbmp)
{
if(!hbmp)
return;
HDC hdc = ::GetDC(HWND_DESKTOP);
BITMAP bm;
GetObject(hbmp, sizeof(bm), &bm);
BITMAPINFO bi = { 0 };
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = bm.bmWidth;
bi.bmiHeader.biHeight = bm.bmHeight;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
std::vector<uint32_t> pixels(bm.bmWidth * bm.bmHeight);
GetDIBits(hdc, hbmp, 0, bm.bmHeight, &pixels[0], &bi, DIB_RGB_COLORS);
//assume that the color at (0,0) is the background color
uint32_t color_old = pixels[0];
//this is the new background color
uint32_t bk = GetSysColor(COLOR_MENU);
//swap RGB with BGR
uint32_t color_new = RGB(GetBValue(bk), GetGValue(bk), GetRValue(bk));
for (auto &pixel : pixels)
if(pixel == color_old)
pixel = color_new;
SetDIBits(hdc, hbmp, 0, bm.bmHeight, &pixels[0], &bi, DIB_RGB_COLORS);
::ReleaseDC(HWND_DESKTOP, hdc);
}
Usage:
CBitmap bmp;
bmp.LoadBitmap(IDB_BITMAP1);
swap_color(bmp);
menu.SetMenuItemBitmaps(0, MF_BYPOSITION, &bmp, &bmp);
I found this article. I replicate the made code here:
#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;
}
Now, if I add a 24 bit bitmap to my project, and set the background as 71/71/71 and load it like this:
HBITMAP hBmp = (HBITMAP)::LoadImage(AfxGetResourceHandle(),
MAKEINTRESOURCE(IDB_BITMAP1),
IMAGE_BITMAP,
0, 0, // cx,cy
LR_LOADTRANSPARENT | LR_LOADMAP3DCOLORS);
HBITMAP hBmp2 = ReplaceColor(hBmp, RGB(71, 71, 71), GetSysColor(COLOR_MENU), NULL);
DeleteObject(hBmp);
m_bmpSwap.Attach(hBmp2);
pMnuPopup->SetMenuItemBitmaps(0, MF_BYPOSITION, &m_bmpSwap, &m_bmpSwap);
The result:
I'm not good at winapi field.. so I want to ask your help.
Below is the code.
void DrawScreen(HDC hdc)
{
int x = 0;
int y = 0;
BITMAP bit;
HBITMAP backBitmap;
HBITMAP hOldBitmap;
HBITMAP hOldBitmap2;
HDC backMemDC, MemDc;
backBitmap = CreateCompatibleBitmap(hdc, rt.right, rt.bottom);
backMemDC = CreateCompatibleDC(hdc);
MemDc = CreateCompatibleDC(backMemDC);
//hOldBitmap2 = (HBITMAP)SelectObject(MemDc, backBmitmap2);
FillRect(backMemDC, &rt, (HBRUSH)GetStockObject(WHITE_BRUSH));
hOldBitmap = (HBITMAP)SelectObject(backMemDC, backBitmap);
for ( y = 0; y < 3; ++y)
{
for ( x = 0; x < 3; ++x)
{
switch (cChessBoard[y][x])
{
case '#':
iBit = WHITE_BOARD;
break;
case '#':
iBit = BLACK_BOARD;
break;
SelectObject(MemDc, hBit[iBit]);
}//end of switch
hOldBitmap2 = (HBITMAP)SelectObject(MemDc,hBit[iBit]);
BitBlt(backMemDC, x*BW, y*BH, BW, BH, MemDc, 0, 0, SRCCOPY);
}//end of for
}//end of for
hOldBitmap2 = (HBITMAP)SelectObject(MemDc, hBit[PLAYER]);
BitBlt(backMemDC, Player.m_iX*BW, Player.m_iY*BH, BW, BH, MemDc, 0, 0, SRCCOPY);
//InvalidateRect(hWndMain, NULL, FALSE);
BitBlt(hdc, 0, 0, rt.right, rt.bottom, backMemDC, 0, 0, SRCCOPY);
ReleaseDC(hWndMain,hdc);
DeleteObject(hOldBitmap);
DeleteDC(backMemDC);
DeleteDC(MemDc);
}
As you can see from the code above, I want to do double buffering.
But I still can not stop flickering... Someone help me please?
Moving to memory dc and moving things to hdc is not right?
i have implemented double buffering in my application, using the code below.
After wards, i found that my child windows (ie, buttoms), was not animating like normal, so i added WS_EX_COMPOSITED to my parent window, and the child windows are now animating correctly. However, after adding WS_EX_COMPOSITED, my Menu that is created from my resource, is black, and not displaying properly.
So, how to add double buffering to my top Menu?
MainWinHwnd = CreateWindowEx(WS_EX_COMPOSITED, MainWinClassName, MainWinTitle,
WS_VISIBLE|WS_BORDER|WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_SIZEBOX|WS_CLIPCHILDREN,
NULL, NULL, 609, 440, NULL, NULL, hInstance, NULL);
if (!MainWinHwnd) return FALSE;
case WM_PAINT:
{
RECT WinRt;
RECT TxtRt;
HFONT TxtFont = NULL;
SIZE TxtSize;
HDC WinDC = GetDC(hWnd);
GetClientRect(hWnd, &WinRt);
HDC WinBuffer = CreateCompatibleDC(WinDC);
HBITMAP BitBuffer = CreateCompatibleBitmap(WinDC, WinRt.right, WinRt.bottom);
SelectObject(WinBuffer, BitBuffer);
FillRect(WinBuffer, &WinRt, (HBRUSH) (COLOR_BTNFACE + 1));
SetBkColor(WinBuffer, GetSysColor((COLOR_BTNFACE)));
DrawThemeBackground(ButtonThemeData, WinBuffer, BP_GROUPBOX, GBS_NORMAL, &GroupOptionBox, NULL);
DrawThemeBackground(DragDropThemeData, WinBuffer, RP_GRIPPER, 0, &DragDropItem, NULL);
for (DWORD I = 0; I < sizeof(MainWinLabels) / sizeof(CustomLabel); I++)
{
if (MainWinLabels[I].Font == NULL)
{
TxtFont = CreateFontA(MainWinLabels[I].cFontHeight, NULL, NULL, NULL, FW_DONTCARE,
MainWinLabels[I].cFontItalic, MainWinLabels[I].cFontUnderline,
MainWinLabels[I].cFontStrikeOut, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
MainWinLabels[I].cFontQuality, DEFAULT_PITCH, MainWinLabels[I].cFontFaceName);
if (TxtFont == NULL) continue;
SelectObject(WinBuffer, TxtFont);
}
else SelectObject(WinBuffer, *MainWinLabels[I].Font);
SetTextColor(WinBuffer, MainWinLabels[I].Color);
TxtRt.left = MainWinLabels[I].X;
TxtRt.top = MainWinLabels[I].Y;
DrawTextA(WinBuffer, MainWinLabels[I].Text, strlen(MainWinLabels[I].Text), &TxtRt,
DT_LEFT|DT_SINGLELINE|DT_NOCLIP);
GetTextExtentPoint32A(WinBuffer, MainWinLabels[I].Text, strlen(MainWinLabels[I].Text), &TxtSize);
MainWinLabels[I].Width = TxtSize.cx;
MainWinLabels[I].Height = TxtSize.cy;
if (TxtFont != NULL) DeleteObject(TxtFont);
}
BitBlt(WinDC, 0, 0, WinRt.right, WinRt.bottom, WinBuffer, 0, 0, SRCCOPY);
ReleaseDC(hWnd, WinDC);
DeleteObject(BitBuffer);
DeleteDC(WinBuffer);
break;
}
The solution:
Do not use WS_EX_COMPOSITED.
Instead of using GetDC(hWnd) use BeginPaint(hwnd, &ps) and EndPaint(hwnd, &ps)
i tried to use UpdateLayeredWindow for all day, but it failed to work :(, i put the code in OnCreate and load a png file (created by photoshop) with CImage.
int CMainWindow::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
SetWindowLong(this->m_hWnd, GWL_STYLE, 0);
SetWindowLong(this->m_hWnd, GWL_EXSTYLE, 0);
SetWindowLong(this->m_hWnd, GWL_EXSTYLE, GetWindowLong(this->m_hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
this->SetMenu(NULL);
::SetWindowPos(this->m_hWnd, HWND_TOP, 50, 50, 652, 492, SWP_SHOWWINDOW);
CBitmap imageBitmap, *oldBitmap;
CDC *dcWindow, dcMem;
CRect wRect;
CPoint wPos;
CSize wSize;
HBITMAP hbmp;
BLENDFUNCTION bFunc;
m_Image.Load(_T("Img/BG_Blue.png"));
GetWindowRect(&wRect);
dcWindow = GetWindowDC();
imageBitmap.CreateCompatibleBitmap(dcWindow, wRect.Width(), wRect.Height());
dcMem.CreateCompatibleDC(dcWindow);
oldBitmap = dcMem.SelectObject(&imageBitmap);
m_Image.Draw(dcMem.m_hDC, 0, 0, wRect.Width(), wRect.Height(), 0, 0, wRect.Width(), wRect.Height());
wPos.x = 0; wPos.y = 0;
wSize.cx = wRect.left; wSize.cy = wRect.bottom;
bFunc.SourceConstantAlpha = 125;
bFunc.BlendFlags = 0;
bFunc.BlendOp = AC_SRC_OVER;
bFunc.AlphaFormat = AC_SRC_ALPHA;
UpdateLayeredWindow(dcWindow, &wPos, &wSize, &dcMem, &wPos, 0, &bFunc, ULW_ALPHA);
//BitBlt(dcWindow->m_hDC, 0, 0, wRect.Width(), wRect.Height(), dcMem.m_hDC, 0, 0, SRCCOPY);
DWORD error = GetLastError();
dcMem.SelectObject(oldBitmap);
return 0;
}
the function returned with 1 , but nothing appeared on the screen, only an icon on the starup menu indicating the program is running. I am wondering if there is something wrong with the png file ... can anyone help ?
dcWindow should be the Device Context of the screen not the window. So initialize it like so: dcWindow->Attach(::GetDC(NULL)) instead of dcWindow = GetWindowDC();