Printing to a Printer DC with MFC - c++

I've followed Microsoft's tutorial on creating a Device Context, and I've tried looking around the internet for a decent source (apparently, MFC is a mystical thing). The following successfully prints out "Hello, World!"; except it's extremely tiny.
How can I send a CImage to the printer, rather than text?
And how could I get the text's size to be bigger than a couple millimeters?
I've scoured MSDN, but everything is either outdated (like the example code I am using), or just not well documented.
// get the default printer
CPrintDialog dlg(FALSE);
dlg.GetDefaults();
// is a default printer set up?
HDC hdcPrinter = dlg.GetPrinterDC();
if (hdcPrinter == NULL)
{
//MessageBox(_T("Buy a printer!"));
}
else
{
// create a CDC and attach it to the default printer
CDC dcPrinter;
dcPrinter.Attach(hdcPrinter);
// call StartDoc() to begin printing
DOCINFO docinfo;
memset(&docinfo, 0, sizeof(docinfo));
docinfo.cbSize = sizeof(docinfo);
docinfo.lpszDocName = _T("CDC::StartDoc() Code Fragment");
// if it fails, complain and exit gracefully
if (dcPrinter.StartDoc(&docinfo) < 0)
{
//MessageBox(_T("Printer wouldn't initalize"));
}
else
{
// start a page
if (dcPrinter.StartPage() < 0)
{
//MessageBox(_T("Could not start page"));
dcPrinter.AbortDoc();
}
else
{
// actually do some printing
//CGdiObject* pOldFont = dcPrinter.SelectStockObject(SYSTEM_FONT);
dcPrinter.SetMapMode(MM_HIENGLISH);
auto font = CreateFont(
3'000, // nHeight
1'500, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
_T("Arial")); // lpszFacename
dcPrinter.SelectObject(&font);
dcPrinter.TextOut(450, 450, _T("Hello World!"), 12);
dcPrinter.EndPage();
dcPrinter.EndDoc();
//dcPrinter.SelectObject(pOldFont);
}
}
}

Tiny Text Problem
The problem is that, by default, the size of a font is specified in device-dependent units and printers are generally much higher resolution that a screen. So if you've created a font that is 20 pixels high on the screen (which might have 96 pixels per inch) when you try to use that font on a printer, which maybe has 300 or 600 dots per inch, your text looks really small.
As another answer shows, one idea is to change the mapping mode so that the printer uses units that are closer to what is on the screen.
An alternative way is to create a new font with an appropriate size (the lfHeight field in the LOGFONT structure) based on the DPI of the printer, which you can determine with the GetDeviceCaps function. This can be handy if you want a particular font size, like 14 point text.
LOGFONT lf = {0};
lf.lfHeight = -MulDiv(point_size, ::GetDeviceCaps(dcPrinter, LOGPIXELSY), 72);
// init other field of lf as you like
HFONT hfontPrinter = ::CreateFontIndirect(&lf);
HFONT hfontOld = ::SelectObject(hdcPrinter, hfontPrinter);
// now draw to the hdcPrinter
::SelectObject(hdcPrinter, hfontOld);
::DeleteObject(hfontPrinter);
Sending a CImage
I don't use MFC, but it looks like you can just call CImage::StretchBlt with the printer DC. Once again, you'll probably have to take the printer's much higher resolution into account when you choose the target coordinates.

Use CFont::CreatePointFont() or CFont::CreatePointFontIndirect() to create a font that is reasonable. Most printers are 600 DPI. Most screens are 96 DPI. A 12 point font on the screen is basically a 2 point font and illegible on a printer.
Create the font and select it into your DC. Do not forget to select it out of the DC after using it and before destroying your DC (CDC class). (The CDC destructor automatically deletes the HDC).

Here is the problem:
dcPrinter.SetMapMode(MM_TEXT);
MM_TEXT maps one logical point to one device point; considering typical resolution of 600 DPI for a printer, your stuff will be few times smaller that on the screen.
Use MM_HIENGLISH or some other device-independent mode; here is MSDN link.

