zoukankan      html  css  js  c++  java
  • windows下用纯C实现一个简陋的imshow:基于GDI

    intro

    先前实现了GDI显示图像时设定窗口大小为图像大小,不过并没有刻意封装函数调用接口,并不适合给其他函数调用。现在简单封装一下,特点:

    • 纯C

    • 基于GDI,因此只支持windows平台

    • 类似于opencv早期的接口:fc_load_image()读取图像,fc_show_image()显示图像,fc_free_image()释放图像,fc_destroy_window()释放窗口资源

    • 样例代码经过检查,没有内存泄露

    • 有很多没有实现的:例如waitKey, keyPress的处理

    代码

    #include <stdio.h>
    #include <stdbool.h>
    #include <windows.h>
    #include "png.h"
    
    #pragma comment(lib, "D:/work/libfc/lib/libpng.lib")
    #pragma comment(lib, "D:/work/libfc/lib/zlib.lib")
    //#pragma comment(lib, "msimg32.lib") // for png's alpha channel
    
    static const char* fc_window_class_name = "fc GUI class";
    
    typedef enum FcImageType { FC_IMAGE_BMP, FC_IMAGE_PNG } FcImageType;
    
    typedef struct FcWindow {
    	HDC dc;
    	//HGDIOBJ image;
    	HBITMAP hBmp;
    	unsigned char* im_data;
    	FcImageType im_type;
    	int width;
    	int height;
    	int top_left_x; // window position, top left point's x coordinate
    	int top_left_y; // window posttion, top left point's y coordinate
    	bool adjusted;
    	const char* title;
    } FcWindow;
    
    FcWindow* fc_window;
    
    
    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;
    }
    
    
    //int DEFAULT_WIDTH, DEFAULT_HIGHT;
    void SetWindowSize(HWND hWnd)
    {
    	if (fc_window->adjusted == false) {
    		RECT WindowRect;
    		RECT ClientRect;
    		GetWindowRect(hWnd, &WindowRect);
    		GetClientRect(hWnd, &ClientRect);
    		WindowRect.right += (fc_window->width - ClientRect.right);
    		WindowRect.bottom += (fc_window->height - ClientRect.bottom);
    		MoveWindow(hWnd, WindowRect.left, WindowRect.top, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, true);
    	}
    
    	fc_window->adjusted = true;
    }
    
    
    LRESULT __stdcall WindowProcedure(HWND window, unsigned int msg, WPARAM wp, LPARAM lp)
    {
    	switch (msg)
    	{
    	case WM_CREATE:
    		if (fc_window->im_type == FC_IMAGE_BMP) {
    			fc_window->hBmp = (HBITMAP)LoadImage(NULL, "D:/work/libfc/imgs/lena512.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    		}
    		else if (fc_window->im_type == FC_IMAGE_PNG) {
    			fc_window->hBmp = CreateBitmap(fc_window->width, fc_window->height, 32, 1, fc_window->im_data);
    		}
    		if (fc_window->hBmp == NULL)
    			MessageBox(window, "Could not load image!", "Error", MB_OK | MB_ICONEXCLAMATION);
    		break;
    
    	case WM_PAINT:
    	{
    		SetWindowSize(window);
    		BITMAP bm;
    		PAINTSTRUCT ps;
    
    		HDC hdc = BeginPaint(window, &ps);
    		SetStretchBltMode(hdc, COLORONCOLOR);
    
    		fc_window->dc = CreateCompatibleDC(hdc);
    		HBITMAP hbmOld = SelectObject(fc_window->dc, fc_window->hBmp);
    
    		GetObject(fc_window->hBmp, sizeof(bm), &bm);
    
    #if 1
    		//原样拷贝,不支持拉伸
    		BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, fc_window->dc, 0, 0, SRCCOPY);
    		//AlphaBlend(hdc, 100, 0, bm.bmWidth, bm.bmHeight, my_window->dc, 0, 0, bm.bmWidth, bm.bmHeight, bf);
    #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(fc_window->dc, hbmOld);
    		DeleteDC(fc_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);
    	}
    }
    
    void fc_destroy_window() {
    	if (fc_window) {
    		free(fc_window);
    	}
    }
    
    void 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 = fc_window_class_name;
    	/* Win 4.0 */
    	wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
    
    	RegisterClassEx(&wc);
    }
    
    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
    	// hard code here. since x=600 and y=300 is good for most cases
    	fc_window->top_left_x = 600;
    	fc_window->top_left_y = 300;
    
    	DWORD defStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU;
    
    	HWND hwnd = CreateWindowEx(0, fc_window_class_name, fc_window->title,
    		defStyle, fc_window->top_left_x, fc_window->top_left_y,
    		fc_window->width, fc_window->height, 0, 0, hInstance, 0);
    
    	if (!hwnd)
    	{
    		return FALSE;
    	}
    	ShowWindow(hwnd, nCmdShow);
    	UpdateWindow(hwnd);
    
    	return TRUE;
    }
    
    void fc_create_window(FcWindow** _my_window) {
    	FcWindow* fc_window = (FcWindow*)malloc(sizeof(FcWindow));
    	fc_window->dc = NULL;
    	fc_window->im_data = NULL;
    	fc_window->hBmp = NULL;
    
    	fc_window->adjusted = false;
    
    	*_my_window = fc_window; // write back
    }
    
    typedef struct FcImage {
    	int width;
    	int height;
    	unsigned char* data;
    	size_t elem_size;
    } FcImage;
    
    FcImage* fc_load_image(const char* im_pth, size_t elem_size, FcImageType im_type) {
    	FcImage* im = (FcImage*)malloc(sizeof(FcImage));
    	im->height = -233;
    	im->width = -233;
    	im->data = NULL;
    	im->elem_size = elem_size;
    	//TODO: check if im_pth exist and priviledge OK
    	//TODO: guess image's file type, then load it via corresponding codec lib
    	if (im_type == FC_IMAGE_PNG) {
    		ReadPngData(im_pth, &im->width, &im->height, &im->data);
    	}
    	return im;
    }
    
    void fc_show_image(const char* title, FcImage* im) {
    	fc_create_window(&fc_window);
    	fc_window->width = im->width;
    	fc_window->height = im->height;
    	fc_window->im_data = im->data;
    	fc_window->im_type = FC_IMAGE_PNG;
    	fc_window->title = title;
    
    	HINSTANCE hInstance = GetModuleHandle(0);
    	int nCmdShow = SW_SHOWDEFAULT;
    
    	MyRegisterClass(hInstance);
    
    	if (!InitInstance(hInstance, nCmdShow))
    	{
    		fprintf(stderr, "Error! failed to init window
    ");
    	}
    
    	MSG msg;
    	while (GetMessage(&msg, 0, 0, 0)) {
    		DispatchMessage(&msg);
    	}
    }
    
    void fc_free_image(FcImage* im) {
    	if (im) {
    		if (im->data) {
    			free(im->data);
    			im->data = NULL;
    		}
    		free(im);
    		im = NULL;
    	}
    }
    
    int main()
    {
    	printf("hello libfc!
    ");
    	const char* im_pth = "D:/work/libfc/imgs/Lena.png";
    	FcImage* im = fc_load_image(im_pth, sizeof(unsigned char), FC_IMAGE_PNG);
    	if (im->data == NULL) {
    		printf("Error! failed to load image
    ");
    		return 1;
    	}
    
    	const char* im_pth2 = "D:/work/libfc/imgs/op.png";
    	FcImage* im2 = fc_load_image(im_pth2, sizeof(unsigned char), FC_IMAGE_PNG);
    	if (im2->data == NULL) {
    		printf("Error! failed to load image
    ");
    		return 1;
    	}
    
    	const char* title = "Lena png";
    	fc_show_image(title, im);
    
    	const char* title2 = "op png";
    	fc_show_image(title2, im2);
    
    	fc_free_image(im);
    	fc_free_image(im2);
    
    	fc_destroy_window();
    
    	return 0;
    }
    

    参考

    D:libopencv_345sourcesmoduleshighguisrc/window_w32.cpp

  • 相关阅读:
    node的http与前端交互示例(入门)
    PC监听鼠标和键盘事件,定时无响应退出
    日常问题记录
    基于node.js的websocket 前后端交互小功能
    基于node.js的websocket上传小功能
    阿里云服务器(Linux)上打开新端口
    云服务器初尝试
    wx小程序知识点(八)
    wx小程序知识点(七)
    点绘,线绘,图绘
  • 原文地址:https://www.cnblogs.com/zjutzz/p/10852430.html
Copyright © 2011-2022 走看看