How to place GDI+ Bitmap onto the clipboard? - winapi

I want to place a GDI+ Bitmap onto the clipboard. The obvious way would be to:
use Bitmap.GetHBITMAP to create an HBITMAP
use SetClipboardData to place the HBITMAP onto the clipboard as a CF_BITMAP
So i try pseudo-code:
void PlaceBitmapOnClipboard(Bitmap image)
{
//Convert GDI+ Bitmap to GDI Bitmap
HBITMAP bmp;
image.GetHBITMAP(0, #bmp);
OpenClipboard(this.Handle);
EmptyClipboard();
SetClipboardData(CF_BITMAP, bmp);
CloseClipboard();
}
Error checking has been omitted for expository purposes; but neither function fails:
Not GetHBITMAP and its HRESULT style error
Nor does SetClipboardData return a null
But when i try to use the CF_BITMAP on the clipboard, it can't be pasted into Paint:
So, what is the correct code to fill in the function:
void PlaceBitmapOnClipboard(Bitmap image)
{
//TODO: Ask Stackoverflow to figure this out
}

We need to call:
SetClipboardData(CF_BITMAP, hbitmap_ddb)
Where hbitmap_ddb must be a compatible bitmap (DDB), not DIB which we get from Gdiplus::GetHBitmap
Or we call:
SetClipboardData(CF_DIB, hmemory)
Where hmemory is not HBITMAP. hmemory is described in documentation as:
A memory object containing a BITMAPINFO structure followed by the
bitmap bits.
Example using CF_BITMAP
Use CreateDIBitmap to create a compatible bitmap based on our DIB bitmap. Then call SetClipboardData with the new DDB bitmap.
Gdiplus::Bitmap gdibmp(L"file.bmp");
if(gdibmp.GetLastStatus() != Gdiplus::Ok)
return;
HBITMAP hbitmap;
auto status = gdibmp.GetHBITMAP(0, &hbitmap);
if(status != Gdiplus::Ok)
return;
BITMAP bm;
GetObject(hbitmap, sizeof bm, &bm);
DIBSECTION ds;
if(sizeof ds == GetObject(hbitmap, sizeof ds, &ds))
{
HDC hdc = GetDC(NULL);
HBITMAP hbitmap_ddb = CreateDIBitmap(hdc, &ds.dsBmih, CBM_INIT,
ds.dsBm.bmBits, (BITMAPINFO*)&ds.dsBmih, DIB_RGB_COLORS);
ReleaseDC(NULL, hdc);
if(OpenClipboard(hwnd))
{
EmptyClipboard();
SetClipboardData(CF_BITMAP, hbitmap_ddb);
CloseClipboard();
}
DeleteObject(hbitmap_ddb);
}
DeleteObject(hbitmap);
Example using CF_DIB
Use GlobalAlloc to allocate memory, and copy BITMAPINFOHEADER to that memory, followed by the bits. There is no need to worry about the color table, because Gdiplus::HBitmap returns 32-bit bitmap (at least on modern displays as far as I know)
Gdiplus::Bitmap gdibmp(L"file.bmp");
if(gdibmp.GetLastStatus() != Gdiplus::Ok)
return;
HBITMAP hbitmap;
auto status = gdibmp.GetHBITMAP(NULL, &hbitmap);
if(status != Gdiplus::Ok)
return;
BITMAP bm;
GetObject(hbitmap, sizeof bm, &bm);
BITMAPINFOHEADER bi =
{ sizeof bi, bm.bmWidth, bm.bmHeight, 1, bm.bmBitsPixel, BI_RGB };
std::vector<BYTE> vec(bm.bmWidthBytes * bm.bmHeight);
auto hdc = GetDC(NULL);
GetDIBits(hdc, hbitmap, 0, bi.biHeight, vec.data(), (BITMAPINFO*)&bi, 0);
ReleaseDC(NULL, hdc);
auto hmem = GlobalAlloc(GMEM_MOVEABLE, sizeof bi + vec.size());
auto buffer = (BYTE*)GlobalLock(hmem);
memcpy(buffer, &bi, sizeof bi);
memcpy(buffer + sizeof bi, vec.data(), vec.size());
GlobalUnlock(hmem);
if(OpenClipboard(hwnd))
{
EmptyClipboard();
SetClipboardData(CF_DIB, hmem);
CloseClipboard();
}
DeleteObject(hbitmap);

Related

MFC: How do you add a bitmap to CMenu items added on OnInitMenuPopup?

I need to add custom menu items as needed. I found OnInitMenuPopup (WM_INITMENUPOPUP) does what I need but I can't get an icon to show next to the text on the menu? I've tried a 16x16 png graphic using m_MyGraphic as a CPngImage, I've tried attaching it to a CBitmap, I've tried saving the graphic as a .bmp and loading as CBitmap. I've tried not setting the graphic on the load, but then trying to do it with SetMenuItemBitmaps(), I've tried a 13x13 graphic, I've tried a 15x15 graphic (which matches GetMenuCheckMarkDimensions()). Never does a graphic show next to the menu item? What am I doing wrong or missing?
TIA!
void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
// add items
if (pPopupMenu && pPopupMenu->GetMenuItemCount() > 0 && pPopupMenu->GetMenuItemID(0) == ID_MY_EXPECTED_ID) {
// loop though and add menu items
for (UINT i=0; i<theApp.m_MyList.GetCount(); i++) {
CString s;
s.Format(_T("%i: %s"), i, theApp.m_MyList[i].String);
MENUITEMINFO mii={};
mii.cbSize=sizeof(mii);
mii.fMask=MIIM_ID|MIIM_STRING|MIIM_BITMAP;
mii.wID=ID_MY_RANGE_0+i;
mii.dwTypeData=s.GetBuffer();
mii.hbmpItem=(HBITMAP)m_MyBitmap.GetSafeHandle();
pPopupMenu->InsertMenuItem(i+1, &mii, TRUE);
// not working above so tried using this as well but it doesn't work either:
//pPopupMenu->SetMenuItemBitmaps(i+1, MF_BYPOSITION, &m_MyBitmap, &m_MyBitmap);
}
}
CFrameWndEx::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
}
I found a work around. First add CMFCToolBarMenuButton::m_bAlwaysCallOwnerDraw=TRUE; somewhere on initialization.
Then handle drawing it:
BOOL CMainFrame::OnDrawMenuImage(CDC* pDC, const CMFCToolBarMenuButton* pMenuButton, const CRect& rectImage)
{
BOOL result=FALSE;
if (pMenuButton->m_nID>=ID_MY_RANGE_0 && pMenuButton->m_nID<=ID_MY_RANGE_N) {
// size to use on menu
CSize sizemenuimage = CMFCToolBar::GetMenuImageSize();
// get size of our bitmap
BITMAP bitmap;
m_MyBitmap.GetBitmap(&bitmap);
// create dc to attach bitmap to
CDC dcmem;
if (dcmem.CreateCompatibleDC(pDC)) {
// attach bitmap to dc
CBitmap * poldbitmap=dcmem.SelectObject(&m_MyBitmap);
if (poldbitmap) {
// Draw bitmap
result=pDC->StretchBlt(rectImage.left+(rectImage.Width()-sizemenuimage.cx)/2,
rectImage.top+(rectImage.Height()-sizemenuimage.cy)/2,
sizemenuimage.cx, sizemenuimage.cy,
&dcmem, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);
// Select original object
dcmem.SelectObject(poldbitmap);
}
dcmem.DeleteDC();
}
}
return result;
}
Another possible solution (if you already have the bitmaps for toolbar) is:
void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
CMDIFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
// TODO: Add your message handler code here
HICON hIcon = AfxGetApp()->LoadIcon(IDR_TESTMETYPE);
pPopupMenu->SetMenuItemBitmaps(ID_FILE_NEW, MF_BYCOMMAND, ConvertIconToBitmap(hIcon), NULL);
...
...
}
where SetMenuItemBitmaps is defined as:
CBitmap* CMainFrame::ConvertIconToBitmap(HICON hIcon)
{
CDC dc;
CBitmap bmp;
CClientDC ClientDC(this);
dc.CreateCompatibleDC(&ClientDC);
bmp.CreateCompatibleBitmap(&ClientDC, 13, 13);
CBitmap* pOldBmp = (CBitmap*)dc.SelectObject(&bmp);
::DrawIconEx(dc.GetSafeHdc(), 0, 0, hIcon, 13, 13, 0, (HBRUSH)RGB(255, 255, 255), DI_NORMAL);
dc.SelectObject(pOldBmp);
dc.DeleteDC();
HBITMAP hBitmap = (HBITMAP)::CopyImage((HANDLE)((HBITMAP)bmp),
IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);
return CBitmap::FromHandle(hBitmap);
}

