MFC通过CImage绘制透明图层的png图片

参考:https://blog.csdn.net/u013472838/article/details/80519525

https://blog.csdn.net/zhongbin104/article/details/8730935

https://blog.csdn.net/u011711997/article/details/52551106/

一、Cimage类的介绍及使用

详细参考:https://blog.csdn.net/u013472838/article/details/80519525

为什么引入CImage类?

    CBitmap 类只能处理BMP格式的图片,非常受限。

    而CImage可以处理JPGE GIF BMP PNG多种格式图片,扩展了图片处理功能 且能与CBitmap 进行转换( 因为所载入的位图句柄都是HBITMAP,所以可相互转换),因此引入CImage类进行图像处理

CImage类介绍

     CImage是MFC和ATL共享的新类,它能从外部磁盘中调入一个JPEG、GIF、BMP和PNG格式的图像文件加以显示,而且这些文件格式可以相互转换。

     CImage是VC.NET中定义的一种MFC/ATL共享类,也是ATL的一种工具类,它提供增强型的(DDB和DIB)位图支持,可以装入、显示、转换和保存多种格式的图像文件,包括BMP、GIF、JPG、PNG、TIF等。CImage是一个独立的类,没有基类。(CImage类是基于GDI+的,从VC.NET起引进,VC 6.0中没有。)

      ATL(Active Template Library,活动模板库)是一套基于模板的 C++ 类,用以简化小而快的 COM 对象的编写。

为了在MFC程序中使用CImage类,必须包含ATL的图像头文件atlimage.h:(在VS08 SP1中不用包含)

#include <atlimage.h>

1、加载位图文件

// CImage可加载的图片文件有JPG,BMP,TIF.PNG等格式  而CBitmap只能加载BMP图片文件  
if(!PathFileExists(imgFilePath))   
    return NULL;  
  
CImage nImage;  
nImage.Load(imgFilePath);  
return nImage.Detach();  //返回HBITMAP    可用CBitmap 处理 也可用CImage处理

2、与CBitmap转换

CImage nImage;  
nImage.Load(imgFilePath);  
  
HBITMAP hBitmap=nImage.Detach(); // 获得位图句柄 用以转换 (注意后面就不能再用nImage了?)
  
  
// 转换方式一:  
CBitmap bmp;  
bmp.DeleteObject();  
bmp.Attach(hBitmap); //  转换为CBitmap对象  
  
  
// 转换方式二:  
   
CBitmap *pBitmap=CBitmap::FromHandle(nImage.m_hBitmap); 

3、获得CImage对象的cdc

CDC *pDC=CDC::FromHandle(nImage.GetDC());  
  
// Use pDC here  
  
nImage.ReleaseDC();  

4、显示位图

   思路: 将CImage对象 绘制在对应的DC中

   所使用的函数 BitBlt   StretchBlt  Draw等

   以Draw举例:

EXAMPLE 1:

CImage img;  
img.Load("1.jpg");  
  
if (!img.IsNull())  
{  
    img.Draw(pDC->m_hDC,CRect(0,0,100,100));  
}  

EXAMPLE 2: 画在另一个位图中

CImage img;  
img.Load(filePath);  
  
// 获得CImage对象的 CDC  
HDC hDC=img.GetDC();  
CDC *pDC=CDC::FromHandle(hDC);  
  
CBitmap bmp;// 只是创建了位图对象,但还没有将位图对象与位图资源联系起来  
bmp.CreateCompatibleBitmap(pDC,nWidth,nHeight); // 创建新的位图资源  
  
  
CDC memDC;  
memDC.CreateCompatibleDC(pDC);  
CBitmap *pOld=memDC.SelectObject(&bmp);  
  
// 将img图像绘制到bmp中  
  
::SetStretchBltMode(memDC.m_hDC,HALFTONE);  
::SetBrushOrgEx(memDC.m_hDC,0,0,NULL);  
img.StretchBlt(memDC.m_hDC,CRect(0,0,nWidth,nHeight)/*DestRect*/,CRect(0,0,nWidth,nHeight)/*SourceRect*/,SRCCOPY);  
  
