使用 libpng 配合 GDI 完成对 png 图片的解析与显示(2)

第二步: 通过 GDI 实现 png 的显示

这里准备了两张png图片, 一个作为背景(bk.png), 一张作为前景(op.png), 图片来源于互联网, 前景图具有 Alpha通道, 用于实现部分透明效果。图片如下:

使用 libpng 配合 GDI 完成对 png 图片的解析与显示

使用 libpng 配合 GDI 完成对 png 图片的解析与显示

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) ;
}

运行效果如下:

使用 libpng 配合 GDI 完成对 png 图片的解析与显示

关于 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 );。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/c09e917bfd6ffdd3c585a2d5acc97d66.html