I have used the following to successfully print "Hello World!" and "Have a Nice Day!" to the right of a 200x200 monochrome bitmap (MyLogo.bmp) placed at the origin of the printer page (I am using a black & white thermal printer):
CDC printDC( GetMyPrintDC() ); // e.g. as per original code
DOCINFO di( GetMyDocInfo() ); // e.g. as per original code
printDC.StartDoc( &di );
ATL::CImage logo;
logo.Load( "MyLogo.bmp" );
const BOOL result( logo.Draw( printDC.GetSafeHdc(), CPoint( 0, 0 ) ) );
CFont myFont, *old;
myFont.CreatePointFont(100, "Courier New", &printDC);
old = printDC.SelectObject(&myFont);
printDC.TextOut( 250, 50, " Hello World!" );
printDC.TextOut( 250, 150, "Have a nice Day!" );
printDC.SelectObject( old );
myFont.DeleteObject();
printDC.EndPage();
printDC.EndDoc();
printDC.DeleteDC();
The three indented lines highlight all that is required to render a CImage on my printer. Vary the parameter in CreatePointFont() to size the (otherwise tiny) text to suit.

Related

Copy a bitmap image to the clipboard in Win 10 with GDI Scaling active

I have an MFC desktop application that generates a graphics image, and I want the user to be able to copy it to the clipboard as a bitmap. I use standard clipboard API routines and they have been working fine up to now. However, I have now activated GDI Scaling (via a manifest) so that the program can run (reasonably well) on HiDPI displays, and I find that the image placed on the clipboard is just the upper-left quadrant of the actual image.
This is presumably due to the internal scaling of the image - the display is running at 200%.
How do I copy the whole image?
The key is given in the answer to a previous question:
Saving Bitmaps on Windows 10 with GDI scaling active
but it took me some time to adapt that answer to my particular problem, so I thought I'd share in case it was helpful to others. (Disclaimer - I'm no expert, so there may be better ways...)
bool CopyRectToClipboard(CWnd *pW, CRect rctCopy)
{
if (!pW->OpenClipboard())
return false;
if (!EmptyClipboard())
return false;
CClientDC dc(pW);
CDC dcMem;
VERIFY(dcMem.CreateCompatibleDC(&dc));
CBitmap bmTrial; // need a trial bitmap to get GDI Scaled size
VERIFY(bmTrial.CreateCompatibleBitmap(&dc, rctCopy.Width(), rctCopy.Height()));
// see https://stackoverflow.com/questions/51169846/saving-bitmaps-on-windows-10-with-gdi-scaling-active
BITMAPINFO bi = {};
bi.bmiHeader.biSize = sizeof(bi);
int result = GetDIBits(dc.GetSafeHdc(), (HBITMAP)bmTrial.GetSafeHandle(), 0, 0, NULL, &bi, DIB_RGB_COLORS);
CBitmap bm;
VERIFY(bm.CreateCompatibleBitmap(&dc, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight));
ASSERT(bm.m_hObject != NULL);
CBitmap* pbmOld = dcMem.SelectObject(&bm);
//dcMem.PatBlt(0, 0, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight, WHITENESS); // legacy
VERIFY(dcMem.BitBlt(0, 0, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight,
&dc, rctCopy.left - 1, rctCopy.top - 1, SRCCOPY));
HGDIOBJ hBM = bm.Detach();
VERIFY(::EmptyClipboard());
VERIFY(::SetClipboardData(CF_BITMAP, hBM));
VERIFY(::CloseClipboard());
dcMem.SelectObject(pbmOld);
dcMem.DeleteDC();
return true;
}

Creating, displaying, and then accessing bitmap/DIB data (w/o GetBitmapBits())

