(转)CImage显示透明背景的PNG图片

PNG透明背景显示之路

   在VC7.1中 MFC图形处理类里有一个强大的成员---CImage,这个类提供了从外部磁盘中调入一个JPEG、GIF、BMP和PNG格式的图像文件加以显示,而且这些文件格式可以相互转换。由于CImage在不同的Windows操作系统中其某些性能是不一样的,因此在使用时要特别注意。例如,CImage::PlgBlt和CImage::MaskBlt只能在 Windows NT 4.0 或更高版本中使用,但不能运行在Windows 95/98 应用程序中。CImage::AlphaBlend和CImage::TransparentBlt也只能在 Windows 2000/98或其更高版本中使用。即使在Windows 2000运行程序还必须将stdafx.h文件中的WINVER和_WIN32_WINNT的预定义修改成0x0500才能正常使用

使用CImage的一般方法

  使用CImage的一般方法是这样的过程:

  (1) 打开应用程序的stdafx.h文件添加CImage类的包含文件:

#include <atlimage.h>

  (2) 定义一个CImage类对象,然后调用CImage::Load方法装载一个外部图像文件。

  (3) 调用CImage::Draw方法绘制图像。Draw方法具有如下定义:

BOOL Draw( HDC hDestDC, int xDest, int yDest,
int nDestWidth, int nDestHeight, int xSrc, int ySrc,
int nSrcWidth, int nSrcHeight );
BOOL Draw( HDC hDestDC, const RECT& rectDest, const RECT& rectSrc );
BOOL Draw( HDC hDestDC, int xDest, int yDest );
BOOL Draw( HDC hDestDC, const POINT& pointDest );
BOOL Draw( HDC hDestDC, int xDest, int yDest,
int nDestWidth, int nDestHeight );
BOOL Draw( HDC hDestDC, const RECT& rectDest );

  其中,hDestDC用来指定绘制的目标设备环境句柄,(xDest, yDest)和pointDest用来指定图像显示的位置,这个位置和源图像的左上角点相对应。nDestWidth和nDestHeight分别指定图像要显示的高度和宽度,xSrc、ySrc、nSrcWidth和nSrcHeight用来指定要显示的源图像的某个部分所在的位置和大小。rectDest和rectSrc分别用来指定目标设备环境上和源图像所要显示的某个部分的位置和大小。

  需要说明的是,Draw方法综合了StretchBlt、TransparentBlt和AlphaBlend函数的功能。默认时,Draw的功能和StretchBlt相同。但当图像含有透明色或Alpha通道时,它的功能又和TransparentBlt、AlphaBlend相同。因此,在一般情况下,我们都应该尽量调用CImage::Draw方法来绘制图像。

现在简单介绍一下CImage图像的提取和显示,这个在很多的网站上都有讲述,随便都能找到的东西,我这里就不再详述,随便找了一个代码

(1) 创建一个默认的单文档程序项目Ex_Image。

  (2) 打开stdafx.h文件中添加CImage类的包含文件atlimage.h。

  (3) 在CEx_ImageView类添加ID_FILE_OPEN的COMMAND事件映射程序,并添加下列代码:

void CEx_ImageView::OnFileOpen()
{
 CString strFilter;
 CSimpleArray<GUID> aguidFileTypes;
 HRESULT hResult;

 // 获取CImage支持的图像文件的过滤字符串
 hResult = m_Image.GetExporterFilterString(strFilter,aguidFileTypes,
_T( "All Image Files") );
 if (FAILED(hResult)) {
  MessageBox("GetExporterFilter调用失败!");
  return;
 }
 CFileDialog dlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST, strFilter);
 if(IDOK != dlg.DoModal()) 
  return;

 m_Image.Destroy();
 // 将外部图像文件装载到CImage对象中
 hResult = m_Image.Load(dlg.GetFileName());
 if (FAILED(hResult)) {
  MessageBox("调用图像文件失败!");
  return;
 }

 // 设置主窗口标题栏内容
 CString str;
 str.LoadString(AFX_IDS_APP_TITLE);
 AfxGetMainWnd()->SetWindowText(str + " - " +dlg.GetFileName());

 Invalidate(); // 强制调用OnDraw
}

  (4) 定位到CEx_ImageView::OnDraw函数处,添加下列代码:

void CEx_ImageView::OnDraw(CDC* pDC)
{
 CEx_ImageDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 if (!m_Image.IsNull()) {
  m_Image.Draw(pDC->m_hDC,0,0);
 }
}

  (5) 打开Ex_ImageView.h文件,添加一个公共的成员数据m_Image:

public:
CImage m_Image;

  (6) 编译并运行。单击"打开"工具按钮,在弹出的对话框中指定一个图像文件后,单击"打开"按钮,其结果如图7.21所示。

 

如果这个时候加载透明图层,一般情况是不会显示透明背景的(我想能看到这里的朋友都是为透明所困的人吧),我也是卡在这里很长时间,查阅了大量的资料和网上跑了很多网站,都没有得到满意的答复,以下有些网站资料可以参考:

供CImage类显示的半透明PNG文件处理方法

在高兴之余,也很不理解

使用CImage类如何显示背景是透明的png格式图片

