VC++:位图图像转灰度数组

GdiUtils.h

/* ----------------------------------------------------------
* 文件名称:GdiUtils.h
*
* 作者:秦建辉
*
* 微信:splashcn
*
* 博客:http://www.firstsolver.com/wordpress/
*
* 开发环境:
*      Visual Studio V2017
*
* 版本历史:
*   V1.0    2018年12月08日
*           Gdi扩展函数
* ---------------------------------------------------------- */
#pragma once

#include <Windows.h>
#include <gdiplus.h>

#pragma comment(lib, "Gdiplus.lib")

using namespace Gdiplus;

#ifdef __cplusplus
extern "C"
{
#endif	

	/*
	功能:获取位图图像Bgra数组
	参数说明:
	hbmp:图像句柄
	bgra:输出,存储图像Bgra数组
	width:输出,存储图像宽度
	height:输出,存储图像高度
	返回值:错误编码
	*/
	HRESULT WINAPI ToBgra(IN HBITMAP hBmp, OUT BYTE* &bgra, OUT INT& width, OUT INT& height);

	/*
	功能:获取位图图像灰度数组
	参数说明:
	hbmp:图像句柄
	gray:输出,存储图像灰度数组
	width:输出,存储图像宽度
	height:输出,存储图像高度
	返回值:错误编码
	*/
	HRESULT WINAPI ToGray(IN HBITMAP hBmp, OUT BYTE* &gray, OUT INT& width, OUT INT& height);

	/*
	功能:从文件生成HBITMAP
	参数说明:
	filename:图像文件路径
	返回值:位图图像句柄
	*/
	HBITMAP WINAPI ToHBitmapFromFile(IN const TCHAR * filename);

	/*
	功能:从文件获取位图图像Bgra数组
	参数说明:
	filename:图像文件路径
	bgra:输出,存储图像Bgra数组
	width:输出,存储图像宽度
	height:输出,存储图像高度
	返回值:错误编码
	*/
	HRESULT WINAPI ToBgraFromFile(IN const TCHAR * filename, OUT BYTE* &bgra, OUT INT& width, OUT INT& height);

	/*
	功能:获取位图图像灰度数组
	参数说明:
	filename:图像文件路径
	gray:输出,存储图像灰度数组
	width:输出,存储图像宽度
	height:输出,存储图像高度
	返回值:错误编码
	*/
	HRESULT WINAPI ToGrayFromFile(IN const TCHAR * filename, OUT BYTE* &gray, OUT INT& width, OUT INT& height);

	/*
	功能:启动Gdiplus
	返回值:状态码
	*/
	Gdiplus::Status WINAPI StartupGdiplus();

	/*
	功能:关闭Gdiplus
	*/
	VOID WINAPI ShutdownGdiplus();

	/*
	功能:基于Gdiplus从内存缓冲区生成HBITMAP
	参数说明:
	buffer:图像内存缓冲区
	size:图像字节数
	返回值:位图图像句柄
	说明:需要预先启动Gdiplus
	*/
	HBITMAP WINAPI ToHBitmapFromBuffer(const BYTE* buffer, int size);

	/*
	功能:获取位图图像Bgra数组
	参数说明:
	buffer:图像内存缓冲区
	size:图像字节数
	bgra:输出,存储图像Bgra数组
	width:输出,存储图像宽度
	height:输出,存储图像高度
	返回值:错误编码
	说明:需要预先启动Gdiplus
	*/
	HRESULT WINAPI ToBgraFromBuffer(const BYTE* buffer, int size, OUT BYTE* &bgra, OUT INT& width, OUT INT& height);

	/*
	功能:获取位图图像灰度数组
	参数说明:
	buffer:图像内存缓冲区
	size:图像字节数
	gray:输出,存储图像灰度数组
	width:输出,存储图像宽度
	height:输出,存储图像高度
	返回值:错误编码
	说明:需要预先启动Gdiplus
	*/
	HRESULT WINAPI ToGrayFromBuffer(const BYTE* buffer, int size, OUT BYTE* &gray, OUT INT& width, OUT INT& height);

#ifdef __cplusplus
}
#endif