HBITMAP hBitmap=(HBITMAP)memDC.SelectObject(pOld->m_hObject); // 获得新创建的位图资源句柄  
  
img.ReleaseDC();  

二、CImage绘制带alpha透明图层的png图片

参考:https://blog.csdn.net/zhongbin104/article/details/8730935 方法二

https://blog.csdn.net/u011711997/article/details/52551106/ 用MFC做漂亮界面之美化按钮(PNG透明贴图)

下面以绘制按钮透明PNG图片为例

第1步,我们先在对话框上放置两个按钮,一个是关闭按钮,另一个是最小化按钮,它们对应的ID分别是IDC_BUTTON_CLOSE和IDC_BUTTON_MIN,然后将我们的按钮设置为自绘制模式,方法如下:

选择按钮,右键属性,在属性列表中找到Owner Draw选项,将其设置为True

再为它们添加两个成员变量,具体如下:

CButton m_btnClose;
CButton m_btnMin;

第2步,我们新建一个类,继承自CButton,我们取名为CMyButton,为其添加3个成员变量,分别如下:

//按钮背景图像
CImage m_imgButton;
//按钮png路径,包括焦点,正常,按下3个状态
CString m_strImgPath;
//父窗口背景图片背景路径,透明png需要使用
CString m_strImgParentPath;

第3步,我们为CMyButton添加3个成员函数,分别如下:

//设置按钮背景图片路径,父窗口背景图片路径
void SetImagePath(CString strImgPath, CString strParentImgPath);
//初始化按钮,主要是调整按钮的位置,处理透明色
bool InitMyButton(int nX/*左上角X坐标*/, int nY/*左上角Y坐标*/,int nW/*图像宽*/, int nH/*图像高*/, bool bIsPng/*是否是PNG图片*/);
//自绘制函数
void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);

CMyButton的声明最终如下:

class CMyButton : public CButton
{
	DECLARE_DYNAMIC(CMyButton)
 
public:
	CMyButton();
 
	virtual ~CMyButton();
	
	//按钮背景图像
	CImage m_imgButton;
 
	//按钮png路径,包括焦点,正常,按下3个状态
	CString m_strImgPath;
 
	//父窗口背景图片背景路径,透明png需要使用
	CString m_strImgParentPath;
 
	//设置按钮背景图片路径,父窗口背景图片路径
	void SetImagePath(CString strImgPath, CString strParentImgPath);
 
	//初始化按钮,主要是调整按钮的位置,处理透明色
	bool InitMyButton(int nX/*左上角X坐标*/, int nY/*左上角Y坐标*/,int nW/*图像宽*/, int nH/*图像高*/, bool bIsPng/*是否是PNG图片*/);
 
	//自绘制函数
	void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
 
protected:
	DECLARE_MESSAGE_MAP()
};

第4步,我们实现SetImagePath函数,它的功能是为按钮背景图片和父窗口背景图片成员函数初始化,具体如下:

void CMyButton::SetImagePath(CString strImgPath, CString strParentImgPath)
{
    m_strImgPath = strImgPath;
    m_strImgParentPath = strParentImgPath;
}

第5步,我们实现InitMyButton函数,它的功能是调整按钮在对话框上的位置,其中的参数代表该按钮在父窗口的左上角X坐标,Y坐标,宽度,高度,最后一个参数是为PNG格式图片准备的,如果是PNG带透明色的图片,需要对它进行特殊处理,具体定义如下:

