2

I have some old C++/MFC code which uses GDI to create an image in a device context (CDC), using functions like Rectangle. The image is then painted to the screen, by the destructor of CPaintDC. Everything works as expected.

Now I want to store the image in a PNG file. For this, I am using Windows Imaging Component (WIC). I can create an image in WIC by creating a bitmap frame, storing pixels in a buffer, and copying the pixels into it using WritePixels. Then WIC stores the image as a PNG file via Commit. All this also works.

The question is, how can I transfer the image I created using GDI, which is in a CDC, to something WIC recognizes? I'm looking for something more efficient than a CDC::GetPixel loop storing into a buffer followed by WritePixels.

Windows 7, Visual Studio 2015, C++.

Update: I'm trying to get the CImage method suggested by @Andrew Komiagin. My code is

CDC memdc;
memdc.CreateCompatibleDC(nullptr);
myControl.RenderToDC(memdc); // this draws the image onto the dc
CBitmap bmp;
BOOL ok=bmp.CreateCompatibleBitmap(&memdc, w, h); // w=h=292 defined elsewhere
CBitmap *pOldBitmap=memdc.SelectObject(&bmp);
ok=memdc.BitBlt(0, 0, w, h, &memdc, 0, 0, SRCCOPY);
CImage tempImageObj;
tempImageObj.Attach((HBITMAP)bmp.Detach());
CString outputFilename("outputImage.png");
HRESULT hr=tempImageObj.Save(outputFilename);

This produces a PNG image file with an all-black square, w x h pixels, rather than the image I drew. I know the image is being drawn correctly by RenderToDC (see below).

For comparison, this is in MyControl::OnPaint for the control (derived from CStatic):

CPaintDC dc(this); // device context for painting
RenderToDC(dc); // this draws the image onto the dc
// Painted to screen by CPaintDC's destructor

The image appears correctly.

Woody20
  • 791
  • 11
  • 30
  • You can create an HBITMAP from CDC, then use IWICImagingFactory::CreateBitmapFromHBITMAP. – Simon Mourier Oct 18 '15 at 08:03
  • @Simon Mourier: I tried this originally, but I couldn't figure out how to get the bitmap from the CDC. Using the HBITMAP operator gets a handle to an empty bitmap. Apparently, the CDC contains the drawn pixels, but its attached bitmap does not. Can you show an example of how to get the pixels into the bitmap? – Woody20 Oct 18 '15 at 19:50
  • This is explained here http://stackoverflow.com/questions/351236/copy-hdc-contents-to-bitmap .With MFC you can use the CBitmap class instead of GDI directly of course there is an example (SaveAsImage) here: http://www.codeproject.com/Articles/253054/Converting-Device-context-to-images-PNG-JPEG-BMP-G – Simon Mourier Oct 19 '15 at 07:57
  • You are creating a monochrome bitmap (`bmp.CreateCompatibleBitmap(&memdc, w, h);`). See [CreateCompatibleBitmap](https://msdn.microsoft.com/en-us/library/dd183488.aspx): *"When a memory device context is created, it initially has a 1-by-1 monochrome bitmap selected into it. If this memory device context is used in **CreateCompatibleBitmap**, the bitmap that is created is a* monochrome *bitmap."* You need to pass a device context with appropriate color information. – IInspectable Oct 20 '15 at 15:25

1 Answers1

1

I'd suggest using standard CImage class from ATL/MFC that works just great with GDI device context and natively supports the ability to load and save images in JPEG, GIF, BMP, and Portable Network Graphics (PNG) formats.

Here is an example on how to use it:

CDC MemDC;
MemDC.CreateCompatibleDC(&dc);
CBitmap Bmp;
Bmp.CreateCompatibleBitmap(&dc,ClientRect.Width(),ClientRect.Height());
MemDC.SelectObject(&Bmp);
MemDC.BitBlt(0,0,ClientRect.Width(),ClientRect.Height(),&dc,0,0,SRCCOPY);
CImage TempImageObj;
TempImageObj.Attach((HBITMAP)Bmp.Detach());
TempImageObj.Save(sFilePath);
Andrew Komiagin
  • 6,446
  • 1
  • 13
  • 23