Correctly release resources after flipping a bitmap - visual-studio-2010

I have an application that writes a very large data set as a bitmap. [~500MB] I am writing the data patch wise from top to down. Due to the nature of BMP's file structure it must be flipped once written to the disk. (I wrote it this way because I assumed that flipping a bitmap would be a common application and I would find libraries to do the task)
I am using a code snippet I found on the internet to flip the bitmap. This one:
// GetInvertedBitmap - Creates a new bitmap with the inverted image
// Returns - Handle to a new bitmap with inverted image
// hBitmap - Bitmap to invert
// bLateral - Flag to indicate whether to invert laterally or vertically
HBITMAP CRisatImport::GetInvertedBitmap( HBITMAP hBitmap, BOOL bLateral )
{
// Create a memory DC compatible with the display
CDC sourceDC, destDC;
sourceDC.CreateCompatibleDC( NULL );
destDC.CreateCompatibleDC( NULL );
// Get logical coordinates
BITMAP bm;
::GetObject( hBitmap, sizeof( bm ), &bm );
// Create a bitmap to hold the result
HBITMAP hbmResult = ::CreateCompatibleBitmap(CClientDC(NULL),
bm.bmWidth, bm.bmHeight);
// Select bitmaps into the DCs
HBITMAP hbmOldSource = (HBITMAP)::SelectObject( sourceDC.m_hDC, hBitmap );
HBITMAP hbmOldDest = (HBITMAP)::SelectObject( destDC.m_hDC, hbmResult );
if( bLateral )
destDC.StretchBlt( 0, 0, bm.bmWidth, bm.bmHeight, &sourceDC,
bm.bmWidth-1, 0, -bm.bmWidth, bm.bmHeight, SRCCOPY );
else
destDC.StretchBlt( 0, 0, bm.bmWidth, bm.bmHeight, &sourceDC,
0, bm.bmHeight-1, bm.bmWidth, -bm.bmHeight, SRCCOPY );
// Reselect the old bitmaps
::SelectObject( sourceDC.m_hDC, hbmOldSource );
::SelectObject( destDC.m_hDC, hbmOldDest );
return hbmResult;
}
The problem is that I have a limited understanding of the above code. I tried to write a function to use the above snippet the best I could from sample code from MSDN. I do not think I am releasing all resources correctly. And I cant seem to figure out the error - mainly because my lack of knowledge about GDI. I would really appreciate it if some one pointed out what I am doing wrong.
The program crashes if I try to call this function twice - that is why I suspect that I am releasing resources incorrectly.
This is the function I wrote:
void CRisatImport::flipBitMapSaveAsJPG( CString outputFileName, CString outputJPGName, bool saveAsJpg)
{
// Flip the bitmap to correct odd file structure
HBITMAP hBitmap;
hBitmap = (HBITMAP)::LoadImage(NULL, outputFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
hBitmap = GetInvertedBitmap( hBitmap, FALSE );
CImage image;
image.Attach(hBitmap);
image.Save(outputFileName,Gdiplus::ImageFormatBMP);
if(saveAsJpg){
image.Save(outputJPGName,Gdiplus::ImageFormatJPEG);
}
image.Destroy();
DeleteObject( hBitmap );
}
I am using MFC and VS2010 for this application - I have 4GB of RAM and no other applications running.
This is the error I get:

When you Attach your bitmap handle to CImage you're handing over responsibility for the handle's lifetime to CImage so you don't want to Destory it after. But you're also using the same variable name for your new bitmap. Try this instead:
void CRisatImport::flipBitMapSaveAsJPG( CString outputFileName, CString outputJPGName, bool saveAsJpg)
{
// Flip the bitmap to correct odd file structure
HBITMAP hBitmapLoad = (HBITMAP)::LoadImage(NULL, outputFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HBITMAP hBitmapInverted = GetInvertedBitmap( hBitmapLoad , FALSE );
DeleteObject( hBitmapLoad );
CImage image;
image.Attach(hBitmapInverted );
image.Save(outputFileName,Gdiplus::ImageFormatBMP);
if(saveAsJpg){
image.Save(outputJPGName,Gdiplus::ImageFormatJPEG);
}
image.Destroy();
}

Related

Retrieve Physical Font Data from Logical Font Handle

I wrote a font parser and renderer based on some functionality in the freetype library, but it currently only works for font files that I specify. I'd like to be able to retrieve fonts from the Windows Font Mapper and then parse and render them, but I can't figure out how to get the actual font data from a logical font.
I thought that the easy way to accomplish this would be to get the font file name from the logical font, but based on other stack overflow posts, there isn't any good solution to this because there isn't a 1 to 1 mapping of physical fonts to the logical font you specify.
This code shows how I'm currently retrieving a handle to a logical font:
HFONT windowsFont = CreateFontA(72, 0, 0, 0, 0,
0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, "Arial");
HDC deviceContext = GetDC(windowHandle);
SelectObject(deviceContext, windowsFont);
DWORD fontSize = GetFontData(deviceContext, 0, 0, NULL, 0);
void *fontData = malloc(fontSize);
fontSize = GetFontData(deviceContext, 0, 0, fontData, 0);
char *fontFileData;
size_t fontFileSize;
// This is a function that I wrote that does what you'd expect. It opens
// a file, reads all the bytes to a buffer and closes the file
readFileToBuff(&fontFileData, &fontFileSize, "c:/windows/fonts/arial.ttf");
assert(fontFileSize == fontSize); // This passes
assert(((char)fontFileData) == ((char)fontData)); // This fails
Based on this stack overflow post which is for Java, I'm thinking that what I want to do may not be possible. It seems that the only solution may be to look at all the system font files and try to figure out what they are.
How to get ttf font data from system fonts in java
This surprises me though, because it seems that it would be relatively common for a program to want to render fonts themselves, without relying on the Windows renderer. Does anyone know of a way to get the font data, or how other people have solved this problem?
I don't understand why it's getting the exact same amount of bytes as
the file, but the data isn't the same.
From the official sample, I found the correct way to use GetFontData.
This is the modified code, and the returned data is the same as fontFileData.
HRESULT hr = S_OK;
LPVOID ptr = NULL;
HGLOBAL hGlobal = NULL;
...
HFONT windowsFont = CreateFontA(72, 0, 0, 0, 0,
0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, "Arial");
HDC deviceContext = GetDC(hWnd);
SelectObject(deviceContext, windowsFont);
DWORD fontSize = GetFontData(deviceContext, 0, 0, NULL, 0);
hGlobal = GlobalAlloc(GMEM_MOVEABLE, fontSize);
ptr = GlobalLock(hGlobal);
if (!ptr)
{
hr = HRESULT_FROM_WIN32(GetLastError());
fwprintf(stderr, L"ERROR: Could not lock global memory object: %08X\n", hr);
}
else
{
if (GetFontData(deviceContext, 0, 0, ptr, fontSize) == GDI_ERROR)
{
fwprintf(stderr, L"ERROR: Could not get font data\n");
hr = E_UNEXPECTED;
}
GlobalUnlock(hGlobal);
}
char *fontFileData;
size_t fontFileSize;
// This is a function that I wrote that does what you'd expect. It opens
// a file, reads all the bytes to a buffer and closes the file
readFileToBuff(&fontFileData, &fontFileSize, "c:/windows/fonts/arial.ttf");
assert(fontFileSize == fontSize); // This passes
assert(((char)fontFileData) == ((char)fontData)); // This fails

Windows - GDI - Scaling a screen DC to a printer DC without modifying the draw functions

I'm writing a Windows application showing a document to the user. The content is painted using the GDI functions, and all appears as expected on the screen.
Now I want to print this document. I get a printer device context, and I do the exact same drawing as I do on the screen. Of course the printed content appears tiny on the top of the printed page. The reason of this behavior is clear for me, and is fully explained here:
https://www.codeproject.com/Articles/764057/GDI-Drawing-and-Printing
So I need to add a scaled viewport on my printer DC, and there are several functions to achieve that in the GDI. However I'm a little puzzled about HOW to configure these functions. I tried various examples found on the internet, but none of them worked for me.
My screen resolution is 1920x1080 pixels, and I'm trying to print on an A4 portrait page. I tested various configurations, and I found that the best approximation to fit on my printed page is the following:
::SetMapMode(hDC, MM_ISOTROPIC);
::SetWindowExtEx(hDC, 1, 1, NULL);
::SetViewportExtEx(hDC, 5, 5, NULL);
::SetViewportOrgEx(hDC, -10200, 0, NULL);
As the screen and print configurations may, of course, change on other PC, I need to know how the above values may be calculated, but I cannot find a formula that works in my case. Especially I don't know why I need to scale my canvas origin using the SetViewportOrgEx() function, nobody mentioned that on the documents I read.
So what is the correct manner to calculate my print DC viewport, considering that:
The exactly same painting functions will be used for both the screen and printer drawing, and I will NEVER write different functions to print on the screen and the printer
The screen and printer devices may be entirely configured by the user, but the printed result should always fit the document on both the screen and the printer
And as an additional question, it would be better to use a metafile to do this kind of job?
In order to map the screen coordinates to paper coordinates, we need the width and length of the paper. This information is available in GetDeviceCaps(hdc, PHYSICALWIDTH) and GetDeviceCaps(hdc, PHYSICALHEIGHT), where hdc is printer's device context. We already have the screen coordinates somewhere.
The printer cannot print on the edges of the paper. We can get that information from PHYSICALOFFSETX and PHYSICALOFFSETY.
The example below uses uses a common function paint which does all the painting. print doesn't do any painting, it calls paint instead.
This assumes that rc.left and rc.right is (0,0) in screen coordinates.
void paint(HDC hdc, RECT rc)
{
HBRUSH brush = GetSysColorBrush(COLOR_WINDOWTEXT);
InflateRect(&rc, -10, -10);
FrameRect(hdc, &rc, brush);
DrawText(hdc, L"hello world", -1, &rc, 0);
}
void print(HWND hWnd, RECT rc)
{
PRINTDLG pd = { sizeof(pd) };
pd.hwndOwner = hWnd;
pd.Flags = PD_RETURNDC;
if(!PrintDlg(&pd))
return;
HDC hdc = pd.hDC;
DOCINFO doc = { sizeof(doc) };
StartDoc(hdc, &doc);
StartPage(hdc);
SetMapMode(hdc, MM_ISOTROPIC);
SetWindowExtEx(hdc, rc.right, rc.bottom, NULL);
SetViewportExtEx(hdc,
GetDeviceCaps(hdc, PHYSICALWIDTH), GetDeviceCaps(hdc, PHYSICALHEIGHT), NULL);
SetViewportOrgEx(hdc,
-GetDeviceCaps(hdc, PHYSICALOFFSETX), -GetDeviceCaps(hdc, PHYSICALOFFSETY), NULL);
paint(hdc, rc);
EndPage(hdc);
EndDoc(hdc);
DeleteObject(hdc);
}
Testing:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc);
paint(hdc, rc);
EndPaint(hwnd, &ps);
break;
}
case WM_LBUTTONDOWN:
{
RECT rc;
GetClientRect(hwnd, &rc);
print(hwnd, rc);
break;
}

