Drawing Lines, Shapes, or Text on Bitmaps






4.91/5 (22 votes)
Quick reference to draw lines, shapes, or text on bitmaps
Introduction
This article describes the operations needed to draw lines, shapes, or text on bitmaps. The task is quite simple, but a quick reference could be handy for the beginners.
Working with HBITMAPs
As first step, we need a background image, referenced by a hbitmap
handle. hbitmap
can be the result of previous operations, or created with CreateBitmap()
, or a resource:
HBITMAP hbitmap = ::LoadBitmap(AfxGetInstanceHandle(),
MAKEINTRESOURCE(IDB_BITMAP1));
From hbitmap
, we can extract the basic attributes:
BITMAP bm;
GetObject( hbitmap, sizeof(BITMAP), &bm );
long width=bm.bmWidth;
long height=bm.bmHeight;
Now let's create a memory device context and select a new bitmap.
//prepare the bitmap attributes
BITMAPINFO bmInfo;
memset(&bmInfo.bmiHeader,0,sizeof(BITMAPINFOHEADER));
bmInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bmInfo.bmiHeader.biWidth=width;
bmInfo.bmiHeader.biHeight=height;
bmInfo.bmiHeader.biPlanes=1;
bmInfo.bmiHeader.biBitCount=24;
//create a temporary dc in memory.
HDC pDC = ::GetDC(0);
HDC TmpDC=CreateCompatibleDC(pDC);
//create a new bitmap and select it in the memory dc
BYTE *pbase;
HBITMAP TmpBmp=CreateDIBSection(pDC,
&bmInfo,DIB_RGB_COLORS,(void**)&pbase,0,0);
HGDIOBJ TmpObj=SelectObject(TmpDC,TmpBmp);
TmpDC
is the device context where you can draw lines, text, or images. For example, the next lines draw a string
over a background image:
//draw the background
HDC dcBmp=CreateCompatibleDC(TmpDC);
HGDIOBJ TmpObj2 = SelectObject(dcBmp,hbitmap);
BitBlt(TmpDC,0,0,width,height,dcBmp,0,0,SRCCOPY);
SelectObject(TmpDC,TmpObj2);
DeleteDC(dcBmp);
//choose the font
CFont m_Font;
LOGFONT* m_pLF;
m_pLF=(LOGFONT*)calloc(1,sizeof(LOGFONT));
strncpy(m_pLF->lfFaceName,"Times New Roman",31);
m_pLF->lfHeight=64;
m_pLF->lfWeight=600;
m_pLF->lfItalic=1;
m_pLF->lfUnderline=0;
m_Font.CreateFontIndirect(m_pLF);
//select the font in the dc
CDC dc;
dc.Attach(TmpDC);
CFont* pOldFont=NULL;
if (m_Font.m_hObject)
pOldFont = dc.SelectObject(&m_Font);
else
dc.SelectObject(GetStockObject(DEFAULT_GUI_FONT));
//Set text color
dc.SetTextColor(RGB(60,120,240));
//Set text position;
RECT pos = {40,40,0,0};
//draw the text
dc.SetBkMode(TRANSPARENT);
dc.DrawText("Test",4,&pos,DT_CALCRECT);
dc.DrawText("Test",4,&pos,0);
//cleanup
if (pOldFont) dc.SelectObject(pOldFont);
m_Font.DeleteObject();
dc.Detach();
free(m_pLF);
At this point, there are 2 bitmaps: hbitmap
and TmpBmp
, you can keep both the old and the new image, or replace hbitmap
with TmpBmp
:
DeleteObject(hbitmap);
hbitmap=TmpBmp;
Finally, we can delete the temporary device context. Do not delete hbitmap
and TmpBmp
here, or you'll lose the bitmap.
//final cleanup
SelectObject(TmpDC,TmpObj);
DeleteDC(TmpDC);
Conclusion
The article starts with a background image stored in a HBITMAP
, and ends with a new image in a new HBITMAP
. This grants a high flexibility on what you can do before, after and in between: you can use all the GDI functions to build your image, and for example you can use CBitmap
, or image processing libraries like FreeImage and CxImage, to add effects or save the result in a file.