• 基于GDI显示png图像


    intro

    先前基于GDI已经能够显示BITMAP图像:windows下控制台程序实现窗口显示 ,其中BMP图像是使用LoadImage()这一Win32 API函数来做的。考虑到LoadImage()函数并不能读取png图像,因此需要libpng或stb等png编解码库的帮助。

    网上找到相关代码不多,稍加修改可以运行,具备特点:

    • 纯C,单个文件(依赖的libpng和zlib可以忽略)

    • 直接读取png图像而不是通过读取.rc文件(资源文件)再读取png图像

    • png图像的读取:基于libpng(以及zlib),我直接用的opencv345 windows预编译报里的.h文件和库文件

    • 入口函数为main()而非WinMain(),也即控制台程序,方便作为库函数、移植

    • 不同于BMP的地方:在窗口处理函数的创建阶段有所不同:

      case WM_CREATE:
      	if (image_type == BMP) {
      		my_window->hBmp = (HBITMAP)LoadImage(NULL, "D:/work/libfc/imgs/lena512.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
      	}
      	else if (image_type == PNG) {
      		ReadPngData("D:/work/libfc/imgs/Lena.png", &im_width, &im_height, &my_window->imdata);
      		my_window->hBmp = CreateBitmap(im_width, im_height, 32, 1, my_window->imdata);
      	}
      
    • 并没有处理alpha通道
      如果图像有alpha通道,透明区域将显示为黑色,因为用的是BitBlt()。如果要使用alpha通道(例如做融合,或者显示为黑色以外的颜色),则应当使用AlphaBlend()和msing32库:

      #pragma comment(lib, "msimg32.lib")
      ...
      
          BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, my_window->dc, 0, 0, SRCCOPY);
          //AlphaBlend(hdc, 100, 0, bm.bmWidth, bm.bmHeight, my_window->dc, 0, 0, bm.bmWidth, bm.bmHeight, bf);
      

      opencv中看起来也是把透明区域显示为黑色。

    • 缺点:窗口client区域大小固定,没有能根据图像尺寸来变化

    代码

    #include <stdio.h>
    #include <windows.h>
    #include "png.h"
    
    
    #define CRTDBG_MAP_ALLOC 
    #include <crtdbg.h>
    
    
    #pragma comment(lib, "D:/work/libfc/lib/libpng.lib")
    #pragma comment(lib, "D:/work/libfc/lib/zlib.lib")
    
    typedef struct MyRect {
    	int x, y, width, height;
    } MyRect;
    
    const char* project_root = "D:/work/libfc";
    char bitmap_im_pth[100];
    
    typedef struct MyWindow {
    	HDC dc;
    	//HGDIOBJ image;
    	HBITMAP hBmp;
    	unsigned char* imdata;
    } MyWindow;
    
    MyWindow* my_window;
    enum ImageType {BMP, PNG};
    
    long ReadPngData(const char *szPath, int *pnWidth, int *pnHeight, unsigned char **cbData)
    {
    	FILE *fp = NULL;
    	long file_size = 0, pos = 0, mPos = 0;
    	int color_type = 0, x = 0, y = 0, block_size = 0;
    
    	png_infop info_ptr;
    	png_structp png_ptr;
    	png_bytep *row_point = NULL;
    
    	fp = fopen(szPath, "rb");
    	if (!fp)    return -1;            //文件打开错误则返回 FILE_ERROR
    
    	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);        //创建png读取结构
    	info_ptr = png_create_info_struct(png_ptr);        //png 文件信息结构
    	png_init_io(png_ptr, fp);                //初始化文件 IO
    	png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);                //读取png文件
    
    	*pnWidth = png_get_image_width(png_ptr, info_ptr);        //获得图片宽度
    	*pnHeight = png_get_image_height(png_ptr, info_ptr);        //获得图片高度
    	color_type = png_get_color_type(png_ptr, info_ptr);        //获得图片色彩深度
    	file_size = (*pnWidth) * (*pnHeight) * 4;                    //计算需要存储RGB(A)数据所需的内存大小
    	*cbData = (unsigned char *)malloc(file_size);            //申请所需的内容, 并将 *cbData 指向申请的这块内容
    
    	row_point = png_get_rows(png_ptr, info_ptr);            //读取RGB(A)数据
    
    	block_size = color_type == 6 ? 4 : 3;                    //根据是否具有a通道判断每次所要读取的数据大小, 具有Alpha通道的每次读4位, 否则读3位
    
    	//将读取到的RGB(A)数据按规定格式读到申请的内存中
    	for (x = 0; x < *pnHeight; x++)
    		for (y = 0; y < *pnWidth*block_size; y += block_size)
    		{
    			(*cbData)[pos++] = row_point[x][y + 2];        //B
    			(*cbData)[pos++] = row_point[x][y + 1];        //G
    			(*cbData)[pos++] = row_point[x][y + 0];        //R
    			(*cbData)[pos++] = row_point[x][y + 3];        //alpha
    		}
    
    	png_destroy_read_struct(&png_ptr, &info_ptr, 0);
    	fclose(fp);
    
    	return file_size;
    }
    
    
    LRESULT __stdcall WindowProcedure(HWND window, unsigned int msg, WPARAM wp, LPARAM lp)
    {
    	int im_width, im_height;
    	
    	int image_type = PNG;
    	switch (msg)
    	{
    	case WM_CREATE:
    		if (image_type == BMP) {
    			my_window->hBmp = (HBITMAP)LoadImage(NULL, "D:/work/libfc/imgs/lena512.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    		}
    		else if (image_type == PNG) {
    			ReadPngData("D:/work/libfc/imgs/Lena.png", &im_width, &im_height, &my_window->imdata);
    			my_window->hBmp = CreateBitmap(im_width, im_height, 32, 1, my_window->imdata);
    		}
    		if (my_window->hBmp == NULL)
    			MessageBox(window, "Could not load image!", "Error", MB_OK | MB_ICONEXCLAMATION);
    		break;
    
    	case WM_PAINT:
    	{
    		BITMAP bm;
    		PAINTSTRUCT ps;
    
    		HDC hdc = BeginPaint(window, &ps);
    		SetStretchBltMode(hdc, COLORONCOLOR);
    
    		my_window->dc = CreateCompatibleDC(hdc);
    		HBITMAP hbmOld = SelectObject(my_window->dc, my_window->hBmp);
    
    		GetObject(my_window->hBmp, sizeof(bm), &bm);
    
    #if 1
    		//原样拷贝,不支持拉伸
    		BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, my_window->dc, 0, 0, SRCCOPY);
    #else
    		RECT rcClient;
    		GetClientRect(window, &rcClient);//获得客户区的大小
    		int nWidth = rcClient.right - rcClient.left;//客户区的宽度
    		int nHeight = rcClient.bottom - rcClient.top;//客户区的高度
    		StretchBlt(hdc, 0, 0, nWidth, nHeight, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);//拉伸拷贝
    #endif
    
    		SelectObject(my_window->dc, hbmOld);
    		DeleteDC(my_window->dc);
    
    		EndPaint(window, &ps);
    	}
    	break;
    
    	case WM_DESTROY:
    		printf("
    destroying window
    ");
    		PostQuitMessage(0);
    		return 0L;
    
    	case WM_LBUTTONDOWN:
    		printf("
    mouse left button down at (%d, %d)
    ", LOWORD(lp), HIWORD(lp));
    
    		// fall thru
    	default:
    		//printf(".");
    		return DefWindowProc(window, msg, wp, lp);
    	}
    }
    
    const char* szWindowClass = "myclass";
    
    
    ATOM MyRegisterClass(HINSTANCE hInstance)
    {
    	WNDCLASSEX wc;
    	wc.cbSize = sizeof(WNDCLASSEX);
    	/* Win 3.x */
    	wc.style = CS_DBLCLKS;
    	wc.lpfnWndProc = WindowProcedure;
    	wc.cbClsExtra = 0;
    	wc.cbWndExtra = 0;
    	wc.hInstance = GetModuleHandle(0);
    	wc.hIcon = LoadIcon(0, IDI_APPLICATION);
    	wc.hCursor = LoadCursor(0, IDC_ARROW);
    	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    	wc.lpszMenuName = 0;
    	wc.lpszClassName = szWindowClass;
    	/* Win 4.0 */
    	wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
    
    	return RegisterClassEx(&wc);
    }
    
    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
    	MyRect rect;
    	rect.x = 300;
    	rect.y = 300;
    	rect.width = 640;
    	rect.height = 480;
    
    	DWORD defStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU;
    
    	HWND hwnd = CreateWindowEx(0, szWindowClass, "title",
    		defStyle, rect.x, rect.y,
    		rect.width, rect.height, 0, 0, hInstance, 0);
    
    	if (!hwnd)
    	{
    		return FALSE;
    	}
    	ShowWindow(hwnd, nCmdShow);
    	UpdateWindow(hwnd);
    
    	return TRUE;
    }
    
    void create_my_window(MyWindow** _my_window) {
    	MyWindow* my_window = (MyWindow*)malloc(sizeof(MyWindow));
    	my_window->dc = NULL;
    	my_window->imdata = NULL;
    	my_window->hBmp = NULL;
    
    	*_my_window = my_window; // write back
    }
    
    void destroy_my_window(MyWindow* my_window) {
    	if (my_window) {
    		if (my_window->imdata) free(my_window->imdata);
    		free(my_window);
    	}
    }
    
    int main()
    {
    	printf("hello world!
    ");
    
    	HINSTANCE hInstance = GetModuleHandle(0);
    	int nCmdShow = SW_SHOWDEFAULT;
    
    	MyRegisterClass(hInstance);
    	create_my_window(&my_window);
    
    	if (!InitInstance(hInstance, nCmdShow))
    	{
    		return FALSE;
    	}
    		
    	MSG msg;
    	while (GetMessage(&msg, 0, 0, 0)) {
    		DispatchMessage(&msg);
    	}
    
    	destroy_my_window(my_window);
    
    
    	return 0;
    	
    }
    

    参考

    使用 Libpng 配合 GDI 完成对 png 图片的解析与显示 , 讲解细致

    How would I load a PNG image using Win32/GDI (no GDI+ if possible)? (没用)

    使用libpng和GDI读取显示png图片 (代码无法运行)

  • 相关阅读:
    洛谷——P2054 [AHOI2005]洗牌(扩展欧几里得,逆元)
    线性筛法(伪模板及。。。)
    洛谷——P3919 【模板】可持久化数组(可持久化线段树/平衡树)
    CF450B Jzzhu and Sequences(矩阵加速)
    洛谷——P1349 广义斐波那契数列(矩阵加速)
    P1269 信号放大器
    istio prometheus预警Prometheus AlertManager
    istio promethus收集不到数据
    KubeletNotReady runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
    centos7虚拟机设置静态ip
  • 原文地址:https://www.cnblogs.com/zjutzz/p/10850382.html
Copyright © 2020-2023  润新知