GdiUtils.cpp

#include "GdiUtils.h"
#include <new>

ULONG_PTR mToken = NULL;

// 获取位图图像Bgra数组
HRESULT WINAPI ToBgra(IN HBITMAP hBmp, OUT BYTE* &bgra, OUT INT& width, OUT INT& height)
{
	BITMAP bm;
	if (::GetObject(hBmp, sizeof(BITMAP), &bm) == 0) return E_INVALIDARG;

	width = bm.bmWidth;		// 图像像素宽度
	height = bm.bmHeight;	// 图像像素高度
	int biSizeImage = (bm.bmWidth * bm.bmHeight) << 2;

	BITMAPINFO bmi;
	bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bmi.bmiHeader.biWidth = bm.bmWidth;
	bmi.bmiHeader.biHeight = -bm.bmHeight; // 负数表示图像左上角为起始点
	bmi.bmiHeader.biPlanes = 1;
	bmi.bmiHeader.biBitCount = 32;
	bmi.bmiHeader.biCompression = BI_RGB; // 不压缩
	bmi.bmiHeader.biSizeImage = 0; // 对于BI_RGB,可以设置为0
	bmi.bmiHeader.biClrUsed = 0;
	bmi.bmiHeader.biClrImportant = 0;

	bgra = new (std::nothrow) BYTE[biSizeImage];
	if (bgra == NULL) return E_OUTOFMEMORY;

	HRESULT hr = S_OK;
	HDC hDC = ::GetDC(NULL);
	if (hDC == NULL)
	{
		hr = E_FAIL;
	}
	else
	{
		if (::GetDIBits(hDC, hBmp, 0, bm.bmHeight, bgra, &bmi, DIB_RGB_COLORS) == 0)
		{
			hr = E_INVALIDARG;
		}

		::ReleaseDC(NULL, hDC);
	}

	if (FAILED(hr)) { delete[] bgra; bgra = NULL; }
	return hr;
}

// 获取位图图像灰度数组
HRESULT WINAPI ToGray(IN HBITMAP hBmp, OUT BYTE* &gray, OUT INT& width, OUT INT& height)
{
	BITMAP bm;
	if (::GetObject(hBmp, sizeof(BITMAP), &bm) == 0) return E_INVALIDARG;

	width = bm.bmWidth;	// 图像像素宽度
	height = bm.bmHeight;	// 图像像素高度
	int biPixels = bm.bmWidth * bm.bmHeight;
	int biSizeImage = biPixels << 2;

	BITMAPINFO bmi;
	bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bmi.bmiHeader.biWidth = bm.bmWidth;
	bmi.bmiHeader.biHeight = -bm.bmHeight; // 负数表示图像左上角为起始点
	bmi.bmiHeader.biPlanes = 1;
	bmi.bmiHeader.biBitCount = 32;
	bmi.bmiHeader.biCompression = BI_RGB;
	bmi.bmiHeader.biSizeImage = 0; // 对于BI_RGB,可以设置为0
	bmi.bmiHeader.biClrUsed = 0;
	bmi.bmiHeader.biClrImportant = 0;

	BYTE* bgra = new (std::nothrow) BYTE[biSizeImage];
	if (bgra == NULL) return E_OUTOFMEMORY;

	HRESULT hr = S_OK;
	HDC hDC = ::GetDC(NULL);
	if (hDC == NULL)
	{
		hr = E_FAIL;
	}
	else
	{
		if (::GetDIBits(hDC, hBmp, 0, bm.bmHeight, bgra, &bmi, DIB_RGB_COLORS) == 0)
		{
			hr = E_INVALIDARG;
		}
		else
		{
			gray = new (std::nothrow) BYTE[biPixels];
			if (gray == NULL)
			{
				hr = E_OUTOFMEMORY;
			}
			else
			{
				BYTE* pSrc = bgra;
				BYTE* pDest = gray;
				for (int i = 0; i < biPixels; i++)
				{
					*pDest++ = ((*pSrc++) * 7471 + (*pSrc++) * 38469 + (*pSrc++) * 19595 + 32768) >> 16;
					pSrc++;
				}
			}
		}

		::ReleaseDC(NULL, hDC);
	}

	delete[] bgra;
	return hr;
}

