How to save bitmap to file in C++?

Talk about things C/C++, some related to AutoHotkey
brandonhotkey
Posts: 113
Joined: 03 Nov 2013, 05:46

How to save bitmap to file in C++?

19 May 2014, 06:27

Do you have experience how to save bitmap to file? Is it possible to do it with image.save()?

I have bitmap in memory and would like to save it with image::save, but I cannot find how to do it. The example on MSDN uses image object created from file.

http://msdn.microsoft.com/en-us/library ... 85%29.aspx

Is it even possible, or it is useless for the bitmap?

Code: Select all

CLSID pngClsid;
GetEncoderClsid(L"image/png", &pngClsid);
// some code to initiate Image object and get the bitmap there
// then to save it
Image.Save(L"Mosaic2.png", &pngClsid, NULL);
I know AHK uses GdipSaveImageToFile

so theoretically it should be possible...
brandonhotkey
Posts: 113
Joined: 03 Nov 2013, 05:46

Re: How to save bitmap to file in C++?

20 May 2014, 09:05

I have solved the saving, but my problem now is that the image file does not contain the picture of the window. Can you help to fix it?

Code: Select all

// CODE 81 and 82 de facto no difference
#define	CODE		85 // 81
#define	WINDOW_MIN_HEIGHT		200
#define	WINDOW_MAX_HEIGHT	700
#define	WINSTYLE		0x14FF0000
#define	ROTATION_ANGLE		45

#define M_PI           3.14159265358979323846

// #define	IS_CHILD	TRUE  // [TRUE | FALSE | 0] ... 0 - deactivates this detection
// GetWindowLong: GWL_HWNDPARENT

#include <windows.h>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <cstring>
#include <string>
#include <tchar.h>
#include <conio.h>
#include <gdiplus.h>
#include <stdlib.h> // abs
#include <math.h> // cos
using namespace std;
using namespace Gdiplus;

int GetEncoderClsid (const WCHAR* format, CLSID* pClsid) 
{ 
   UINT num = 0;//number of image encoders 
   UINT size = 0;//size of the image encoder array in bytes 

   ImageCodecInfo* pImageCodecInfo = NULL; 

   GetImageEncodersSize (&num, &size); 
   if (size == 0) 
      return-1;//Failure 

   pImageCodecInfo = (ImageCodecInfo *) (malloc (size)); 
   if (pImageCodecInfo == NULL) 
      return-1;//Failure 

   GetImageEncoders (num, size, pImageCodecInfo); 

   for (UINT j = 0; j <num; ++ j) 
   { 
      if (wcscmp (pImageCodecInfo [j].MimeType, format) == 0) 
      { 
         *pClsid = pImageCodecInfo [j].Clsid; 
         free (pImageCodecInfo); 
         return j;//Success 
      }    
   }

   free (pImageCodecInfo); 
   return-1;//Failure 
}

// Old way to save file
int FileWriteSaveImage(BYTE*	memory, LONG dx, LONG dy, int fudgex)
{
	char *buffer = new char[50];
	sprintf(buffer, "capture%d%d.bmp", dx, dy);
	ofstream file(buffer, ios::binary);
	if (!file) return 1;

// initialize bitmap file headers

	BITMAPFILEHEADER fileHeader = {0};
	BITMAPINFOHEADER infoHeader = {0};

	fileHeader.bfType      = 0x4d42;
	fileHeader.bfSize      = 0;
	fileHeader.bfOffBits   = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

	infoHeader.biSize          = sizeof(infoHeader);
	infoHeader.biWidth         = dx + fudgex;
	infoHeader.biHeight        = dy;
	infoHeader.biPlanes        = 1;
	infoHeader.biBitCount      = 24;
	infoHeader.biCompression   = BI_RGB;

	// save file headers
	file.write((char*)&fileHeader, sizeof(fileHeader));
	file.write((char*)&infoHeader, sizeof(infoHeader));

	// save 24-bit bitmap data
	int wbytes = (((24 * (dx) + 31) & (~31)) / 8);
	int tbytes = wbytes * dy;
	file.write((char*)(memory), tbytes);
	file.close();
	return 0;
}

