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

前端 未结 4 450
轮回少年
轮回少年 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 10:56

    You can use the Windows Imaging Component to load PNG files (on Windows XP SP2 and later). See MSDN Magazine for an introduction on how to use the API and my blog post for a code sample that loads a PNG from an IStream and converts it to an HBITMAP.

    0 讨论(0)
  • 2020-11-28 11:10

    There is no need to use Windows Imaging Component, GDI+ or PNG library. You can use Icon functionality.

    1. Add new icon (ICO_PNG) to VC project resources with custom Width and Height (Resource Editor->Image->New Image Type). Copy Your png image here and use Fill Tool+transparent color to make icon transparent.

    2. Add Picture Control (IDC_PNG) to Your dialog (Type = Owner draw).

    3. Dialog procedure code:

    switch (msg)
    {
        ...
    
        case WM_DRAWITEM:
        {
            LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)lParam;
            if (pDIS->CtlID == IDC_PNG)
            {
                HICON hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(ICO_LOGO), IMAGE_ICON, 0, 0, LR_LOADTRANSPARENT); 
                DrawIconEx(pDIS->hDC, 0, 0, hIcon, 0, 0, 0, NULL, DI_NORMAL);
                DestroyIcon(hIcon);
                return TRUE;
            }
        }
    }
    
    0 讨论(0)
  • 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 <stdio.h>
    #include <windows.h>
    #include "png.h"
    
    
    #define CRTDBG_MAP_ALLOC 
    #include <crtdbg.h>
    
    // **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

    0 讨论(0)
  • 2020-11-28 11:19

    You can do it with StretchDIBits API, but limited by OS/driver availability.

    Consult MSDN documentation for details:

    http://msdn.microsoft.com/en-us/library/dd145121(v=VS.85).aspx

    http://msdn.microsoft.com/en-us/library/dd145107(VS.85).aspx


    I sincerely apologize for misleading you guys interested in this issue. Let me correct my mistake. No StretchDIBits for PNG drawing. You'd better try WIC method or consider way to integrate GDI+ in your projects.

    0 讨论(0)
提交回复
热议问题