在网上搜索的结果让人感到很失望,我也开始思考是不是要用其他的方法来处理图片的透明,因为我原来写游戏都一直使用SDK的,在9.0的SDK中绘制透明PNG格式是支持透明的,为什么在2003的.net里他就不支持了呢?我觉得这个问题并不是CImage本身的处理能力问题,而是有的地方我们并设置好。先做一个实验:我调入一张带有透明背景图片,然后通过CImage保存成新的一张PNG,用PHOTOSHOP打开发现背景依然是透明的,这证明我的猜想是正确的,但是为什么就是不显示透明呢?我开始反复查阅MSDN,试图找到问题的根本原因。

    在MSDN种介绍CImage::Draw 的段落并不多,如果在编译应用程序时将 _WIN32_WINNT 的值设置为等于或大于 0x0500,则 Draw 将在运行 Windows 2000 和 Windows 98 以及更高版本的系统上自动处理透明。它在 Windows NT 4.0 和 Windows 95 上同样有效,但没有透明支持。我们现用的系统是XP,按照MSDN说的,应该是自动处理透明,为什么我们就不自动呢?其中一句话引起了我的注意  如果编译应用程序时将 _WIN32_WINNT 的值设置为小于 0x0500,则 Draw 有效,但它在运行 Windows 2000 和 Windows 98 以及更高版本的系统上将不自动处理透明  这句话在本文开篇的时候我已经用蓝色标注出来了,这行字是Draw是否自动处理透明的关键,但是我早就把_WIN32_WINNT改成0x0500了,为什么还不行呢?我又重新查看了stdafx.h中需要修改的部分:

WINVER         // 允许使用 Windows 95 和 Windows NT 4 或更高版本的特定功能。
_WIN32_WINNT   // 允许使用 Windows NT 4 或更高版本的特定功能。
_WIN32_WINDOWS // 允许使用 Windows 98 或更高版本的特定功能。

这里的3个版本相关的内容,但是MSDN上只让我们修改了一出处,是不是问题出在这里呢?修改一下就知道了。

#ifndef WINVER    // 允许使用 Windows 95 和 Windows NT 4 或更高版本的特定功能。
#define WINVER 0x0500  //为 Windows98 和 Windows 2000 及更新版本改变为适当的值。
#endif

#ifndef _WIN32_WINNT  // 允许使用 Windows NT 4 或更高版本的特定功能。
#define _WIN32_WINNT 0x0500 // 为 Windows98 和 Windows 2000 及更新版本改变为适当的值。
#endif      

#ifndef _WIN32_WINDOWS  // 允许使用 Windows 98 或更高版本的特定功能。
#define _WIN32_WINDOWS 0x0510 //为 Windows Me 及更新版本改变为适当的值。
#endif

编译原来的程序通过,打开带有半透明背景的图片,马上显示背面的网格,OK!问题找到了!打开透明的怎么样呢?结果令人失望,大片的透明背景被绘制成了白色背景,似乎还是没有解决好啊,再看看MSDN,里面有一个TransparentBlt绘制透明,他不是把透明变成白色的了吗?TransparentBlt里面的关键色改成白色绘制,哈哈,透明的图片出来了。

总结一下:

1.设置_WIN32系统,确保生成的程序支持透明(主要是WINVER)

2.用TransparentBlt输出透明图片,关键色定为白色

Visual C++6.0使用GDI+的一般方法 1. 载解压GDI+开发包; 2. 正确设置include & lib 目录; 3. stdafx.h 添加: #ifndef ULONG_PTR #define ULONG_PTR unsigned long* #endif #include 4. 程序中添加GDI+的包含文件gdiplus.h以及附加的类库gdiplus.lib。 通常gdiplus.h包含文件添加在应用程序的stdafx.h文件中,而gdiplus.lib可用两种进行添加: 第一种是直接在stdafx.h文件中添加下列语句: #pragma comment( lib, "gdiplus.lib" ) 另一种方法是: 在VC.net中添加库文件在:项目菜单->属性->链接器->输入 举个例子: (1)在应用程序项目的应用类中,添加一个成员变量,如下列代码: ULONG_PTR m_gdiplusToken; 其中,ULONG_PTR是一个DWORD数据类型,该成员变量用来保存GDI+被初始化后在应用程序中的GDI+标识,以便能在应用程序退出后,引用该标识来调用Gdiplus:: GdiplusShutdown来关闭GDI+。 (2)在应用类中添加ExitInstance的重载,并添加下列代码用来关闭GDI+: int CGDITestApp::ExitInstance() { Gdiplus::GdiplusShutdown(m_gdiplusToken); return CWinApp::ExitInstance(); } (3)在应用类的InitInstance函数中添加GDI+的初始化代码: 注意:下面这些GDI+的初始化代码必须放在m_pMainWnd->UpdateWindow();之前。 CWinApp::InitInstance(); Gdiplus::GdiplusStartupInput gdiplusStartupInput; Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL); (4)在需要绘图的窗口或视图类中添加GDI+的绘制代码。 下面分别就单文档和基于对话框应用程序为例,说明使用GDI+的一般过程和方法。 1. 在单文档应用程序中使用GDI+ 在上面的过程中,我们就是以一个单文档应用程序Ex_GDIPlus作为示例的。下面列出第4步所涉及的代码: void CGDITestView::OnDraw(CDC* pDC) { CGDITestDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here usingnamespace Gdiplus; Graphics graphics(pDC->m_hDC); Pen newPen(Color(255,0,0),3); HatchBrush newBrush(HatchStyleCross,Color(255,0,255,0),Color(255,0,0,255));//创建一个填充画刷,前景色为绿色,背景色为蓝色 graphics.DrawRectangle(&newPen,50,50,100,60);// 在(50,50)处绘制一个长为100,高为60的矩形 graphics.FillRectangle(&newBrush,50,50,100,60); // 在(50,50)处填充一个长为100,高为60的矩形区域 } 编译并运行,结果如图:
要将 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、付费专栏及课程。

余额充值