const int maxstr = 200;
// const char wndtitle[] = "AOK";
RECT rect, wrect;
int fudgex, fudgey;
struct windata {
	static const string titles[]; // only static const integral data members can be initialized within a class
	static const string default;
	string choose;
	HWND hfnd; // Found destination window handler
};
const string windata::titles[] = {"learn", "dialog", "aok", "map" };
const string windata::default = "learn"; // windata.default = ... will produce syntax error : missing ';' before '.'

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lparam)
{
char title[maxstr + 1];
char *ch = title;

GetWindowRect(hwnd, &wrect); // Calculate window height
int winHeight = wrect.bottom-wrect.top;

if ( (WINDOW_MIN_HEIGHT && winHeight<WINDOW_MIN_HEIGHT) || 
	 (WINDOW_MAX_HEIGHT && winHeight>WINDOW_MAX_HEIGHT))
		return TRUE; // skip window

if ( GetWindowLong(hwnd, GWL_STYLE) & WINSTYLE 	!= TRUE )
		return TRUE;

	/*	GetWindowTextA - copies the text of the specified window's 
	title bar (if it has one) into a buffer. If the specified window is a
	control, the text of the control is copied. However, it
	can't retrieve the text of a control in another application. */
	
if (GetWindowTextA(hwnd, title, maxstr)) {
		for (char *ch = title; *ch; ++ch) // was ch++)
			*ch = (char)tolower(*ch);		
		 // strstr - finds the first occurrence of a substring within a string. The comparison is case-sensitive.
		 // c_str - converts the contents of a string as a C-style, null-terminated string.
		if (strstr(title, ((windata*)lparam)->choose.c_str())) {
			((windata*)lparam)->hfnd = hwnd;
			SetLastError(ERROR_NO_MATCH);
			cout << "Found matching window - " << title << endl;
			return FALSE;
		}		
	}
	return TRUE;
} // Press F5 to continue debug, not F10. Otherwise "error" occurs.

