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);                //初始化文件 I\O
	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("\ndestroying window\n");
		PostQuitMessage(0);
		return 0L;
	case WM_LBUTTONDOWN:
		printf("\nmouse left button down at (%d, %d)\n", 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\n");
	}
	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!\n");
	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\n");
		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\n");
		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:\lib\opencv_345\sources\modules\highgui\src/window_w32.cpp
windows下用纯C实现一个简陋的imshow:基于GDI的更多相关文章
- gcc和MinGW的异同(在cygwin/gcc做的东西可以无缝的用在linux下,没有任何问题,是在windows下开发linux程序的一个很好的选择)
		
cygwin/gcc和MinGW都是gcc在windows下的编译环境,但是它们有什么区别,在实际工作中如何选择这两种编译器. cygwin/gcc完全可以和在linux下的gcc化做等号,这个可以从 ...
 - Windows 下针对python脚本做一个简单的进程保护
		
前提: 大家运行的脚本程序经常会碰到系统异常关闭.或被其他用户错杀的情况.这样就需要一个进程保护的工具. 本文结合windows 的计划任务,实现一个简单的进程保护的功能. 利用py2exe生产 ex ...
 - 在Windows下编写并运行第一个ASP.NET 5 Preview Web API程序
		
2015年07月21日在微软中国MSDN的官方微博上得知Visual Studio 2015正式版完美发布. 抱着尝鲜的心态下载了Visual Studio社区版本. 在这个首发的版本里面,我们可以看 ...
 - windows下mysql安装失败的一个解决案例
		
操作系统:windows8.1,之前安装过mysql,这次安装在配置的最后一部执行“Apply security settings”的过程中弹出经典错误: Access denied for user ...
 - windows下修复Linux引导  and   linux下几个常用软件
		
在这里,我选择的是deepinLinux,不用说,高端大气上档次! Linux下引导修复 在win7上安装好了Linux,一不小心Linux系统启动不了 (一不小心的过程,想使用root登录图像界面, ...
 - windows 下的 Rsync 同步
		
整理一下 windows 下的 rsync 文件同步. Rsync下载地址: 链接:https://pan.baidu.com/s/1nL0Ee_u76ytWKUFMeiKDIw 提取码:52in 一 ...
 - linux,windows下日志文件查找关键词
		
1.查找 /apps/tomcat/tomcat3/apache-tomcat-7.0.69/logs 目录下已.txt结尾的文件,在文件中搜索关键字 IfcmpEcrService并打印行号 /lo ...
 - windows下安装RabbitMQ【我】
		
windows下 安装 rabbitMQ rabbitMQ是一个在AMQP协议标准基础上完整的,可服用的企业消息系统.它遵循Mozilla Public License开源协议,采用 Erlang 实 ...
 - windows下docker 启动jenkins成功,浏览器无法访问,拒绝了我们的连接
		
[问题现象] 在Windows下使用docker启动了一个jenkins,翻越了无数的坑,最后的启动命令为 docker run --name jenkins -u root -p 8000:8000 ...
 
随机推荐
- Sword 哈希表
			
哈希表 哈希表是一种典型的以空间换取时间的数据结构,在没有冲突的情况下,对任意元素的插入.索引.删除的时间复杂度都是O().这样优秀的时间复杂度是通过将元素的key值以hash方法f映射到哈希表中的某 ...
 - WPF Button LinkButton 绑定多个值 Template
			
效果如下: 代码如下: <Button Click="InventoryDetail_OnClick" Template="{StaticResource Inve ...
 - openvswitch2.11.0修改源码后重新编译
			
一:推文 https://www.jianshu.com/p/923f49c290f5(可以删除运行当中的DataPath内核) https://github.com/ebiken/doc-netwo ...
 - 最好用的Redis Desktop Manager 0.9.3 版本下载
			
因为Redis Desktop Manager作者在 0.9.4 版本之后选择对所有的安装包收费,不再提供安装包下载,但是源码依旧公开. github 上有 redis destop manager ...
 - Java技术体系 JDK与JRE
			
从广义上讲,Clojure.JRuby.Groovy等运行于Java虚拟机上的语言及其相关的程序都属于Java技术体系中的一员.如果仅从传统意义上来看,Sun官方所定义的Java技术体系包括以下几个组 ...
 - es6 学习小计
			
es6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这称之为解构:
 - Grammar01 语法七要素之一_词类
			
1 词类 1.1 词类表格 实词 名词 -> n. -> noun -> 给所有人和物命名的词. 动词 -> v. (vt. vi.) -> verb ( transit ...
 - python获取https并且写文件日志
			
# -*- coding: utf-8 -*- import os import os.path import shutil import chardet import urllib.request ...
 - noi openjudge 1768:最大子矩阵
			
链接:http://noi.openjudge.cn/ch0406/1768/ 描述已知矩阵的大小定义为矩阵中所有元素的和.给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩阵. 比如 ...
 - 026 SSM综合练习02--数据后台管理系统--数据库表创建及SSM环境搭建
			
1.数据库准备 本项目我们Oracle数据库,Oracle 为每个项目创建单独user,oracle数据表存放在表空间下,每个用户有独立表空间. (1)采用数据库管理员账号:SYSTEM,再配合数据库 ...