intro
先前实现了,不过并没有刻意封装函数调用接口,并不适合给其他函数调用。现在简单封装一下,特点:
纯C
基于GDI,因此只支持windows平台
类似于opencv早期的接口:
fc_load_image()
读取图像,fc_show_image()
显示图像,fc_free_image()
释放图像,fc_destroy_window()
释放窗口资源样例代码经过检查,没有内存泄露
有很多没有实现的:例如waitKey, keyPress的处理
代码
#include#include #include #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 channelstatic 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