Working with HBitmaps

I'm trying to save some HBITMAPs into an array, and display them at a later time. Creating a HBITMAP from a DC works, but when I try to display a saved HBITMAP I seem to get the wrong one. This leads me to think that I haven't really understood how HTBIMAPs are stored in memory. I was assuming that the data type HBITMAP is essentially a pointer to the object in memory and that that's all I need, but maybe there is more to it?
Here's a sample code:
HBITMAP aBitMaps[NUM_BITMAPS];
int iNumBitMaps;
void SaveScreen(CDC *dc)
{
if (iNumBitMaps>0)
{
if (iNumBitMaps>=NUM_BITMAPS)
{
iNumBitMaps=NUM_BITMAPS-1;
DeleteObject(aBitMaps[iNumBitMaps]);
}
for (int i=iNumBitMaps;i>0;i--)
{
aBitMaps[i] = aBitMaps[i-1];
}
}
iNumBitMaps++;
aBitMaps[0] = CreateCompatibleBitmap(dc->m_hDC, 800, 800);
HDC hdcMem = CreateCompatibleDC(dc->m_hDC);
SelectObject(hdcMem, aBitMaps[0]);
BitBlt(hdcMem, 0, 0, 800, 800, dc->m_hDC, 0, 0, SRCCOPY);
ReleaseDC(hdcMem);
}
void RestoreScreen(CDC *dc, int i)
{
if (i>=NUM_BITMAPS) i = NUM_BITMAPS-1;
if (i<0) i = 0;
HDC hdcMem = CreateCompatibleDC(dc->m_hDC);
SelectObject(hdcMem, aBitMaps[i]);
BitBlt(dc->m_hDC, 0, 0, 800, 800, hdcMem, 0, 0, SRCCOPY);
ReleaseDC(hdcMem);
}
So, the idea is essentially to push the HBITMAP pointers to an array, and at a later time display any of the stored images by selecting it into a memory DC and then copying it to the actual DC. What's wrong with this?
I'm using VC++, Visual Studio 2010, no MFC.
EDIT:
I did some more tests. For debugging, I tried to display all stored bitmaps (shifted in position) by adding the following for loop to SaveScreen
...
BitBlt(hdcMem, 0, 0, 800, 800, dc->m_hDC, 0, 0, SRCCOPY);
for (int j=0; j<iNumBitMaps;j++)
{
SelectObject(hdcMem, aBitMaps[j]);
BitBlt(dc->m_hDC, 20*(j+1), 100*(j+1), 800+20*(j+1), 800+100*(j+1), hdcMem, 0, 0, SRCCOPY);
}
ReleaseDC(hdcMem);
This then does display iNumBitMaps times the image, but it's always the same image (namely the one I stored in aBitMaps[0]). I did verify in the debugger, that aBitMaps contains all different pointers. So, somehow I think I don't correctly load the bitmaps into the memory DC.
I figured it out. In my code above there were two mistakes (of which I'm aware of). First, the memory DC must be deleted after use, so ReleaseDC must be replaced by DeleteDC.
Second, one must know that an object that is selected into a DC cannot be selected into another DC. Hence it is important that after operating on an object in the memory DC, the object must be unloaded by selecting the DC's previous object. The correct contruction is thus essentially
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, aBitMaps[0]);
BitBlt(...);
HBITMAP hbmNew = (HBITMAP)SelectObject(hdcMem, hbmOld);
where now hbmNew points to the same object as aBitMaps[0].