int main()
{
// HWND HCapture = GetForegroundWindow();
/* HWND HCapture = FindWindow(NULL, _T("Learning WInAPI")); // get window handle
*/
windata wd;
cout << "Partial window title: ";
getline(cin, wd.choose);
wd.choose.erase(wd.choose.find_last_not_of(" \n\r\t")+1); // trim string
if ( wd.choose == "" )  // select default window
	wd.choose = wd.default;


/* EnumWindows(in,in) - Enumerates all top-level windows on the
screen by passing the handle to each window, in turn, to 
callback function. EnumWindows continues until the last top-level
window is enumerated or the callback function returns FALSE. 
The EnumWindows function does not enumerate child windows.
*/

	if (EnumWindows(EnumWindowsProc, (LPARAM)&wd) || (GetLastError() != ERROR_NO_MATCH)) {
		cout << "Cannot find window\n";
		_getch();
		return 1;
	}

	HWND HCapture = wd.hfnd;

	if (!IsWindow(HCapture)) {
		cout << "Bad find! Cannot find window\n";_getch();return 2;
	}

#if CODE<85
	fudgey = (GetMenu(HCapture)?GetSystemMetrics(SM_CYMENU):0) 
					+ GetSystemMetrics(SM_CYSIZE) + GetSystemMetrics(SM_CYSIZEFRAME);
	fudgex = 0;
#endif

GetClientRect(HCapture, &rect);
LONG dx = rect.right - rect.left;
LONG dy = rect.bottom - rect.top;


if (CODE>82)	{
					/*
const int fudgey = (GetMenu(HCapture)?GetSystemMetrics(SM_CYMENU):0) 
					+ GetSystemMetrics(SM_CYSIZE) + GetSystemMetrics(SM_CYSIZEFRAME);
					*/
	// Height should be 26 not 22
					fudgey = (wrect.bottom - wrect.top) - dy +4 - 2 * GetSystemMetrics(SM_CYSIZEFRAME);
					fudgex = 0;
}

/*
int test =  GetSystemMetrics(SM_CYSIZEFRAME); // 4
int test2 =  GetSystemMetrics(SM_CYMENU); // 24
int test3 =  wrect.bottom - wrect.top; 
*/

// create BITMAPINFO structure
// used by CreateDIBSection
BITMAPINFO info = {0};

	info.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
	info.bmiHeader.biWidth         = dx + fudgex;
	info.bmiHeader.biHeight        = dy + fudgey;
	info.bmiHeader.biPlanes        = 1;
	info.bmiHeader.biBitCount      = 24;
	info.bmiHeader.biCompression   = BI_RGB;

HDC	HDevice = CreateCompatibleDC(NULL);
//HDC HDevice = GetDC(HCapture);
 
BYTE*	memory = 0;
 
HBITMAP	HBitmap = CreateDIBSection(HDevice, &info, DIB_RGB_COLORS, (void**)&memory, NULL, NULL);

	SelectObject(HDevice, HBitmap);
 
	//SendMessage(HCapture, WM_PRINTCLIENT, 0, 0);
	// SendMessage(HCapture, WM_PRINTCLIENT,(WPARAM) HDevice, 0);
	if (!PrintWindow(HCapture, HDevice, /*PW_CLIENTONLY*/0))
		return 2;	
	// SendMessage(HCapture, WM_PRINTCLIENT, 0, 0);
	ReleaseDC(HCapture, HDevice);

	 // INIT GDI
	ULONG_PTR gdiplusToken;
	GdiplusStartupInput gdiplusStartupInput;
	GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
	if (!gdiplusToken) return 3;

	// Gdip_GetRotatedDimensions:
	GpBitmap* pBitmap;
	int result = Gdiplus::DllExports::GdipCreateBitmapFromHBITMAP(HBitmap, 0, &pBitmap);
	// SelectObject(HDevice, obm); DeleteObject(hbm); DeleteDC(hdc)	
	unsigned int w; unsigned int h;
	//GpStatus WINGDIPAPI GdipGetImageWidth(GpImage *image, UINT *width)
	Gdiplus::DllExports::GdipGetImageWidth(pBitmap, &w);
	Gdiplus::DllExports::GdipGetImageHeight(pBitmap, &h);
	REAL TAngle = ROTATION_ANGLE*(M_PI/180);
	if ( !w && !h )
		return -1;
	REAL RWidth = ceil(abs(w*cos(TAngle))+abs(h*sin(TAngle)));
	REAL RHeight = ceil(abs(w*sin(TAngle))+abs(h*cos(TAngle)));

	// gdip_getrotatedtranslation:
	REAL xTranslation, yTranslation;
	REAL bound = (ROTATION_ANGLE >= 0) ? ROTATION_ANGLE%360 : 360-(-ROTATION_ANGLE%-360);
	if ((bound >= 0) && (bound <= 90))
		{ xTranslation = h*sin(TAngle); yTranslation = 0; }
	else if ((bound > 90) && (bound <= 180))
		{ xTranslation = (h*sin(TAngle))-(w*cos(TAngle)); yTranslation = -1*h*cos(TAngle); }
	else if ((bound > 180) && (bound <= 270))
		{ xTranslation = -(w*cos(TAngle)); yTranslation = -(h*cos(TAngle))-(w*sin(TAngle)); }
	else if ((bound > 270) && (bound <= 360))
		{ xTranslation = 0; yTranslation = -1*w*sin(TAngle); }
	
	// Gdip_GraphicsFromImage:
	// GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image, GpGraphics **graphics)
	GpGraphics * pG;
	result = Gdiplus::DllExports::GdipGetImageGraphicsContext(pBitmap, &pG);
	
	// GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode smoothingMode)
	// smooth = 4; See http://msdn.microsoft.com/en-us/library/windows/desktop/ms534173%28v=vs.85%29.aspx
	Gdiplus::SmoothingMode smooth = SmoothingModeHighQuality;
	result = Gdiplus::DllExports::GdipSetSmoothingMode(pG, smooth);
	
	// ; Best interpolation is 5 - it's like no antialias
	// Gdip_SetInterpolationMode(G, 7)
	// Gdip_SetInterpolationMode(pGraphics, InterpolationMode)
	// GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics, InterpolationMode interpolationMode)
	// Gdiplus::InterpolationMode interpolation = 7; // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms534141%28v=vs.85%29.aspx
	Gdiplus::InterpolationMode interpolation = InterpolationModeNearestNeighbor;
	result = Gdiplus::DllExports::GdipSetInterpolationMode(pG, interpolation);

	// Gdip_TranslateWorldTransform(pGraphics, x, y, MatrixOrder=0)
    // GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx, REAL dy, GpMatrixOrder order)
	/*
	enum MatrixOrder
	{
		MatrixOrderPrepend    = 0,
		MatrixOrderAppend     = 1
	};
	*/
	MatrixOrder MatrixOrder_ = MatrixOrderPrepend;
	result = Gdiplus::DllExports::GdipTranslateWorldTransform(pG, xTranslation, yTranslation, MatrixOrder_);

	// Gdip_RotateWorldTransform(G, Angle)
	// Gdip_RotateWorldTransform(pGraphics, Angle, MatrixOrder=0)
	// GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle, GpMatrixOrder order)
	MatrixOrder_ = MatrixOrderPrepend;
	result = Gdiplus::DllExports::GdipRotateWorldTransform(pG, ROTATION_ANGLE, MatrixOrder_);
	
	// GpStatus WINGDIPAPI GdipCreateImageAttributes(GpImageAttributes **imageattr)
	GpImageAttributes * ImgAttributes;
	// from 'Gdiplus::GpImageAttributes' to 'Gdiplus::GpImageAttributes **'
	result = Gdiplus::DllExports::GdipCreateImageAttributes(&ImgAttributes); // create an ImageAttribute object

	int sx(0), sy(0);
	// Gdip_DrawImage
	// http://msdn.microsoft.com/en-us/library/windows/desktop/ms536044%28v=vs.85%29.aspx
	//	GpStatus WINGDIPAPI GdipDrawImageRectRect(
	//	   GpGraphics *graphics, GpImage *image, REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight, 
	//	   GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback, VOID * callbackData)
	result = Gdiplus::DllExports::GdipDrawImageRectRect(pG,pBitmap,0,0,w,h,0,0,w,h,UnitPixel,ImgAttributes,0,0);  // Draw the original image onto the new bitmap
	result = Gdiplus::DllExports::GdipDisposeImageAttributes(ImgAttributes);
		
	CLSID pngClsid;	
	GetEncoderClsid(L"image/png", &pngClsid);
	// GpStatus WINGDIPAPI GdipCreateBitmapFromGraphics(INT width, INT height, GpGraphics* target, GpBitmap** bitmap)
	result = Gdiplus::DllExports::GdipCreateBitmapFromGraphics(w, h, pG, &pBitmap);
    result = Gdiplus::DllExports::GdipSaveImageToFile(pBitmap, L"justest.png", &pngClsid, NULL);  // last voluntary? GDIPCONST EncoderParameters* encoderParams
    DeleteObject(HBitmap);

	// FileWriteSaveImage(memory, dx, dy, fudgex);	 
	return 0;
}
I did this test:

Code: Select all

GpGraphics * pG;
result = Gdiplus::DllExports::GdipGetImageGraphicsContext(pBitmap, &pG);

CLSID pngClsid;	
GetEncoderClsid(L"image/png", &pngClsid);
result = Gdiplus::DllExports::GdipCreateBitmapFromGraphics(600, 600, pG, &pBitmap);
result = Gdiplus::DllExports::GdipSaveImageToFile(pBitmap, L"justest.png", &pngClsid, NULL);
And it fails to get the picture to the file. In the png just created, there are no pixels (I see black image only). Tested on several windows.
brandonhotkey
Posts: 113
Joined: 03 Nov 2013, 05:46

Re: How to save bitmap to file in C++?

21 May 2014, 03:32

This is my problem:

Code: Select all

GpGraphics * pG; // pBitmap already contains image/picture
result = Gdiplus::DllExports::GdipGetImageGraphicsContext(pBitmap, &pG);
CLSID pngClsid;	
GetEncoderClsid(L"image/png", &pngClsid);
GpBitmap* pBitmap2;
result = Gdiplus::DllExports::GdipCreateBitmapFromGraphics(60, 60, pG, &pBitmap2);
result = Gdiplus::DllExports::GdipSaveImageToFile(pBitmap2, L"justest.png", &pngClsid, NULL);  // last voluntary? GDIPCONST EncoderParameters* encoderParams
If I comment the line with GdipCreateBitmapFromGraphics, it will save the pucture.
If I use the command GdipCreateBitmapFromGraphics, the image is black, no picture there.
Why the GdipCreateBitmapFromGraphics command does not do what I expect to do?
brandonhotkey
Posts: 113
Joined: 03 Nov 2013, 05:46

Re: How to save bitmap to file in C++?

21 May 2014, 04:26

I have found some reading which can be useful. It compares differences between basic bitmap or graphics functions in GDI or GDI+:
http://www.jose.it-berater.org/smfforum ... pic=3661.0

Important information to me:
Well, GdipCreateBitmapFromGraphics creates a new, blank, usually solid black bitmap in memory which can be used with GDI+, not a clipped copy of the image.
So the command:

Code: Select all

result = Gdiplus::DllExports::GdipCreateBitmapFromGraphics(60, 60, pG, &pBitmap2);
creates new blank image). So I need to find out how to get the old Bitmap into new Bitmap or rather how to get graphics to the new Bitmap.
User avatar
joedf
Posts: 8965
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: How to save bitmap to file in C++?

18 Jun 2014, 00:09

Well, for me, for Bitmaps I used to use QDBMP (Quick n' Dirty BMP). It's really good :)
http://qdbmp.sourceforge.net/
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]

Return to “C/C++”

Who is online

Users browsing this forum: No registered users and 27 guests