API

비트맵을 파일(png, jpg, bmp)로 저장하기 / 파일을 비트맵으로 생성하기

디버그정 2022. 8. 21. 00:46
// GDI+ library를 사용해 bmp, png, jpg, gif 등 대부분의 이미지 형식 모두 처리 가능하다.
#include <GdiPlus.h>
#pragma comment(lib, "gdiplus")
using namespace Gdiplus;



int __stdcall GetEncoderClsid(const WCHAR *mime, CLSID *clsid)
{
	UINT i, num, size;
	ImageCodecInfo *info;

	GetImageEncodersSize(&num, &size);
	if (!size)
		return -1;

	if (!(info = (ImageCodecInfo*)malloc(size)))
		return -1;

	GetImageEncoders(num, size, info);

	for (i = 0; i < num; ++i)
	{
		if (0 == lstrcmpiW(info[i].MimeType, mime))
		{
			*clsid = info[i].Clsid;
			free(info);
			return i;	// success
		}
	}

	free(info);
	return -1;
}

#if defined(UNICODE) || defined(_UNICODE)
#define	_tcsistr wcsistr
#else
#define	_tcsistr stristr
#endif
char * __stdcall stristr(const char *src, const char *fnd)
{
	const char *p, *q;
	char a, b;
	for (; ; ++src)
	{
		for (p = src, q = fnd; *q; ++p, ++q)	// 빠른 실행 위해 *p 조건은 넣지 않는다. 안의 문자 불일치 조건으로 처리된다.
		{
			if (*p <= 'Z' && *p >= 'A') {a = *p + 0x20;} else {a = *p;}	// 'A' 이상인 경우가 훨씬 많으므로 'Z' 이하 조건을 앞에 두는게 효율적이다.
			if (*q <= 'Z' && *q >= 'A') {b = *q + 0x20;} else {b = *q;}
			if (a != b)
				break;
		}
		if (!*q)
			return (char *)src;
		if (!*src)
			break;
	}
	return NULL;
}
wchar_t * __stdcall wcsistr(const wchar_t *src, const wchar_t *fnd)
{
	const wchar_t *p, *q;
	wchar_t a, b;
	for (; ; ++src)
	{
		for (p = src, q = fnd; *q; ++p, ++q)
		{
			if (*p <= 'Z' && *p >= 'A') {a = *p + 0x20;} else {a = *p;}
			if (*q <= 'Z' && *q >= 'A') {b = *q + 0x20;} else {b = *q;}
			if (a != b)
				break;
		}
		if (!*q)
			return (wchar_t *)src;
		if (!*src)
			break;
	}
	return NULL;
}