wglCreatePbufferARB with compatible device context

Is it possible to create pBuffer using wglCreatePbufferARB for a compatible device context ?
HDC fHDC = ::GetDC((HWND)_window);
HDC _fHDC = ::CreateCompatibleDC(fHDC);
HBITMAP memBM = ::CreateCompatibleBitmap ( fHDC, bounds.xMax - bounds.xMin, bounds.yMax - bounds.yMin );
::SelectObject ( _fHDC, memBM );
later on, i'm creating pBuffer using calls -
wglChoosePixelFormatARB(_fHDC, iAttribList, fAttribList, 1,
&pixelFormat, &numFormats)
hBuffer=wglCreatePbufferARB(_fHDC, pixelFormat, width, height, flags);
My call to wglChoosePixelFormatARB is failing

How do I correctly convert an ICON to a BITMAP using MFC?

I'm loading an ICON of another application via
HICON ico = ExtractIcon(NULL, L"path\\to\\OtherApp.exe", 0);
How can I create a CBitmap object from this icon?
Specifically (not really answered in the dup question for me):
Which device context?
At the end, I want a CBitmap object that outlives the function that converts the icon:
What do I need to clean up immediately and what do I need to keep around? (DC, ...?)
Here's the code I have so far:
void ConvertIconToBitmap(CBitmap& bmpObj, HICON hIcon, int cx, int cy) {
CClientDC clientDC(NULL);
CDC dc;
dc.CreateCompatibleDC(NULL);
CBitmap bmpTmp;
VERIFY( bmpTmp.CreateCompatibleBitmap(&clientDC, cx, cy) );
CBitmap* pOldBmp = (CBitmap*)dc.SelectObject(&bmpTmp);
VERIFY( ::DrawIconEx( dc.GetSafeHdc(), 0, 0, hIcon, cx, cy, 0, NULL, DI_NORMAL) );
dc.SelectObject( pOldBmp );
// For some reason I need to copy the bitmap here: (maybe it's the DIB flag)
HBITMAP hDibBmp = (HBITMAP)::CopyImage((HANDLE)(HBITMAP)bmpTmp, IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_CREATEDIBSECTION);
VERIFY( hDibBmp );
VERIFY( bmpObj.Attach(hDibBmp) );
// VERIFY( bmpObj.Attach(bmpTmp.Detach()) );
}
Now, this code works, but I don't understand it:
Why do I need a CClientDC? (If I use only CDC the bitmap is not shown or Black&White, depending on where I put it.)
(Why) is the dc.SelectObject( pOldBmp ) line needed?
Why do I have to do CopyImage? (If I don't, the bitmap is sometimes drawn with inverted colors.)
Does this code leak anything or is everything properly cleaned up?
Here's another version that also seems to work:
void ConvertIconToBitmap2(CBitmap& bmpObj, HICON hIcon, int cx, int cy) {
CClientDC clientDC(NULL);
CDC memDC;
memDC.CreateCompatibleDC(&clientDC);
ASSERT(hIcon);
ICONINFO info;
VERIFY(GetIconInfo(hIcon, &info));
BITMAP bmp;
GetObject(info.hbmColor, sizeof(bmp), &bmp);
HBITMAP hBitmap = (HBITMAP)CopyImage(info.hbmColor, IMAGE_BITMAP, 0, 0, 0);
ASSERT(hBitmap);
ASSERT(memDC.GetSafeHdc());
HBITMAP hOldBmp = (HBITMAP)memDC.SelectObject(hBitmap);
clientDC.BitBlt(0, 0, bmp.bmWidth, bmp.bmHeight, &memDC, 0, 0, SRCCOPY);
memDC.SelectObject(hOldBmp);
VERIFY( bmpObj.Attach(hBitmap) );
DeleteObject(info.hbmColor);
DeleteObject(info.hbmMask);
}
•Why do I need a CClientDC? (If I use only CDC the bitmap is not shown or Black&White, depending on where I put it.)
You would need a DC that is based on your window or the screen, just declaring a CDC is not enough, you will also need to call dc.Attach() or one of the CDC::Create* functions.
•(Why) is the dc.SelectObject( pOldBmp ) line needed?
So that the bitmap is disconnected from the DC
•Why do I have to do CopyImage? (If I don't, the bitmap is sometimes drawn with inverted colors.)
It looks like you are creating a device independent bimap using the CopyImage() call using the LR_CREATEDIBSECTION parameter
•Does this code leak anything or is everything properly cleaned up?
Looks ok to me!

Resources