// 从文件生成HBITMAP
HBITMAP WINAPI ToHBitmapFromFile(IN const TCHAR * filename)
{
	return (HBITMAP)::LoadImage(NULL, filename, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
}

// 获取位图图像Bgra数组
HRESULT WINAPI ToBgraFromFile(IN const TCHAR * filename, OUT BYTE* &bgra, OUT INT& width, OUT INT& height)
{
	HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, filename, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
	if (hBmp == NULL)
	{
		return HRESULT_FROM_WIN32(GetLastError());
	}
	else
	{
		HRESULT hr = ToBgra(hBmp, bgra, width, height);
		DeleteObject(hBmp);
		return hr;
	}
}

// 获取位图图像灰度数组
HRESULT WINAPI ToGrayFromFile(IN const TCHAR * filename, OUT BYTE* &gray, OUT INT& width, OUT INT& height)
{
	HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, filename, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
	if (hBmp == NULL)
	{
		return HRESULT_FROM_WIN32(GetLastError());
	}
	else
	{
		HRESULT hr = ToGray(hBmp, gray, width, height);
		DeleteObject(hBmp);
		return hr;
	}
}

// 启动Gdiplus
Gdiplus::Status WINAPI StartupGdiplus()
{
	if (mToken == NULL)
	{
		GdiplusStartupInput input;
		return GdiplusStartup(&mToken, &input, NULL);
	}
	else
	{
		return Status::Ok;
	}
}

// 关闭Gdiplus
VOID WINAPI ShutdownGdiplus()
{
	if (mToken != NULL)
	{
		GdiplusShutdown(mToken);
		mToken = 0;
	}
}

// 从内存缓冲区生成HBITMAP
HBITMAP WINAPI ToHBitmapFromBuffer(const BYTE* buffer, int size)
{
	HBITMAP hBmp = NULL;
	HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, size);
	if (hMem != NULL)
	{
		LPVOID pMem = ::GlobalLock(hMem);
		if (pMem != NULL)
		{
			CopyMemory(pMem, buffer, size);

			IStream *pStream = NULL;
			if (SUCCEEDED(CreateStreamOnHGlobal(hMem, FALSE, &pStream)))
			{
				Bitmap* pBmp = Bitmap::FromStream(pStream, FALSE);
				if (pBmp != NULL)
				{
					Gdiplus::Color bg(0, 255, 255, 255);
					pBmp->GetHBITMAP(bg, &hBmp);
					delete pBmp;
				}

				pStream->Release();
			}

			::GlobalUnlock(hMem);
		}
		::GlobalFree(hMem);
	}

	return hBmp;
}

// 获取位图图像Bgra数组
HRESULT WINAPI ToBgraFromBuffer(const BYTE* buffer, int size, OUT BYTE* &bgra, OUT INT& width, OUT INT& height)
{
	HBITMAP hBmp = ToHBitmapFromBuffer(buffer, size);
	if (hBmp == NULL)
	{
		return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
	}
	else
	{
		HRESULT hr = ToBgra(hBmp, bgra, width, height);
		DeleteObject(hBmp);
		return hr;
	}
}

// 获取位图图像灰度数组
HRESULT WINAPI ToGrayFromBuffer(const BYTE* buffer, int size, OUT BYTE* &gray, OUT INT& width, OUT INT& height)
{
	HBITMAP hBmp = ToHBitmapFromBuffer(buffer, size);
	if (hBmp == NULL)
	{
		return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
	}
	else
	{
		HRESULT hr = ToGray(hBmp, gray, width, height);
		DeleteObject(hBmp);
		return hr;
	}
}

Comments are closed.