bool CMyButton::InitMyButton(int nX, int nY, int nW, int nH, bool bIsPng)
{
	HRESULT hr = 0;
	if (m_strImgPath.IsEmpty())
		return false;
	hr = m_imgButton.Load(m_strImgPath);
 
	if (FAILED(hr))
		return false;
 
	if (bIsPng)
	{
		if (m_imgButton.GetBPP() == 32)
		{
			int i = 0;
			int j = 0;
			for (i = 0; i < m_imgButton.GetWidth(); i++)
			{
				for (j = 0; j < m_imgButton.GetHeight(); j++)
				{
					byte * pbyte = (byte *)m_imgButton.GetPixelAddress(i, j);
					pbyte[0] = pbyte[0] * pbyte[3] / 255;
					pbyte[1] = pbyte[1] * pbyte[3] / 255;
					pbyte[2] = pbyte[2] * pbyte[3] / 255;
				}
			}
		}
	}
 
	MoveWindow(nX,nY,nW,nH);
 
	return true;
}

其中MoveWindow函数是用来调整按钮位置的函数,其中的参数分别代表其在父窗口的左上角X坐标,左上角Y坐标,宽度,高度。
第6步,我们实现DrawItem函数,它是美化Button的核心函数,当我们将Button设置为自绘制后,每次按钮需要刷新,重新绘制的时候,MFC框架会调用它的DrawItem函数,在这个函数中,我们可以根据按钮当前的状态为其贴上相应的背景图。当我们按钮按钮的时候,为其贴上被按下的背景图;当我们的按钮获取焦点的时候,为其贴上获取焦点的背景图;当我们的按钮没有焦点,我们为其贴上默认的背景图片,它们对应的位置前面已经说过。为了避免闪烁,我们采用双缓冲的方式,具体代码如下:

void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	if (!lpDrawItemStruct)
		return;
	HDC hMemDC;
	HBITMAP bmpMem;
	HGDIOBJ hOldObj;
	bmpMem = CreateCompatibleBitmap(lpDrawItemStruct->hDC, lpDrawItemStruct->rcItem.right - lpDrawItemStruct->rcItem.left, lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top);
	if (!bmpMem)
		return;
	hMemDC = CreateCompatibleDC(lpDrawItemStruct->hDC);
	if (!hMemDC)
	{
		if (bmpMem)
		{
			::DeleteObject(bmpMem);
			bmpMem = NULL;
		}
		return;
	}
 
	hOldObj = ::SelectObject(hMemDC, bmpMem);
 
	RECT rectTmp = { 0 };
 
	rectTmp = lpDrawItemStruct->rcItem;
 
	MapWindowPoints(GetParent(), &rectTmp);
 
	int nW = lpDrawItemStruct->rcItem.right - lpDrawItemStruct->rcItem.left;
 
	int nH = lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top;
 
	if (lpDrawItemStruct->itemState & ODS_SELECTED)
	{
		//按钮被选择
		m_imgButton.BitBlt(hMemDC, 0, 0, nW, nH, nW*2, 0, SRCCOPY);
	}
	else if (lpDrawItemStruct->itemState & ODS_FOCUS)
	{
               //焦点状态
              m_imgButton.BitBlt(hMemDC, 0, 0, nW, nH, nW, 0, SRCCOPY);		
	}
	else
	{
		//默认状态
		CImage imgParent;
 
		imgParent.Load(m_strImgParentPath);
 
		imgParent.Draw(hMemDC, 0, 0, nW, nH, rectTmp.left, rectTmp.top, nW, nH);
 
		m_imgButton.AlphaBlend(hMemDC, 0, 0, nW, nH, 0, 0, nW, nH);
 
		imgParent.Destroy();
		
	}
 
	::BitBlt(lpDrawItemStruct->hDC, 0, 0, nW, nH, hMemDC, 0, 0, SRCCOPY);
 
	SelectObject(hMemDC, hOldObj);
 
	if (bmpMem)
	{
		::DeleteObject(bmpMem);
		bmpMem = NULL;
	}
 
	if (hMemDC)
	{
		::DeleteDC(hMemDC);
		hMemDC = NULL;
	}
	return;
}

这里我们重点说一下默认状态的背景图,因为它是透明的,并且我们采用的是双缓冲,所以,为了避免最终透明色变成黑色,我们先在内存DC上贴上按钮在父窗口位置的背景图,这样可以解决透明色变成黑色的问题,如果你采用GDI+,就不用这么做,但是我们采用的是GDI。
第7步,用CMyButton替代对话框头文件中的CButton。