int __stdcall BitmapToFile(HBITMAP hBmp, const WCHAR *path = NULL, const WCHAR *mime = NULL)
{
	int i, len, ret;
	BOOL bExtensionFound;
	SYSTEMTIME st;
	ULONG_PTR gdiplusToken;
	GdiplusStartupInput gdiplusStartupInput;
	CLSID clsid;
	WCHAR *p, *pExt, wszPath[256], wszMime[256];

	if (!path)	// 경로를 전달하지 않은 경우 프로그램 디렉토리에 "Image_날짜_시간" 파일을 생성한다. 
	{
		GetModuleFileNameW(NULL, wszPath, 255);
		if (p = wcsrchr(wszPath, L'\\'))
		{
			if (!mime)	// 이미지 형식을 지정하지 않은 경우 디폴트로 png 생성
				pExt = L"png";
			else if (pExt = wcsistr(mime, L"image/"))	// 이미지 형식을 표현 규격에 맞춰 전달한 경우 확장자 부분만 구한다.
				pExt += 6/*lstrlenW(L"image/")*/;	// "image/" 문자열 부분을 건너뛴다.
			else	// bmp, jpeg, png 등 간단히 전달한 경우
				pExt = (WCHAR *)mime;

			GetLocalTime(&st);
			wsprintfW(++p, L"Image_%04u-%02u-%02u_%02u.%02u.%02u.%03u.%s", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, pExt);
			path = wszPath;
		}
		else
		{
			MessageBoxW(NULL, L"Invalid file path", L"BitmapToFile", MB_ICONERROR);
			return 0;
		}
	}

	if (mime)	// 이미지 형식을 지정한 경우
	{
		if (wcsistr(mime, L"jpg"))	// jpg인 경우 정식 표현 규격인 jpeg로 수정
		{
			mime = L"image/jpeg";
		}
		else if (!wcsistr(mime, L"image/"))	// bmp, jpeg, png 등 간단히 전달한 경우 표현 규격에 맞춘다.
		{
			wsprintfW(wszMime, L"image/%s", mime);
			mime = wszMime;
		}
	}
	else	// 이미지 형식을 지정하지 않은 경우 파일 확장자를 구해 표현 규격에 맞춘다.
	{
		bExtensionFound = FALSE;
		len = lstrlenW(path);
		if (len >= 3)	// 파일명.확장자 형식이므로 길이가 최소 3 이상이어야 한다.
		{
			p = (WCHAR *)path + len - 2;	// 확장자는 . 뒤에 문자열 형식이므로 논리상 -2해도 된다.
			i = 32;	// 반복횟수 제한. 정상적인 확장자라면 아무리 길이도 32자 이내일 것이다.
			do		// 논리적으로 첫 조건은 항상 참이므로 for문 대신 do while문으로 구성
			{
				if (*p == L'.')
				{
					bExtensionFound = TRUE;
					++p;
					if (0 == lstrcmpiW(p, L"jpg"))	// jpg인 경우 정식 표현 규격인 jpeg로 수정
					{
						mime = L"image/jpeg";
					}
					else
					{	wsprintfW(wszMime, L"image/%s", p);	// 표현 규격
						mime = wszMime;
					}
					break;
				}
			}
			while (--p > path && --i);	// 시작위치보다 뒤 && 지정한 확장자 길이 제한내 
		}
		if (!bExtensionFound)
		{
			MessageBoxW(NULL, L"File extension not found", L"BitmapToFile", MB_ICONERROR);
			return 0;
		}
	}

	ret = 0;	// 리턴값 초기화
	if (Ok == GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL))
	{
		if (GetEncoderClsid(mime, &clsid) >= 0)
		{
			//Gdiplus::Bitmap gBmp(hBmp, NULL);	// 스택에 지역변수로 생성. 아래 해제 부분에서 delete문을 주석처리한다.
			Gdiplus::Bitmap *pBmp = Gdiplus::Bitmap::FromHBITMAP(hBmp, NULL);	// new 연산자로 객체 생성
			if (pBmp)
			{
				if (Ok == pBmp->Save(path, &clsid))
				{
					ret = 1;
				}
				else
				{
					MessageBoxW(NULL, L"Failed to save image file", L"BitmapToFile", MB_ICONERROR);
				}

				delete pBmp;
			}
			else
			{
				MessageBoxW(NULL, L"Failed to create Gdiplus::Bitmap object", L"BitmapToFile", MB_ICONERROR);
			}
		}
		else
		{
			MessageBoxW(NULL, L"Failed to find the mime encoder", L"BitmapToFile", MB_ICONERROR);
		}

		GdiplusShutdown(gdiplusToken);
	}
	else
	{
		MessageBoxW(NULL, L"Falied to initalize GDI+ library", L"BitmapToFile", MB_ICONERROR);
	}

	return ret;
}

HBITMAP __stdcall FileToBitmap(const WCHAR *path)
{
	HBITMAP hBmp;
	ULONG_PTR gdiplusToken;
	GdiplusStartupInput gdiplusStartupInput;

	hBmp = NULL;	// 리턴값 초기화
	if (Ok == GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL))
	{
		Gdiplus::Bitmap *pBmp = Gdiplus::Bitmap::FromFile(path);
		if (pBmp)
		{
			if (Ok == pBmp->GetHBITMAP(Color(0, 0, 0), &hBmp))
			{
			}
			else
			{
				MessageBoxW(NULL, L"Failed to Get HBITMAP", L"FileToBitmap", MB_ICONERROR);
			}

			delete pBmp;
		}
		else
		{
			MessageBoxW(NULL, L"Failed to create Gdiplus::Bitmap object", L"FileToBitmap", MB_ICONERROR);
		}

		GdiplusShutdown(gdiplusToken);
	}
	else
	{
		MessageBoxW(NULL, L"Falied to initalize GDI+ library", L"FileToBitmap", MB_ICONERROR);
	}

	return hBmp;
}