GDI - Can I use the new Windows 10 Segoe UI Emoji colored font with DrawText?

I'm creating a c++ project using Embarcadero RAD Studio (10.2 Tokyo starter) and the Windows GDI to draw text, via the DrawText() function.
I recently saw that Windows 10 provides a new "Segoe UI Emoji" font, that potentially allows text functions to draw colored emojis. I found several examples using Direct2D, but none with pure GDI functions.
I also tried a simple code, like this:
HDC hDC = ::GetDC(Handle);
std::auto_ptr<TCanvas> pCanvas(new TCanvas());
pCanvas->Handle = hDC;
pCanvas->Brush->Color = clWhite;
pCanvas->Brush->Style = bsSolid;
pCanvas->FillRect(TRect(0, 0, ClientWidth, ClientHeight));
const std::wstring text = L"Test 😀 😬 😁 😂 😃 😄 😅 😆";
TRect textRect(10, 10, ClientWidth - 10, ClientHeight - 10);
hFont = ::CreateFont(-40,
0,
0,
0,
FW_DONTCARE,
FALSE,
FALSE,
FALSE,
DEFAULT_CHARSET,
OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY,
VARIABLE_PITCH,
L"Segoe UI Emoji");
::SelectObject(hDC, hFont);
::DrawTextW(hDC,
text.c_str(),
text.length(),
&textRect,
DT_LEFT | DT_TOP | DT_SINGLELINE);
::DeleteObject(hFont);
The output result sounds good in terms of symbols, but they are drawn in black&white, without colors, as you can see on the screenshot below:
I could not find any additional options that may allow the text to be drawn using colored symbols instead of black&white. Is there a way to activate the support of the color in GDI DrawText() function, and if yes, how to do that? Or only Direct2D may draw colored emojis?
EDITED on 30.10.2017
As the GDI cannot do the job (unfortunately, and as I thought) I publish here the Direct2D version of the above code, that worked for me.
const std::wstring text = L"Test 😀 😬 😁 😂 😃 😄 😅 😆";
HDC hDC = ::GetDC(Handle);
std::auto_ptr<TCanvas> pGDICanvas(new TCanvas());
pGDICanvas->Handle = hDC;
pGDICanvas->Brush->Color = clWhite;
pGDICanvas->Brush->Style = bsSolid;
pGDICanvas->FillRect(TRect(0, 0, ClientWidth, ClientHeight));
::D2D1_RECT_F textRect;
textRect.left = 10;
textRect.top = 10;
textRect.right = ClientWidth - 10;
textRect.bottom = ClientHeight - 10;
std::auto_ptr<TDirect2DCanvas> pCanvas(new TDirect2DCanvas(hDC, TRect(0, 0, ClientWidth, ClientHeight)));
// configure Direct2D font
pCanvas->Font->Size = 40;
pCanvas->Font->Name = L"Segoe UI Emoji";
pCanvas->Font->Orientation = 0;
pCanvas->Font->Pitch = System::Uitypes::TFontPitch::fpVariable;
pCanvas->Font->Style = TFontStyles();
// get DirectWrite text format object
_di_IDWriteTextFormat pFormat = pCanvas->Font->Handle;
if (!pFormat)
return;
pCanvas->RenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
::D2D1_COLOR_F color;
color.r = 0.0f;
color.g = 0.0f;
color.b = 0.0f;
color.a = 1.0f;
::ID2D1SolidColorBrush* pBrush = NULL;
// create solid color brush, use pen color if rect is completely filled with outline
pCanvas->RenderTarget->CreateSolidColorBrush(color, &pBrush);
if (!pBrush)
return;
// set horiz alignment
pFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
// set vert alignment
pFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
// set reading direction
pFormat->SetReadingDirection(DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
// set word wrapping mode
pFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
IDWriteInlineObject* pInlineObject = NULL;
::DWRITE_TRIMMING trimming;
trimming.delimiter = 0;
trimming.delimiterCount = 0;
trimming.granularity = DWRITE_TRIMMING_GRANULARITY_NONE;
// set text trimming
pFormat->SetTrimming(&trimming, pInlineObject);
pCanvas->BeginDraw();
pCanvas->RenderTarget->DrawText(text.c_str(), text.length(), pFormat, textRect, pBrush,
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);
pCanvas->EndDraw();
Of course this code will draw colored emojis only on the currently most recent versions of Windows 10, and above. On previous versions the text will be drawn as above (and the code may not compile).
Bonus Reading
MSDN: Color Fonts with DirectWrite, Direct2D, and Win2D
GDI does not support color fonts (even if you go the full Uniscribe route), you have to use Direct2D if you want color font support. It makes sense that the simpler GDI APIs don't support color fonts as color fonts require using OpenType tags and none of DrawText/TextOut provide that level of control, Uniscribe allows for such tags but has simply not been extended to support color fonts.
You can use DirectWrite to draw colored emojis onto a bitmap in memory DC, then BitBlt() to your destination DC.
Basically, you need to implement a custom IDWriteTextRenderer class and call IDWriteTextLayout::Draw() with your renderer, then copy the result.
In your class, you retrieve IDWriteGdiInterop from IDWriteFactory and call IDWriteGdiInterop::CreateBitmapRenderTarget() to get the bitmap render target; call IDWriteFactory::CreateMonitorRenderingParams() to get the rendering parameters, and call IDWriteFactory::CreateTextFormat() to set up your text format.
The only significant method is DrawGlyphRun(), where you get IDWriteColorGlyphRunEnumerator with IDWriteFactory2::TranslateColorGlyphRun() and with each color run, call IDWriteBitmapRenderTarget::DrawGlyphRun() to do the work for you.
Just remember to update the render target/parameters when the window size/position changes.
You may reference this MSDN documentation:
Render to a GDI Surface
https://msdn.microsoft.com/en-us/library/windows/desktop/ff485856(v=vs.85).aspx
As mentioned by #SoronelHaetir's answer above, the win32 graphics device interface (GDI) that is used when working with static window components (via WC_STATIC) doesn't support colorized fonts. In order to display colored emojis and/or "fancier" text (i.e. colored text, etc.), you'll need to use the Direct2D API.
The example above provided by original poster (OP) #Jean-Milost Reymond isn't something that is immediately able to be compiled and tried out by the reader. Also, it uses the TCanvas class, which isn't strictly needed when working directly with the Win32 API.
For people looking for a complete example that works on the bare-metal Win32 API and can be immediately copied and pasted and compiled, then here is the code that will compile in Visual Studio:
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <wchar.h>
#include <math.h>
#include <d2d1.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <wincodec.h>
#include <string>
#include <cassert>
#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "Dwrite.lib")
HWND WindowHandle = nullptr;
IDWriteFactory * DWriteFactory = nullptr;
ID2D1Factory * Direct2dFactory = nullptr;
ID2D1HwndRenderTarget * RenderTarget = nullptr;
ID2D1SolidColorBrush * TextBlackBrush = nullptr;
const std::wstring DISPLAY_TEXT = L"Test 😀 😬 😁 😂 😃 😄 😅 😆";
template<class Interface>
inline void SafeRelease (Interface ** ppInterfaceToRelease);
LRESULT CALLBACK WndProc (HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
HRESULT CreateDeviceIndependentResources ();
HRESULT InitInstance (HINSTANCE hInstance, int nCmdShow);
void DiscardDeviceResources ();
HRESULT OnRender ();
HRESULT CreateDeviceResources ();
template<class Interface>
inline void SafeRelease (Interface ** ppInterfaceToRelease)
{
if (*ppInterfaceToRelease != NULL)
{
(*ppInterfaceToRelease)->Release ();
(*ppInterfaceToRelease) = NULL;
}
}
HRESULT OnRender ()
{
HRESULT Result = S_OK;
D2D1_SIZE_F RenderCanvasArea = { 0 };
IDWriteTextFormat * TextFormat = nullptr;
D2D1_RECT_F TextCanvasArea = { 0 };
Result = CreateDeviceResources ();
if (SUCCEEDED (Result))
{
RenderTarget->BeginDraw ();
RenderCanvasArea = RenderTarget->GetSize ();
RenderTarget->Clear (D2D1::ColorF (D2D1::ColorF::White));
if (SUCCEEDED (Result))
{
Result = DWriteFactory->CreateTextFormat (L"Segoe UI",
nullptr,
DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
25.0f,
L"en-us",
&TextFormat);
TextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_LEADING);
TextFormat->SetParagraphAlignment (DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
TextFormat->SetReadingDirection (DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
TextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_WRAP);
if (SUCCEEDED (Result) &&
TextFormat != nullptr)
{
TextCanvasArea = D2D1::RectF (0,
0,
RenderCanvasArea.width,
RenderCanvasArea.height);
RenderTarget->DrawTextW (DISPLAY_TEXT.c_str (),
static_cast <UINT32> (DISPLAY_TEXT.size ()),
TextFormat,
TextCanvasArea,
TextBlackBrush,
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);
}
}
Result = RenderTarget->EndDraw ();
}
if (Result == D2DERR_RECREATE_TARGET)
{
DiscardDeviceResources ();
Result = S_OK;
}
return Result;
}
HRESULT CreateDeviceResources ()
{
HRESULT Result = S_OK;
RECT rc = { 0 };
if (!RenderTarget)
{
GetClientRect (WindowHandle,
&rc);
D2D1_SIZE_U size = D2D1::SizeU (rc.right - rc.left,
rc.bottom - rc.top);
// Create a Direct2D render target.
Result = Direct2dFactory->CreateHwndRenderTarget (D2D1::RenderTargetProperties (),
D2D1::HwndRenderTargetProperties (WindowHandle, size),
&RenderTarget);
if (SUCCEEDED (Result))
{
// Create a blue brush.
Result = RenderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::Black),
&TextBlackBrush);
}
}
return Result;
}
void DiscardDeviceResources ()
{
SafeRelease (&RenderTarget);
SafeRelease (&TextBlackBrush);
}
HRESULT InitInstance (HINSTANCE hInstance,
int nCmdShow)
{
HRESULT Result = S_OK;
// Create the window.
WindowHandle = CreateWindow (L"D2DTextDemo",
L"Direct2D Text Demo Application",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
600,
200,
nullptr,
nullptr,
hInstance,
nullptr);
if (WindowHandle == nullptr)
{
Result = E_POINTER;
}
else
{
ShowWindow (WindowHandle,
nCmdShow);
UpdateWindow (WindowHandle);
}
return Result;
}
HRESULT CreateDeviceIndependentResources ()
{
HRESULT Result = S_OK;
Result = D2D1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED,
&Direct2dFactory);
if (SUCCEEDED (Result))
{
Result = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED,
__uuidof (IDWriteFactory),
reinterpret_cast <IUnknown **> (&DWriteFactory));
}
return Result;
}
LRESULT CALLBACK WndProc (HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
LRESULT Result = 0;
switch (message)
{
case WM_SIZE:
{
UINT width = LOWORD (lParam);
UINT height = HIWORD (lParam);
if (RenderTarget != nullptr)
{
// Note: This method can fail, but it's okay to ignore the
// error here, because the error will be returned again
// the next time EndDraw is called.
RenderTarget->Resize (D2D1::SizeU (width,
height));
}
}
break;
case WM_DISPLAYCHANGE:
{
InvalidateRect (hwnd, nullptr, FALSE);
}
break;
case WM_PAINT:
{
OnRender ();
ValidateRect (hwnd,
nullptr);
}
break;
case WM_DESTROY:
{
PostQuitMessage (0);
Result = 1;
}
break;
default:
{
Result = DefWindowProc (hwnd,
message,
wParam,
lParam);
}
break;
}
return Result;
}
int APIENTRY wWinMain (_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER (hInstance);
UNREFERENCED_PARAMETER (hPrevInstance);
UNREFERENCED_PARAMETER (lpCmdLine);
UNREFERENCED_PARAMETER (nCmdShow);
HRESULT ExitCode = S_OK;
MSG NextMessage = { 0 };
WNDCLASSEX wcex = { 0 };
ATOM WindowClassId = 0;
wcex.cbSize = sizeof (WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof (LONG_PTR);
wcex.hInstance = hInstance;
wcex.hbrBackground = nullptr;
wcex.lpszMenuName = nullptr;
wcex.hCursor = LoadCursor (nullptr, IDI_APPLICATION);
wcex.lpszClassName = L"D2DTextDemo";
if (SUCCEEDED (CoInitialize (nullptr)))
{
WindowClassId = RegisterClassEx (&wcex);
if (WindowClassId == 0)
{
ExitCode = HRESULT_FROM_WIN32 (GetLastError ());
}
if (SUCCEEDED (ExitCode))
{
ExitCode = CreateDeviceIndependentResources ();
}
if (SUCCEEDED (ExitCode))
{
ExitCode = InitInstance (hInstance,
nCmdShow);
}
if (SUCCEEDED (ExitCode))
{
while (GetMessage (&NextMessage,
nullptr,
0,
0))
{
TranslateMessage (&NextMessage);
DispatchMessage (&NextMessage);
}
}
CoUninitialize ();
SafeRelease (&Direct2dFactory);
SafeRelease (&DWriteFactory);
SafeRelease (&RenderTarget);
}
return ExitCode;
}
(The above example doesn't have perfect error handling, so it's important to audit this example code if the following is used in any project the reader is working on.)
Before attempting to compile this in Visual Studio, make sure your project has the "SubSystem" linker option set to Windows /SUBSYSTEM:WINDOWS.
Once compiled successfully, the following application window will appear:
I tested this coded example in Visual Studio 2022 Community Edition on Windows 11 with success.
Reference(s):
Creating a Simple Direct2D Application
Tutorial: Getting Started with DirectWrite

How to copy a picture from disk into the clipboard with win32?

It's easy to copy text into clipboard with win32 API, but I want to copy a picture from disk (for example, D:\1.jpg) into the clipborad.
I search many webpages and I can't find something useful.
Please teach me how to do it.
And no MFC.
You can use Gdi+ to load the image, get HBITMAP, and set the clipboard data. Gdi+ is Unicode only, so if using old ANSI functions you have to convert the filename to wide char. Example in C++:
bool copyimage(const wchar_t* filename)
{
bool result = false;
Gdiplus::Bitmap *gdibmp = Gdiplus::Bitmap::FromFile(filename);
if (gdibmp)
{
HBITMAP hbitmap;
gdibmp->GetHBITMAP(0, &hbitmap);
if (OpenClipboard(NULL))
{
EmptyClipboard();
DIBSECTION ds;
if (GetObject(hbitmap, sizeof(DIBSECTION), &ds))
{
HDC hdc = GetDC(HWND_DESKTOP);
//create compatible bitmap (get DDB from DIB)
HBITMAP hbitmap_ddb = CreateDIBitmap(hdc, &ds.dsBmih, CBM_INIT,
ds.dsBm.bmBits, (BITMAPINFO*)&ds.dsBmih, DIB_RGB_COLORS);
ReleaseDC(HWND_DESKTOP, hdc);
SetClipboardData(CF_BITMAP, hbitmap_ddb);
DeleteObject(hbitmap_ddb);
result = true;
}
CloseClipboard();
}
//cleanup:
DeleteObject(hbitmap);
delete gdibmp;
}
return result;
}
Note that Microsoft recommends using CF_DIB to set bitmap clipboard data, but that doesn't work with GDI+. This example uses CF_BITMAP instead.
Gdi+ uses standard GdiPlus.lib library. It needs to be initialized as follows:
#include <Windows.h>
#include <GdiPlus.h>
#pragma comment(lib, "GdiPlus")//Visual Studio specific
bool copyimage(const wchar_t* filename);
int main()
{
//initialize Gdiplus once:
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
copyimage(L"d:\\1.jpg");
Gdiplus::GdiplusShutdown(gdiplusToken);
}

BITMAP Image won't load from file "INVALID HANDLE"

This is basic, so only one snippet of code from the drawing function is needed because it has the info necessary about the
void draw()
{
RECT rect;
GetClientRect(hwnd, &rect);
HBITMAP FRAME1ANIMDASH = NULL;
FRAME1ANIMDASH = (HBITMAP) LoadImage(NULL, "Hidden but correct pathname that won't be shown here", 0, LR_DEFAULTSIZE, LR_DEFAULTSIZE, LR_CREATEDIBSECTION);
if(FRAME1ANIMDASH == NULL)
{
MessageBox(NULL, "CANNOT LOAD", "CANNOT LOAD", MB_OK);
}
HDC device;
PAINTSTRUCT ps;
BITMAP bm;
GetObject(FRAME1ANIMDASH,sizeof(BITMAP),&bm);
HDC hdcdevice=CreateCompatibleDC(device);
SelectObject(hdcdevice,FRAME1ANIMDASH);
BitBlt(device,0,0,0,0,hdcdevice,0,0,SRCCOPY);
UpdateWindow(hwnd);
device=GetDC(hwnd);
DeleteDC(hdcdevice);
DeleteObject((HBITMAP) FRAME1ANIMDASH);
DWORD lastError = GetLastError();
cout << GetLastError();
}
Get last error shows error 6, which is an invalid file handle. The message box shows up, which means the image never loaded and failed, so the code after it is not the primary concern yet.
I am trying to load a bitmap and put it on the Win32 window I have created.
No other code is needed, as there's nothing specific about any other code that would lead to a possible answer overall. Tell me what I can do to make the image load successfully.
You are calling GetLastError() too late. The error code is meaningless at that point as it could be overwritten by any of the other functions you are calling. You must call GetLastError() immediately after LoadImage() fails:
HBITMAP FRAME1ANIMDASH = (HBITMAP) LoadImage(...);
if (FRAME1ANIMDASH == NULL)
{
DWORD lastError = GetLastError();
cout << "CANNOT LOAD IMAGE, ERROR " << lastError;
MessageBox(NULL, "CANNOT LOAD IMAGE", "CANNOT LOAD IMAGE", MB_OK);
return; // <-- add this, too!
}
And the reason why LoadImage() is failing is because you are not specifying the LR_LOADFROMFILE flag, and not requesting the image size correctly, per the LoadImage() documentation:
lpszName [in]
...
If the hinst parameter is NULL and the fuLoad parameter omits the LR_LOADFROMFILE value, the lpszName specifies the OEM image to load. The OEM image identifiers are defined in Winuser.h and have the following prefixes.
...
If the fuLoad parameter includes the LR_LOADFROMFILE value, lpszName is the name of the file that contains the stand-alone resource (icon, cursor, or bitmap file). Therefore, set hinst to NULL.
...
cxDesired [in]
...
... If this parameter is zero and LR_DEFAULTSIZE is not used, the function uses the actual resource width.
...
cyDesired [in]
...
... If this parameter is zero and LR_DEFAULTSIZE is not used, the function uses the actual resource height.
...
fuLoad [in]
...
LR_DEFAULTSIZE
... If this flag is not specified and cxDesired and cyDesired are set to zero, the function uses the actual resource size.
...
LR_LOADFROMFILE
... Loads the stand-alone image from the file specified by lpszName (icon, cursor, or bitmap file).
So it should look more like this:
HBITMAP FRAME1ANIMDASH = (HBITMAP) LoadImage(NULL, "pathname", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
Once you fix that, there are other problems with the code.
You are calling CreateCompatibleDC() with an uninitialized HDC variable, and not checking the result of CreateCompatibleDC() for failure. Your call to GetDC() is in the wrong place, it needs to be moved above CreateCompatibleDC(). And you are leaking the HDC that GetDC() returns, you need to call ReleaseDC() to free it.
You are also telling BitBlt() to copy a 0x0 rectangle of pixels, instead of using the true dimensions of the bitmap from the BITMAP structure, or even the window RECT that you are retrieving from GetClientRect().
Try something more like this instead:
void draw()
{
HBITMAP FRAME1ANIMDASH = (HBITMAP) LoadImage(NULL, "pathname", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
if (FRAME1ANIMDASH == NULL)
{
DWORD lastError = GetLastError();
cout << "CANNOT LOAD IMAGE, ERROR " << lastError;
MessageBox(NULL, "CANNOT LOAD IMAGE", "CANNOT LOAD IMAGE", MB_OK);
return;
}
HDC hdcWnd = GetDC(hwnd);
if (hdcWnd === NULL)
{
DeleteObject(FRAME1ANIMDASH);
cout << "CANNOT GET WINDOW DC";
MessageBox(NULL, "CANNOT GET WINDOW DC", "CANNOT GET DC", MB_OK);
return;
}
HDC hdcMem = CreateCompatibleDC(hdcWnd);
if (hdcMem == NULL)
{
ReleaseDC(hwnd, hdcWnd);
DeleteObject(FRAME1ANIMDASH);
cout << "CANNOT CREATE COMPATIBLE DC";
MessageBox(NULL, "CANNOT CREATE COMPATIBLE DC", "CANNOT CREATE DC", MB_OK);
return;
}
BITMAP bm;
GetObject(FRAME1ANIMDASH, sizeof(BITMAP), &bm);
HBITMAP oldBm = (HBITMAP) SelectObject(hdcMem, FRAME1ANIMDASH);
BitBlt(hdcWnd, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, oldBm);
DeleteDC(hdcMem);
ReleaseDC(hwnd, hdcWnd);
DeleteObject(FRAME1ANIMDASH);
UpdateWindow(hwnd);
}
That being said, the PAINTSTRUCT variable implies that your code is being used inside of a WM_PAINT message handler. If that is true, you should be using BeginPaint() instead of GetDC() to get the target HDC to draw on:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
draw(hdc);
EndPaint(hwnd, &ps);
break;
};
void draw(HDC hdcTarget)
{
HBITMAP FRAME1ANIMDASH = (HBITMAP) LoadImage(NULL, "pathname", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
if (FRAME1ANIMDASH == NULL)
{
DWORD lastError = GetLastError();
cout << "CANNOT LOAD IMAGE, ERROR " << lastError;
// DO NOT use MessageBox() in a WM_PAINT handler,
// You will trigger an endless re-paint loop!
return;
}
HDC hdcMem = CreateCompatibleDC(hdcTarget);
if (hdcMem == NULL)
{
DeleteObject(FRAME1ANIMDASH);
cout << "CANNOT CREATE COMPATIBLE DC";
return;
}
BITMAP bm;
GetObject(FRAME1ANIMDASH, sizeof(BITMAP), &bm);
HBITMAP oldBm = (HBITMAP) SelectObject(hdcMem, FRAME1ANIMDASH);
BitBlt(hdcWnd, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, oldBm);
DeleteDC(hdcMem);
DeleteObject(FRAME1ANIMDASH);
}
Otherwise, if you are using your code outside of WM_PAINT, then the code is pretty much useless because UpdateWindow() will trigger a re-paint that erases your drawing. Persistent drawings on a window must be done using WM_PAINT (unless you are using UpdateLayeredWindow() with a WS_EX_LAYERED window).

win32 bitmaps flashing when more than one sprite on screen

I've been using the win32 api to make a game with sprites. For some reason when I have more than one sprite on screen they flash occasionally as if they are disappearing and returning. When there is only one sprite on screen it displays correctly.
I am using C++, win32 API and working with Visual Studio 08
The following is roughly what I have:
//creates rect based on window client area
GetClientRect(ghwnd, &screenRect);
// Initialises front buffer device context (window)
frontHDC = GetDC(ghwnd);
// sets up Back DC to be compatible with the front
backHDC = CreateCompatibleDC(frontHDC);
// Create another hdc to store the bitmap in before the backbuffer
bitmapHDC = CreateCompatibleDC(frontHDC);
//creates bitmap compatible with the front buffer
theOldFrontBitMap = CreateCompatibleBitmap(frontHDC, screenRect.right, screenRect.bottom);
//creates bitmap compatible with the back buffer
theOldBackBitMap = (HBITMAP)SelectObject(backHDC, theOldFrontBitMap);
HBITMAP originalBitMap = (HBITMAP)SelectObject(bitmapHDC,bitmap);
//Transparency function
TransparentBlt( backHDC,
m_Position.x,
m_Position.y,
m_Size.x,
m_Size.y,
bitmapHDC,
0,
0,
m_Size.x,
m_Size.y,
0x00FFFFFF);
SelectObject(bitmapHDC,originalBitMap);
BitBlt(frontHDC, screenRect.left, screenRect.top,
screenRect.right, screenRect.bottom, backHDC, 0, 0, SRCCOPY);
Am I doing this correctly? and if so where am I going wrong? If I have not given enough information please tell me and I will rectify that.
The problem with creating a Win32 game is that, even if you use double buffering, you have no way to wait for the vertical retrace of the monitor to display the buffer.
Displaying the buffer or sprite while the vertical retrace is in progress can cause tearing or even the disappearing sprite that you experience.
The only real way around this is to use an SDK like OpenGL or DirectX to manage and display the buffers.
Here's a sample program that may help you, use the arrow keys to move the white box on the double buffered background:
#include <Windows.h>
RECT rcSize;
HDC hdcBackBuffer, hdcSprite;
HBITMAP hbmBackBuffer, hbmSprite;
int spriteX = 175, spriteY = 175;
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static PAINTSTRUCT ps;
switch (msg)
{
case WM_CREATE:
{
HDC hdcWindow = GetDC(hWnd);
// make back buffer
GetClientRect(hWnd, &rcSize);
hdcBackBuffer = CreateCompatibleDC(hdcWindow);
hbmBackBuffer = CreateCompatibleBitmap(hdcBackBuffer, rcSize.right - rcSize.left, rcSize.bottom - rcSize.top);
SelectObject(hdcBackBuffer, hbmBackBuffer); // SHOULD SAVE PREVIOUS...
// make sprite
hdcSprite = CreateCompatibleDC(hdcWindow);
hbmSprite = CreateCompatibleBitmap(hdcSprite, 50, 50);
SelectObject(hdcSprite, hbmSprite); // SHOULD SAVE PREVIOUS...
RECT rcSprite;
SetRect(&rcSprite, 0, 0, 50, 50);
FillRect(hdcSprite, &rcSprite, (HBRUSH)GetStockObject(WHITE_BRUSH));
ReleaseDC(hWnd, hdcWindow);
return 0;
}
case WM_KEYDOWN:
{
// SHOULD REALLY USE GetAsyncKeyState for game, but simplified here
switch (wParam)
{
case VK_LEFT:
spriteX--;
break;
case VK_RIGHT:
spriteX++;
break;
case VK_UP:
spriteY--;
break;
case VK_DOWN:
spriteY++;
break;
}
return 0;
}
case WM_ERASEBKGND:
{
return 1; // INDICATE THAT WE ERASED THE BACKGROUND OURSELVES
}
case WM_PAINT:
{
BeginPaint(hWnd, &ps);
// clear back buffer
FillRect(hdcBackBuffer, &rcSize, (HBRUSH)GetStockObject(BLACK_BRUSH));
// render sprite to back buffer
BitBlt(hdcBackBuffer, spriteX, spriteY, 50, 50, hdcSprite, 0, 0, SRCCOPY);
// render back buffer to screen
BitBlt(ps.hdc, 0, 0, rcSize.right - rcSize.left, rcSize.bottom - rcSize.top, hdcBackBuffer, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
return 0;
}
case WM_DESTROY:
{
// TODO - DESTROY ALL BITMAPS AND DEVICE CONTEXTS
PostQuitMessage(0);
return 0;
}
default:
{
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
}
int WINAPI WinMain(HINSTANCE hPrevInstance, HINSTANCE hInstance, LPSTR lpCmdLine, int nShowCmd)
{
static TCHAR className[] = TEXT("GameClass");
static TCHAR windowName[] = TEXT("A Game");
WNDCLASSEX wcex;
wcex.cbClsExtra = 0;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.cbWndExtra = 0;
wcex.hbrBackground = NULL;
wcex.hCursor = LoadCursor(hInstance, IDC_ARROW);
wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wcex.hIconSm = NULL;
wcex.hInstance = hInstance;
wcex.lpfnWndProc = WndProc;
wcex.lpszClassName = className;
wcex.lpszMenuName = NULL;
wcex.style = 0;
if (!RegisterClassEx(&wcex))
return 0;
HWND hWnd = CreateWindow(className, windowName, WS_CAPTION | WS_BORDER | WS_SYSMENU, 0, 0, 400, 400, NULL, NULL, hInstance, NULL);
if (!hWnd)
return 0;
ShowWindow(hWnd, nShowCmd);
UpdateWindow(hWnd);
MSG msg;
for (;;)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
InvalidateRect(hWnd, NULL, FALSE);
}
return msg.wParam;
}
I think your back buffer implementation is wrong although Im not sure exactly where. Try this implementation of a separate back buffer class. I hope it helps.
Here my back buffer class.
#ifndef BACKBUFFER_H
#define BACKBUFFER_H
#include <Windows.h>
class BackBuffer
{
public:
BackBuffer(HWND hWnd, int width, int height);
~BackBuffer();
HDC getDC();
int width();
int height();
void present();
private:
// Make copy constructor and assignment operator private
// so client cannot copy BackBuffers. We do this because
// this class is not designed to be copied because it
// is not efficient--copying bitmaps is slow (lots of memory).
// In addition, most applications will probably only need one
// BackBuffer anyway.
BackBuffer(const BackBuffer& rhs);
BackBuffer& operator=(const BackBuffer& rhs);
private:
HWND mhWnd;
HDC mhDC;
HBITMAP mhSurface;
HBITMAP mhOldObject;
int mWidth;
int mHeight;
};
#endif //BACKBUFFER_H
Heres the implementation:
BackBuffer::BackBuffer(HWND hWnd, int width, int height)
{
//Save a copy of the main window handle
mhWnd = hWnd;
//Get a handle to the device context associated with
// the window
HDC hWndDC = GetDC(hWnd);
//Save the backbuffer dimensions
mWidth = width;
mHeight = height;
//Create system memory device context that is compatible
//with the window one
mhDC = CreateCompatibleDC(hWndDC);
//Create the backbuffer surface bitmap that is compatible
//with the window device context bitmap format. That is
//the surface we will render onto.
mhSurface = CreateCompatibleBitmap(hWndDC, width, height);
//Done with DC
ReleaseDC(hWnd, hWndDC);
//At this point, the back buffer surface is uninitialized,
//so lets clear it to some non-zero value. Note that it
//needs to be a non-zero. If it is zero then it will mess
//up our sprite blending logic.
//Select the backbuffer bitmap into the DC
mhOldObject = (HBITMAP)SelectObject(mhDC, mhSurface);
//Select a white brush
HBRUSH white = (HBRUSH)GetStockObject(WHITE_BRUSH);
HBRUSH oldBrush = (HBRUSH)SelectObject(mhDC, white);
//Clear the backbuffer rectangle
Rectangle(mhDC, 0, 0, mWidth, mHeight);
//Restore the original brush
SelectObject(mhDC, oldBrush);
}
BackBuffer::~BackBuffer()
{
SelectObject(mhDC, mhOldObject);
DeleteObject(mhSurface);
DeleteDC(mhDC);
}
HDC BackBuffer::getDC()
{
return mhDC;
}
int BackBuffer::width()
{
return mWidth;
}
int BackBuffer::height()
{
return mHeight;
}
void BackBuffer::present()
{
//Get a handle to the device context associated with
//the window
HDC hWndDC = GetDC(mhWnd);
//Copy the backbuffer contents over to the
//window client area
BitBlt(hWndDC, 0, 0, mWidth, mHeight, mhDC, 0, 0, SRCCOPY);
//Free window DC when done
ReleaseDC(mhWnd, hWndDC);
}
Try to work your way through this implementation the comments should help you understand. Hope this helps.

Resources