windows下用純C實現一個簡陋的imshow:基於GDI

intro

先前實現了GDI顯示圖像時設定窗口大小爲圖像大小,不過並無刻意封裝函數調用接口,並不適合給其餘函數調用。如今簡單封裝一下,特色:html

  • 純Cwindows

  • 基於GDI,所以只支持windows平臺函數

  • 相似於opencv早期的接口:fc_load_image()讀取圖像,fc_show_image()顯示圖像,fc_free_image()釋放圖像,fc_destroy_window()釋放窗口資源post

  • 樣例代碼通過檢查,沒有內存泄露ui

  • 有不少沒有實現的:例如waitKey, keyPress的處理spa

代碼

#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.cppcode

相關文章
相關標籤/搜索