第8步,在对话框的InitDialog中,对两个按钮进行初始化,具体如下:

m_btnMin.SetImagePath(_T("./res/btn_min.png"), _T("./res/Background.png"));
m_btnMin.InitMyButton(516, 8, 27, 21, true);
m_btnClose.SetImagePath(_T("./res/btn_close.png"),_T("./res/Background.png"));
m_btnClose.InitMyButton(545,8,27,21,true);

三、GDI+画透明图层(alpha)的png图片

参考 https://blog.csdn.net/zhongbin104/article/details/8730935 方法一

 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将 BMP 像转换为 PNG 格式并添加透明度,您可以使用 C++ 的第三方库,例如 libpng 和 zlib。以下是一个简单的示例代码,它使用这些库来执行所需的 BMP 到 PNG 转换和透明度添加: ``` #include <iostream> #include <fstream> #include <cstring> #include <png.h> #include "CImage.h" void write_png_file(char* file_name, int width, int height, png_bytep* row_pointers) { FILE* fp = fopen(file_name, "wb"); if (!fp) { std::cerr << "Error: Failed to open PNG file for writing\n"; return; } png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (!png_ptr) { std::cerr << "Error: Failed to create PNG write structure\n"; fclose(fp); return; } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { std::cerr << "Error: Failed to create PNG info structure\n"; png_destroy_write_struct(&png_ptr, nullptr); fclose(fp); return; } if (setjmp(png_jmpbuf(png_ptr))) { std::cerr << "Error: Failed to initialize PNG write\n"; png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); return; } png_init_io(png_ptr, fp); if (setjmp(png_jmpbuf(png_ptr))) { std::cerr << "Error: Failed to write PNG image header\n"; png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); return; } png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); if (setjmp(png_jmpbuf(png_ptr))) { std::cerr << "Error: Failed to write PNG image data\n"; png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); return; } png_write_image(png_ptr, row_pointers); if (setjmp(png_jmpbuf(png_ptr))) { std::cerr << "Error: Failed to complete PNG write\n"; png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); return; } png_write_end(png_ptr, nullptr); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); } void bmp_to_png_with_alpha(const char* bmp_file, const char* png_file) { CImage img; if (!img.Load(bmp_file)) { std::cerr << "Error: Failed to load BMP file\n"; return; } int width = img.GetWidth(); int height = img.GetHeight(); png_bytep* row_pointers = new png_bytep[height]; for (int y = 0; y < height; ++y) { row_pointers[y] = new png_byte[4 * width]; for (int x = 0; x < width; ++x) { COLORREF color = img.GetPixel(x, y); png_byte red = GetRValue(color); png_byte green = GetGValue(color); png_byte blue = GetBValue(color); png_byte alpha = (color != RGB(255, 0, 255)) ? 0xFF : 0x00; png_bytep pixel = &(row_pointers[y][4 * x]); pixel[0] = red; pixel[1] = green; pixel[2] = blue; pixel[3] = alpha; } } write_png_file(png_file, width, height, row_pointers); for (int y = 0; y < height; ++y) { delete[] row_pointers[y]; } delete[] row_pointers; } int main() { const char* bmp_file = "test.bmp"; const char* png_file = "test.png"; bmp_to_png_with_alpha(bmp_file, png_file); return 0; } ``` 在这个示例中,我们首先使用 CImage 类加载 BMP 像文件。然后,我们遍历每个像素,并将其值转换为 PNG 格式,同时添加透明度(即,如果像素值为 RGB(255, 0, 255),则将其 alpha 值设置为 0,否则将其 alpha 值设置为 255)。最后,我们使用 write_png_file() 函数将转换后的 PNG 像写入磁盘。 请注意,该示例仅提供了一个基本的转换功能。如果您需要处理大量像或需要更高级的功能(例如,调整大小、旋转或滤镜等),则可能需要使用更强大的像处理库,例如 OpenCV 或 ImageMagick。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值