I have inherited an old-school MFC Windows CE program, and am having to make some modifications to it. As part of this I have to create a monochrome image with text on it, and both display it on a screen as well as send each row of the image to a printer one at a time.
I originally used a bitmap, and had success using DrawText() and getting a test string (Hello World) to display on the screen (this code is in Figure 1). However, I hit a wall at the stage where I am looking to extract the wrap data from the bitmap. What I am trying to get is an array with 1s or 0s representing black or white. I had first thought I would use GetBitmapBits() but unfortunately the code I am working with is so old that function is not supported yet. I thought I could get around this issue by using GetBitmap() and then accessing the bmBits parameter. However this appears to always be null which was confirmed when I found the following link: Why does GetObject return an BITMAP with null bmBits?.
My next attempt was to follow the advice in the link and use CreateDIBSection() instead of CreateCompatibleBitmap(). This seems like the right path, and I should have access to the data I want, but unfortunately I cannot get the DIB to display (code is in Figure 2). I suspect I am doing something wrong in creating the header of the DIB, but I cannot figure out what my mistake is.
If anyone has suggestions for a way to access the data in the bitmap, or can see what I am doing wrong with the DIB, I would greatly appreciate the help!
*** FIGURE 1: Code to create and display a bitmap
void CRunPage::OnPaint()
{
CPaintDC dc(this); // property page device context for painting
CBitmap mBmp; // CBitmap object for displaying built-in bitmaps
CDC mDCMem; // CDC object to handle built-in bitmap
int iWidth, iHeight; // dimension to draw on the screen
int icurLabel, // current label index of open print file
iLabelNum; // number of labels in open print file
LPBITMAPINFOHEADER pBMIH; // bitmap header object for current label
LPBYTE pImage; // bitmap data for current label
CSize size; // size of label
int PreviewLeft,PreviewTop,PreviewWidth,PreviewHeight;
CRect Rect;
BITMAP bm;
LPVOID bmBits=NULL;
// Calculate the preview area
PreviewLeft=5;
PreviewTop=5;
GetDlgItem(IDC_RUN_NEXT)->GetWindowRect(&Rect);
ScreenToClient(&Rect);
PreviewWidth=Rect.left-PreviewLeft*2;
GetDlgItem(IDC_RUN_WRAPTEXT)->GetWindowRect(&Rect);
ScreenToClient(&Rect);
PreviewHeight=Rect.top-PreviewTop*2;
CRect textRect;
CString testText(_T("Hello World"));
CBitmap * pOldBitmap;
CBrush whiteBrush, *pOldBrush;
CPen blackPen, *pOldPen;
mDCMem.CreateCompatibleDC(&dc);
mBmp.CreateCompatibleBitmap(&dc, PreviewWidth+PreviewLeft*2, PreviewHeight+PreviewTop*2);
//mBmp.CreateCompatibleBitmap(&dc, PreviewWidth, PreviewHeight);
pOldBitmap = mDCMem.SelectObject(&mBmp);
blackPen.CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
whiteBrush.CreateSolidBrush(RGB(255,255,255));
textRect.SetRect(0,0,PreviewWidth, PreviewHeight);
// this means behind the text will be a white box w/ a black boarder
pOldBrush = mDCMem.SelectObject(&whiteBrush);
pOldPen = mDCMem.SelectObject(&blackPen);
//these commands draw on the memory-only context (mDCMem)
mDCMem.Rectangle(&textRect);
mDCMem.DrawText((LPCTSTR)testText, 11, &textRect, DT_CENTER|DT_VCENTER);
mDCMem.SelectObject(pOldBrush);
mDCMem.SelectObject(pOldPen);
dc.StretchBlt(PreviewLeft,PreviewTop, PreviewWidth, PreviewHeight, & mDCMem, 0, 0, PreviewWidth, PreviewHeight, SRCCOPY);
mDCMem.SelectObject(pOldBitmap);
}
*** FIGURE 2: Trying to use a DIB instead of a bitmap
void CRunPage::OnPaint()
{
CPaintDC dc(this); // property page device context for painting
CBitmap mBmp; // CBitmap object for displaying built-in bitmaps
CDC mDCMem; // CDC object to handle built-in bitmap
int iWidth, iHeight; // dimension to draw on the screen
int icurLabel, // current label index of open print file
iLabelNum; // number of labels in open print file
LPBITMAPINFOHEADER pBMIH; // bitmap header object for current label
LPBYTE pImage; // bitmap data for current label
CSize size; // size of label
int PreviewLeft,PreviewTop,PreviewWidth,PreviewHeight;
CRect Rect;
BITMAP bm;
// Calculate the preview area
PreviewLeft=5;
PreviewTop=5;
GetDlgItem(IDC_RUN_NEXT)->GetWindowRect(&Rect);
ScreenToClient(&Rect);
PreviewWidth=Rect.left-PreviewLeft*2;
GetDlgItem(IDC_RUN_WRAPTEXT)->GetWindowRect(&Rect);
ScreenToClient(&Rect);
PreviewHeight=Rect.top-PreviewTop*2;
CRect textRect;
CString testText(_T("Hello World"));
CBitmap * pOldBitmap;
CBrush whiteBrush, *pOldBrush;
CPen blackPen, *pOldPen;
LPBYTE pFWandImageMem=NULL, pImageMem=NULL, pTemp=NULL;
int i=0,j=0, buffSize=0, numBytesPerRow=0, bitmapWidthPix,bitmapHeightPix;
char *numBytesPerRowString;
char temp;
void ** ppvBits;
BITMAPINFOHEADER bmif;
BITMAPINFO bmi;
HBITMAP myDIB, myOldDIB;
mDCMem.CreateCompatibleDC(&dc);
//this rect is the area in which I can draw (its x,y location is set by BitBlt or StretchBlt
//mBmp.CreateCompatibleBitmap(&dc, PreviewWidth+PreviewLeft*2, PreviewHeight+PreviewTop*2);
bmif.biSize = sizeof(BITMAPINFOHEADER);
bmif.biWidth = PreviewWidth+PreviewLeft*2;
bmif.biHeight = -(PreviewHeight+PreviewTop*2);//- means top down (I think? I tried both ways and neither worked)
bmif.biPlanes = 1;
bmif.biBitCount = 1;
bmif.biCompression = BI_RGB; // no compression
bmif.biSizeImage = 0; // Size (bytes) if image - this can be set to 0 for uncompressed images
bmif.biXPelsPerMeter = 0;
bmif.biYPelsPerMeter = 0;
bmif.biClrUsed =0;
bmif.biClrImportant = 0;
bmi.bmiColors[0].rgbBlue=0;
bmi.bmiColors[0].rgbGreen=0;
bmi.bmiColors[0].rgbRed=0;
bmi.bmiColors[0].rgbReserved=0;
bmi.bmiColors[1].rgbBlue=255;
bmi.bmiColors[1].rgbGreen=255;
bmi.bmiColors[1].rgbRed=255;
bmi.bmiColors[1].rgbReserved=0;
bmi.bmiHeader=bmif;
myDIB = CreateDIBSection(dc.GetSafeHdc(), &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0);
myOldDIB = (HBITMAP)mDCMem.SelectObject(myDIB);//SelectObject(mDCMem, myDIB);
blackPen.CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
whiteBrush.CreateSolidBrush(RGB(255,255,255));
textRect.SetRect(0,0,PreviewWidth, PreviewHeight);
// this means behind the text will be a white box w/ a black boarder
pOldBrush = mDCMem.SelectObject(&whiteBrush);
pOldPen = mDCMem.SelectObject(&blackPen);
//these commands draw on the memory-only context (mDCMem)
mDCMem.Rectangle(&textRect);
mDCMem.DrawText((LPCTSTR)testText, 11, &textRect, DT_CENTER|DT_VCENTER);
mDCMem.SelectObject(pOldBrush);
mDCMem.SelectObject(pOldPen);
dc.StretchBlt(PreviewLeft,PreviewTop, PreviewWidth, PreviewHeight, & mDCMem, 0, 0, PreviewWidth, PreviewHeight, SRCCOPY);
mDCMem.SelectObject(myOldDIB);
}
So I made two minor changes to the DIB code, and it is displaying the image correctly now.
First, I changed the way I passed in my pointer to the CreateDIBSection():
void ** ppvBits;
to
LPBYTE pBits;
And then I had to change how I passed that into CreateDIBSection. I also explicitly casted the return of CreateDIBSection() to an HBITMAP:
myDIB = CreateDIBSection(dc.GetSafeHdc(), &bmi, DIB_RGB_COLORS, (void**)&pBits, NULL, 0);
to
myDIB = (HBITMAP) CreateDIBSection(dc.GetSafeHdc(), &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0);
I have not had a chance to see if I can access the image data, but I am past the initial issues now. Thanks to anyone who looked at this, and if people know how to do the first (device dependent bitmap) method I would be interested to know.

