How would I load a PNG image using Win32/GDI (no GDI+ if possible)?

前端 未结 4 540
轮回少年
轮回少年 2020-11-28 10:32

Is it possible to load a PNG from a file into an HBITMAP using Win32 GDI functions? If not, what would be the lightest solution without using external libraries (like libpn

4条回答
  •  不知归路
    2020-11-28 11:14

    We can display png image via GDI, by the following two steps when creating your window(case WM_CREATE in window procedure function):

    1. load png file (via libpng or stb image), pixel values saved in a variable, say buffer
    2. create HBITMAP instance using buffer in CreateBitmap() function

    Here's the runnable code, which is in pure C and main() as entry point function (libpng and zlib are from my own opencv compilation)

    #include 
    #include 
    #include "png.h"
    
    
    #define CRTDBG_MAP_ALLOC 
    #include 
    
    // **NB**: You may use OpenCV prebuilt package's self contained libpng.lib file
    // or, maybe, you can also compile it from source (which cost time and not necessary), see: `http://www.libpng.org` and  `https://www.zlib.net`
    
    #define LIBPNG_PTH "D:/opencv_249/build/x64/vc12/staticlib/libpng.lib"
    #define ZLIB_PTH "D:/opencv_249/build/x64/vc12/staticlib/zlib.lib"
    
    #pragma comment(lib, LIBPNG_PTH)
    #pragma comment(lib, ZLIB_PTH)
    
    typedef struct MyRect {
        int x, y, width, height;
    } MyRect;
    
    char bitmap_im_pth[100];
    
    typedef struct MyWindow {
        HDC dc;
        //HGDIOBJ image;
        HBITMAP hBmp;
        unsigned char* imdata;
    } MyWindow;
    
    MyWindow* my_window;
    enum ImageType {BMP, PNG};
    
    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;
    
        png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
        info_ptr = png_create_info_struct(png_ptr);
        png_init_io(png_ptr, fp);
        png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);
    
        *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;
        *cbData = (unsigned char *)malloc(file_size);
        row_point = png_get_rows(png_ptr, info_ptr);
    
        block_size = color_type == 6 ? 4 : 3;
    
        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;
    }
    
    
    LRESULT __stdcall WindowProcedure(HWND window, unsigned int msg, WPARAM wp, LPARAM lp)
    {
        int im_width, im_height;
    
        int image_type = PNG;
        switch (msg)
        {
        case WM_CREATE:
            if (image_type == BMP) {
                my_window->hBmp = (HBITMAP)LoadImage(NULL, "lena512.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
            }
            else if (image_type == PNG) {
                ReadPngData("Lena.png", &im_width, &im_height, &my_window->imdata);
                my_window->hBmp = CreateBitmap(im_width, im_height, 32, 1, my_window->imdata);
            }
            if (my_window->hBmp == NULL)
                MessageBox(window, "Could not load image!", "Error", MB_OK | MB_ICONEXCLAMATION);
            break;
    
        case WM_PAINT:
        {
            BITMAP bm;
            PAINTSTRUCT ps;
    
            HDC hdc = BeginPaint(window, &ps);
            SetStretchBltMode(hdc, COLORONCOLOR);
    
            my_window->dc = CreateCompatibleDC(hdc);
            HBITMAP hbmOld = SelectObject(my_window->dc, my_window->hBmp);
    
            GetObject(my_window->hBmp, sizeof(bm), &bm);
    
    #if 1
            BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, my_window->dc, 0, 0, SRCCOPY);
    #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(my_window->dc, hbmOld);
            DeleteDC(my_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);
        }
    }
    
    const char* szWindowClass = "myclass";
    
    
    ATOM 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 = szWindowClass;
        /* Win 4.0 */
        wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
    
        return RegisterClassEx(&wc);
    }
    
    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
        MyRect rect;
        rect.x = 300;
        rect.y = 300;
        rect.width = 640;
        rect.height = 480;
    
        DWORD defStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU;
    
        HWND hwnd = CreateWindowEx(0, szWindowClass, "title",
            defStyle, rect.x, rect.y,
            rect.width, rect.height, 0, 0, hInstance, 0);
    
        if (!hwnd)
        {
            return FALSE;
        }
        ShowWindow(hwnd, nCmdShow);
        UpdateWindow(hwnd);
    
        return TRUE;
    }
    
    void create_my_window(MyWindow** _my_window) {
        MyWindow* my_window = (MyWindow*)malloc(sizeof(MyWindow));
        my_window->dc = NULL;
        my_window->imdata = NULL;
        my_window->hBmp = NULL;
    
        *_my_window = my_window; // write back
    }
    
    void destroy_my_window(MyWindow* my_window) {
        if (my_window) {
            if (my_window->imdata) free(my_window->imdata);
            free(my_window);
        }
    }
    
    int main()
    {
        printf("hello world!\n");
    
        HINSTANCE hInstance = GetModuleHandle(0);
        int nCmdShow = SW_SHOWDEFAULT;
    
        MyRegisterClass(hInstance);
        create_my_window(&my_window);
    
        if (!InitInstance(hInstance, nCmdShow))
        {
            return FALSE;
        }
    
        MSG msg;
        while (GetMessage(&msg, 0, 0, 0)) {
            DispatchMessage(&msg);
        }
    
        destroy_my_window(my_window);
    
    
        return 0;
    
    }
    

    Reference: https://www.cnblogs.com/mr-wid/archive/2013/04/22/3034840.html

提交回复
热议问题