第二步: 通过 GDI 实现 png 的显示
这里准备了两张png图片, 一个作为背景(bk.png), 一张作为前景(op.png), 图片来源于互联网, 前景图具有 Alpha通道, 用于实现部分透明效果。图片如下:
1. 创建一个Win32窗口(已折叠)
- WinMain
#include <windows.h>
#include "gdipng.h"
//////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
//////////////////////////////////////////////////////////////////////////
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdline, int iCmdShow )
{
TCHAR szAppName[] = TEXT("GdiPng");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hbrBackground = GetSysColorBrush( BLACK_BRUSH );
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION );
wndclass.hInstance = hInstance;
wndclass.lpfnWndProc = WndProc;
wndclass.lpszClassName = szAppName;
wndclass.lpszMenuName = NULL;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
if( !RegisterClass(&wndclass) )
{
MessageBox( NULL, TEXT("窗口类注册失败!"), TEXT("应用程序错误"), MB_OK | MB_ICONERROR );
return 0;
}
hwnd = CreateWindow(
szAppName,
TEXT("通过 GDI 实现 png 的显示 - Demo"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
800, 600,
NULL, NULL, hInstance, NULL
);
ShowWindow( hwnd, iCmdShow );
UpdateWindow( hwnd );
while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return msg.wParam;
}
2. 在处理 WM_PAINT 消息时进行显示
整个窗口回调函数部分的代码如下:
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HBITMAP hPngFr, hPngBk; //前景、背景位图句柄
HDC hdc, hdcFr, hdcBk; //客户区、前景、背景设备句柄
PAINTSTRUCT ps ;
static fr_x, fr_y; //前景图宽、高
static bk_x, bk_y; //背景图宽、高
static unsigned char *cbFrData=NULL; //用于指向前景图的RGB(A)数据内存地址
static unsigned char *cbBkData=NULL; //用于指向背景图的RGB(A)数据内存地址
BLENDFUNCTION bf = {0}; //BLENDFUNCTION结构变量, 作为函数参数, 用于 AlphaBlend 函数进行 Alpha 的融图
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.AlphaFormat = AC_SRC_ALPHA;
bf.SourceConstantAlpha = 255; //透明度, 范围为 0-255, 0:透明, 255:不透明
switch (message)
{
case WM_CREATE: //处理窗口创建消息
ReadPngData( "op.png", &fr_x, &fr_y, &cbFrData ); //读取前景图 op.png 数据
ReadPngData( "bk.png", &bk_x, &bk_y, &cbBkData ); //读取背景如 bk.png 数据
hPngFr = CreateBitmap( fr_x, fr_y, 32, 1, cbFrData ); //创建前景位图
hPngBk = CreateBitmap( bk_x, bk_y, 32, 1, cbBkData ); //创建背景位图
return 0 ;
case WM_PAINT: //处理重绘消息
hdc = BeginPaint (hwnd, &ps) ; //开始重绘
hdcFr = CreateCompatibleDC( hdc ); //创建前景设备句柄
SelectObject( hdcFr, hPngFr ); //将前景图句柄选入设备环境
hdcBk = CreateCompatibleDC( hdc ); //创建背景设备句柄
SelectObject( hdcBk, hPngBk ); //将背景图句柄选入设备环境
BitBlt( hdc, 0, 0, bk_x, bk_y, hdcBk, 0, 0, SRCCOPY ); //将背景图进行 BitBlt 操作显示在客户区中
AlphaBlend( hdc, 100, 0, fr_x, fr_y, hdcFr, 0, 0, fr_x, fr_y, bf ); //使用 AlphaBlend 函数将前景与背景进行融合
DeleteDC( hdcFr ); //删除前景图设备环境句柄
DeleteDC( hdcBk ); //删除背景图设备环境句柄
EndPaint( hwnd, &ps ); //结束重绘
return 0 ;
case WM_DESTROY: //处理撤销窗口消息
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
Wnd_Main.c 完整代码:
- W_Main.c
#include <windows.h>
#include "gdipng.h"
//////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
//////////////////////////////////////////////////////////////////////////
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdline, int iCmdShow )
{
TCHAR szAppName[] = TEXT("GdiPng");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hbrBackground = GetSysColorBrush( BLACK_BRUSH );
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION );
wndclass.hInstance = hInstance;
wndclass.lpfnWndProc = WndProc;
wndclass.lpszClassName = szAppName;
wndclass.lpszMenuName = NULL;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
if( !RegisterClass(&wndclass) )
{
MessageBox( NULL, TEXT("窗口类注册失败!"), TEXT("应用程序错误"), MB_OK | MB_ICONERROR );
return 0;
}
hwnd = CreateWindow(
szAppName,
TEXT("通过 GDI 实现 png 的显示 - Demo"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
800, 600,
NULL, NULL, hInstance, NULL
);
ShowWindow( hwnd, iCmdShow );
UpdateWindow( hwnd );
while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return msg.wParam;
}
//////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HBITMAP hPngFr, hPngBk; //前景、背景位图句柄
HDC hdc, hdcFr, hdcBk; //客户区、前景、背景设备句柄
PAINTSTRUCT ps ;
static fr_x, fr_y; //前景图宽、高
static bk_x, bk_y; //背景图宽、高
static unsigned char *cbFrData=NULL; //用于指向前景图的RGB(A)数据内存地址
static unsigned char *cbBkData=NULL; //用于指向背景图的RGB(A)数据内存地址
BLENDFUNCTION bf = {0}; //BLENDFUNCTION结构变量, 作为函数参数, 用于 AlphaBlend 函数进行 Alpha 的融图
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.AlphaFormat = AC_SRC_ALPHA;
bf.SourceConstantAlpha = 255; //透明度, 范围为 0-255, 0:透明, 255:不透明
switch (message)
{
case WM_CREATE: //处理窗口创建消息
ReadPngData( "op.png", &fr_x, &fr_y, &cbFrData ); //读取前景图 op.png 数据
ReadPngData( "bk.png", &bk_x, &bk_y, &cbBkData ); //读取背景如 bk.png 数据
hPngFr = CreateBitmap( fr_x, fr_y, 32, 1, cbFrData ); //创建前景位图
hPngBk = CreateBitmap( bk_x, bk_y, 32, 1, cbBkData ); //创建背景位图
return 0 ;
case WM_PAINT: //处理重绘消息
hdc = BeginPaint (hwnd, &ps) ; //开始重绘
hdcFr = CreateCompatibleDC( hdc ); //创建前景设备句柄
SelectObject( hdcFr, hPngFr ); //将前景图句柄选入设备环境
hdcBk = CreateCompatibleDC( hdc ); //创建背景设备句柄
SelectObject( hdcBk, hPngBk ); //将背景图句柄选入设备环境
BitBlt( hdc, 0, 0, bk_x, bk_y, hdcBk, 0, 0, SRCCOPY ); //将背景图进行 BitBlt 操作显示在客户区中
AlphaBlend( hdc, 100, 0, fr_x, fr_y, hdcFr, 0, 0, fr_x, fr_y, bf ); //使用 AlphaBlend 函数将前景与背景进行融合
//TransparentBlt( hdc, 200, 0, fr_x, fr_y, hdcFr, 0, 0, fr_x, fr_y, RGB(255, 255, 255) ); //对纯色背景的透明操作
DeleteDC( hdcFr ); //删除前景图设备环境句柄
DeleteDC( hdcBk ); //删除背景图设备环境句柄
EndPaint( hwnd, &ps ); //结束重绘
return 0 ;
case WM_DESTROY: //处理撤销窗口消息
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
运行效果如下:
关于 AlphaBlend 函数
该函数用来显示具有Alpha通道的图像, 将两个图像按照图片得Alpha数据以及指定的透明度进行融合。其函数原型:
BOOL AlphaBlend(
HDC hdcDest, // 目标环境设备句柄
int nXOriginDest, // 目标环境设备的x坐标
int nYOriginDest, // 目标环境设备的y坐标
int nWidthDest, // 目标矩形区域的宽度
int nHeightDest, // 目标矩形区域的高度
HDC hdcSrc, // 源设备环境句柄
int nXOriginSrc, // 源环境设备的x坐标
int nYOriginSrc, // 源环境设备的y坐标
int nWidthSrc, // 源矩形区域的宽度
int nHeightSrc, // 源矩形区域的高度
BLENDFUNCTION blendFunction // BLENDFUNCTION结构, 指定融合的方式
);
注意: 该函数在使用时需要在IDE的对象\模块库中添加 msimg32.lib, 或者使用 #pragma comment(lib, "msimg32.lib") 命令。
关于 BLENDFUNCTION 结构
BLENDFUNCTION 结构在 MSDN中的定义:
typedef struct _BLENDFUNCTION {
BYTE BlendOp; // 指定为 AC_SRC_OVER
BYTE BlendFlags; // 必须为 0
BYTE SourceConstantAlpha; // 源位图的透明度, 范围为 0-255, 0:透明, 255:不透明
BYTE AlphaFormat; // 指定为 AC_SRC_ALPHA
}BLENDFUNCTION;
代码解说:
在这段代码中, 首先在处理 WM_CREATE 消息时对前景png图以及背景png图进行读取, 同时, 也获取到了这两张png图片得宽度和高度, 再通过 CreateBitmap 函数将得到的 RGB(A) 数据转为 BITMAP 型的位图, 将位图句柄 hPngFr 和 hPngBk 指向这两张新创建的位图。
接下来就是在处理 WM_PAINT 消息时进行 png 的绘图操作, 创建前景、背景的设备环境句柄 hdcFr 和 hdcBk, 将位图句柄分别选入到设备环境中。
显示是通过两个函数来完成的, BitBlt 和 AlphaBlend。 BltBlt 将 RGB 数据进行输出, 不自动处理 Alpha 数据。 AlphaBlend 函数负责前后背景的融图。
最后就是删除创建的设备环境句柄 DeleteDC( hdcFr ); 、 DeleteDC( hdcBk );。