gdi+ draws string inverted

I am trying to write text to a bitmap i am getting from an usb camera using directshow.
The problem is that the text is mirror inverted upside-down and i don't know why.
Here is the code that writes the text:
BITMAPINFOHEADER bih = m_videoInfo.bmiHeader;
Bitmap bmp(bih.biWidth, bih.biHeight, m_stride, m_pixFmt, pBuffer);
Graphics g(&bmp);
if (this->introTimer->timeToDo())
{
RectF pos(10, 10, 100, 100);
SolidBrush brush(Color::Black);
Font font(FontFamily::GenericSerif(), 30);
hr = g.DrawString(this->introText, -1, &font, pos, StringFormat::GenericDefault(), &brush);
return hr;
}
I am not sure if my code is the only thing that affects the drawing of the string. Maybe there is some configuration or something.
Update
I tried using a negative height as suggested by Hans Passant. The result is that the text is not written at all.
The obvious thing to do would be to set the transformation on GDI+.
Essentially you need to invert the Y axis (though by doing this it would now draw off screen). So you need to then translate it down by the window size.
Something like this:
graphics.Transform = new Matrix2D( 1, 0,
0, -1,
0, -windowHeight );
Then draw as normal.
(Its worth noting I'm suggesting this without testing it. The y translation may not be negative so try both!).
The scanlines are stored upside down, with the first scan (scan 0) in memory being the bottommost scan in the image. This is another artifact of Presentation Manager compatibility. GDI automatically inverts the image during the Set and Get operations.
About the suggestion from Hans Passant, did you try negative height in position?
He suggests to use negative height in bih.
BITMAPINFOHEADER bih = m_videoInfo.bmiHeader;
bih.biHeight = -bih.biHeight;
Bitmap bmp(bih.biWidth, bih.biHeight, m_stride, m_pixFmt, pBuffer);
...
Also copying another solution from this page if it helps
void BitmapControl::OnPaint()
{
EnterCriticalSection (&CriticalSection);
if (_handleBMP)
{
CPaintDC dc(this);
//dc.SetMapMode(MM_ISOTROPIC);
dc.SetMapMode(MM_LOENGLISH);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CRect rect;
GetClientRect(&rect);
dc.DPtoLP(&rect);
CBitmap* pBmpOld = dcMem.SelectObject(CBitmap::FromHandle(_handleBMP));
//tst
dc.SetStretchBltMode(COLORONCOLOR);
//BitBlt(dc,rect.left,-0,rect.Width(),rect.Height(),dcMem,rect.left,rect.top,SRCCOPY); //works with MM_TEXT but upsidedown
BitBlt(dc,0,rect.bottom,rect.Width(),-rect.Height(),dcMem,0,0,SRCCOPY); //works with MM_LOENGLISH
dcMem.SelectObject(pBmpOld);
DeleteDC(dc);
DeleteDC(dcMem);
DeleteObject(_handleBMP);
DeleteObject(pBmpOld);
_handleBMP = NULL;
}
LeaveCriticalSection (&CriticalSection);
}

How to draw text using CDC with transparent background on CBitmap?

I have the following code which sort of works provided you mask out the pink pixels however what I actually want is transparent bits like a PNG file so that I can avoid alpha blending issues and the need mask out a specific color everywhere the bitmap will be used.
CClientDC dc(pWnd);
CDC memDC;
if(!memDC.CreateCompatibleDC(&dc))
return NULL;
CRect bitmapRect;
bitmapRect.SetRectEmpty();
CFont* pOldFont = memDC.SelectObject(pWnd->GetFont());
CSize fontSize = memDC.GetTextExtent(imageText);
bitmapRect.right = fontSize.cx;
bitmapRect.bottom = fontSize.cy;
CBitmap bitmap;
if(!bitmap.CreateCompatibleBitmap(&dc, bitmapRect.Width(), bitmapRect.Height()))
return NULL;
CBitmap* pOldMemDCBitmap = memDC.SelectObject(&bitmap);
memDC.FillSolidRect(&bitmapRect, RGB(255,0,255));
//memDC.SetBkMode(TRANSPARENT); // doesn't work
//memDC.SetBkColor(TRANSPARENT); // doesn't work
memDC.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
//memDC.DrawText(imageText, bitmapRect, DT_TOP|DT_LEFT|DT_NOCLIP); // same difference
memDC.TextOut(0, 0, imageText);
memDC.SelectObject(pOldMemDCBitmap);
memDC.SelectObject(pOldFont);
memDC.DeleteDC();
CImageList bmImage;
bmImage.Create(bitmapRect.Width(), bitmapRect.Height(), ILC_COLOR32|ILC_MASK, 0, 1);
// this masks out the pink but for some windows blends edges of text causing pink text instead of black!
bmImage.Add(&bitmap, RGB(255,0,255));
Is just the bug filled beast that is MFC misbehaving or am I missing something?
Simple DrawText() with transparent background without MFC:
// in my case a user drawn button:
_windowHandle = CreateWindowEx(...);
SendMessage(_windowHandle, WM_SETFONT, (WPARAM)font, (LPARAM)NULL);
...
// WM_DRAWITEM
SetTextColor(hDC, RGB(216, 27, 27));
SetBkMode(hDC, TRANSPARENT);
RECT rect = { 0, 0, backgroundBitmap.bmWidth, backgroundBitmap.bmHeight };
DrawText(hDC, _text.c_str(), -1, &rect, DT_CENTER | DT_WORDBREAK);
--hfrmobile
About 10 minutes after asking this I read my own comment and realized that "some windows" means it was related to the windows being passed in. Specifically the font being used from said window. Fonts with default properties were exhibiting the strange blending.
At the end of the day I determined I needed to modify the font to turn off the things messing up my drawing code. I eventually narrowed it down to the one culprit causing the problem:
CClientDC dc(pWnd);
CDC memDC;
if(!memDC.CreateCompatibleDC(&dc))
return NULL;
LOGFONT tempFont;
CFont* winFont = pWnd->GetFont();
if (winFont)
winFont->GetLogFont(&tempFont);
else
{
// generate a likely font
SecureZeroMemory(&tempFont, sizeof(LOGFONT));
//TEXTMETRIC txt;
//GetTextMetrics(memDC, &txt);
//tempFont.lfHeight = txt.tmHeight * -1; // 16 is too big looking
tempFont.lfHeight = -12;
tempFont.lfWeight = FW_NORMAL;
tempFont.lfCharSet = DEFAULT_CHARSET;
wcscpy_s(tempFont.lfFaceName, L"Segoe UI"); // Win7 control default
}
tempFont.lfQuality = NONANTIALIASED_QUALITY; // this is the fix!!!
CFont newFont;
newFont.CreateFontIndirect(&tempFont);
CFont* pOldFont = memDC.SelectObject(&newFont);
// ... other stuff same as before ...
So I still FillSolidRect pink then draw my icons, text, whatever I want, etc. Then mask out the pink pixels. With the font quality adjustment it no longer blends pink into the font text and it looks good. The else case above creates a default font to use in case the CWnd* passed in doesn't have a valid one specified.

C++ GDI+ drawing text on a transparent layered window

(unmanaged C++)
I already succeeded drawing PNG files to a transparent layered window that I can drag around the desktop, but now my problem is drawing text on a transparent layered window
Here's my code and my attempt at drawing text in the middle, it's important to note that i'm using the screenDC instead of using the one in WM_PAINT messages
[edit]
updated code after the comments, now i'm just trying to write text on the bitmap before getting the HBITMAP version which i need to use
this time I'm using DrawString because textout() isn't GDI+, I hope DrawString really is GDI+ lol
still doesn't work though, wonder what i'm doing wrong
void Draw() // draws a frame on the layered window AND moves it based on x and y
{
HDC screenDC( NULL ); // grab screen
HDC sourceDC( CreateCompatibleDC(screenDC) );
POINT pos = {x,y}; // drawing location
POINT sourcePos = {0,0}; // top left of image
SIZE size = {100,100}; // 100x100 image
BLENDFUNCTION blendFunction = {0};
HBITMAP bufferBitmap = {0};
Bitmap* TheBitmap = crnimage; // crnimage was already loaded earlier
// ------------important part goes here, my attempt at drawing text ------------//
Gdiplus::Graphics Gx(TheBitmap);
// Font* myFont = new Font(sourceDC);
Font myFont(L"Arial", 16);
RectF therect;
therect.Height = 20;
therect.Width = 180;
therect.X = 0;
therect.Y = 0;
StringFormat format;
format.SetAlignment(StringAlignmentCenter);
format.GenericDefault();
Gdiplus::SolidBrush GxTextBrush(Gdiplus::Color(255, 255, 0,255));
WCHAR thetext[] = L"Sample Text";
int stats = Gx.DrawString(thetext, -1, &myFont, therect, &format, &GxTextBrush);
if(stats) // DrawString returns nonzero if there is an error
msgbox(stats);
stats = Gx.DrawRectangle(&Pen(Color::Red, 3), therect);
// the rectangle and text both draw fine now
// ------------important part goes here, my attempt at drawing text ------------//
TheBitmap->GetHBITMAP(0, &bufferBitmap);
HBITMAP oldBmpSelInDC;
oldBmpSelInDC = (HBITMAP)SelectObject(sourceDC, bufferBitmap);
// some alpha blending
blendFunction.BlendOp = AC_SRC_OVER;
blendFunction.SourceConstantAlpha = wndalpha;
blendFunction.AlphaFormat = AC_SRC_ALPHA;
COLORREF colorKey( RGB(255,0,255) );
DWORD flags( ULW_ALPHA);
UpdateLayeredWindow(hWnd, screenDC, &pos, & size, sourceDC, &sourcePos,
colorKey, &blendFunction, flags);
// release buffered image from memory
SelectObject(sourceDC, oldBmpSelInDC);
DeleteDC(sourceDC);
DeleteObject(bufferBitmap);
// finally release the screen
ReleaseDC(0, screenDC);
}
I've been trying to write text on my layered window for two days now, but from those attempts I know there are several ways I can go about doing this
(unfortunately I have no idea how exactly)
The usual option I see is drawing text on a bitmap, then rendering the bitmap itself
Use Gdi+ to load a bitmap
Create a Graphics object from the bitmap
Use DrawString to write text to the bitmap
Dispose of the Graphics object
Use the bitmap Save method to save the result to a file
Apparently one can also make a graphics object from a DC, then draw text on the DC, but again i have no clue as to how to do this
The overall approach looks right, but I think you've got some problems with the DrawString call. Check out the documentation (especially the sample) on MSDN.
Gx.DrawString(thetext, 4, NULL, therect, NULL, NULL)
The third, fifth, and sixth parameters (font, format, and brush) probably need to be specified. The documentation doesn't say that they are optional. Passing NULL for these is probably causing GDI+ to treat the call as a no-op.
The second parameter should not include the terminating L'\0' in the string. It's probably safest to use -1 if your string is always terminated.