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的更多相关文章

  1. gcc和MinGW的异同(在cygwin/gcc做的东西可以无缝的用在linux下,没有任何问题,是在windows下开发linux程序的一个很好的选择)

    cygwin/gcc和MinGW都是gcc在windows下的编译环境,但是它们有什么区别,在实际工作中如何选择这两种编译器. cygwin/gcc完全可以和在linux下的gcc化做等号,这个可以从 ...

  2. Windows 下针对python脚本做一个简单的进程保护

    前提: 大家运行的脚本程序经常会碰到系统异常关闭.或被其他用户错杀的情况.这样就需要一个进程保护的工具. 本文结合windows 的计划任务,实现一个简单的进程保护的功能. 利用py2exe生产 ex ...

  3. 在Windows下编写并运行第一个ASP.NET 5 Preview Web API程序

    2015年07月21日在微软中国MSDN的官方微博上得知Visual Studio 2015正式版完美发布. 抱着尝鲜的心态下载了Visual Studio社区版本. 在这个首发的版本里面,我们可以看 ...

  4. windows下mysql安装失败的一个解决案例

    操作系统:windows8.1,之前安装过mysql,这次安装在配置的最后一部执行“Apply security settings”的过程中弹出经典错误: Access denied for user ...

  5. windows下修复Linux引导 and linux下几个常用软件

    在这里,我选择的是deepinLinux,不用说,高端大气上档次! Linux下引导修复 在win7上安装好了Linux,一不小心Linux系统启动不了 (一不小心的过程,想使用root登录图像界面, ...

  6. windows 下的 Rsync 同步

    整理一下 windows 下的 rsync 文件同步. Rsync下载地址: 链接:https://pan.baidu.com/s/1nL0Ee_u76ytWKUFMeiKDIw 提取码:52in 一 ...

  7. linux,windows下日志文件查找关键词

    1.查找 /apps/tomcat/tomcat3/apache-tomcat-7.0.69/logs 目录下已.txt结尾的文件,在文件中搜索关键字 IfcmpEcrService并打印行号 /lo ...

  8. windows下安装RabbitMQ【我】

    windows下 安装 rabbitMQ rabbitMQ是一个在AMQP协议标准基础上完整的,可服用的企业消息系统.它遵循Mozilla Public License开源协议,采用 Erlang 实 ...

  9. windows下docker 启动jenkins成功,浏览器无法访问,拒绝了我们的连接

    [问题现象] 在Windows下使用docker启动了一个jenkins,翻越了无数的坑,最后的启动命令为 docker run --name jenkins -u root -p 8000:8000 ...

随机推荐

  1. api-doc-php

    主要功能: 根据接口注释自动生成接口文档 演示地址 [Gitee Pages:]http://itxq.gitee.io/api-doc-php 开源地址: [GigHub:]https://gith ...

  2. hackbar简单安装使用教程

    安装hackbar: 在火狐的附加组件中搜索“hackbar”,将它添加到火狐浏览器中, 重启后Firefox后安装完成,按F9键打开我们就会看到在地址栏下面会出现一个大框框就是hackbar了 框框 ...

  3. sql server数据库显示“单用户”的解决方法

    USE master; GO DECLARE @SQL VARCHAR(MAX); SET @SQL='' SELECT @SQL=@SQL+'; KILL '+RTRIM(SPID) --杀掉该进程 ...

  4. linux 系统应用程序桌面图标显示及自启动

    cp test.desktop $RPM_BUILD_ROOT/usr/share/applications/    桌面显示 cp test.desktop $RPM_BUILD_ROOT/etc/ ...

  5. maven:不再支持源选项 5。请使用 6 或更高版本。

    解决办法: 在pom.xml中添加maven的配置 <maven.compiler.source>11</maven.compiler.source> <maven.co ...

  6. java上传文件类型检测

    在进行文件上传时,特别是向普通用户开放文件上传功能时,需要对上传文件的格式进行控制,以防止黑客将病毒脚本上传.单纯的将文件名的类型进行截取的方式非常容易遭到破解,上传者只需要将病毒改换文件名便可以完成 ...

  7. JS增删改查localStorage实现搜索历史功能

    <script type="text/javascript"> var referrerPath = "@ViewBag.ReferrerPath" ...

  8. 删除lvm时出现"Logical volume contains a filesystem in use"

    问题描述: k8s环境中需要重新创建lvm:/dev/mapper/test-vg-test-storage,该lvm挂载在/data/prometheus下面,在删除出现"Logical ...

  9. Java开发笔记(一百四十二)JavaFX的对话框

    JavaFX的对话框主要分为提示对话框和文件对话框两类,其中提示对话框又分作消息对话框.警告对话框.错误对话框.确认对话框四种.这四种对话框都使用Alert控件表达,并通过对话框类型加以区分,例如Al ...

  10. svg 直线水平渐变为什么没有效果,必须得是一条倾斜的不水平的直线才有渐变效果呢??

    参考:https://blog.csdn.net/u012260672/article/details/80905631 对x1=x2(没有宽度)或者y1=y2(没有高度)的直线(line以及path ...