cwindowswinapipixmap

Array buffer and winapi


Context :

Question :

I have a buffer of pixel represented as uint8_t buffer[width * height * PIXEL_SIZE] that i would like to regularly modify the content data and redraw the buffer into the window.

I'm encoutering two issues with the winapi with which one i'm lost :

I have made many researchs but no code snippet have successfully helped me to solve my issue.

Here is a not working code sample in order to show what i would like to archieve with the elements of code i have:

new_image.c

// Global variables
static HDC      hdc;
static HDC      context_hdc;
static HBITMAP  hDib;
static HGDIOBJ  obj;

static void     set_bmi_object(BITMAPINFO *bmi, int width, int height) {
    memset(bmi, 0, sizeof(BITMAPINFO));

    bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi->bmiHeader.biWidth = width;
    bmi->bmiHeader.biHeight = -height;
    bmi->bmiHeader.biPlanes = 1;
    bmi->bmiHeader.biBitCount = 32;
    bmi->bmiHeader.biCompression = BI_RGB;
}

// Allocate a new image buffer
void             *new_image(HWND hwnd, int width, int height)
{
    BITMAPINFO  bmi;
    BYTE        *bits = NULL;
    void        *buffer;

    if (NULL == (buffer = (char*)malloc(width * height * PIXEL_SIZE)))
        return (NULL);

    set_bmi_object(&bmi, width, height);

    hdc = GetDC(hwnd);

    hDib = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**)(&bits), 
                            NULL, 0);

    if (hDib != NULL) {
        context_hdc = CreateCompatibleDC(hdc);
        if (context_hdc == NULL) {
            DeleteObject(hDib);
        } else {
            obj = SelectObject(context_hdc, hDib);
            CopyMemory(bits, buffer, width * height * sizeof(PIXEL_SIZE));
        }
    }

    return (newimg);
}

// Print the buffer of pixel on the window
void             put_image_to_window(HWND hwnd, void *buffer, int x, int y)
{
    (void)hwnd;

    // Void buffer because i should use directly HDCcontext_hdc linked to HGDIOBJ   obj ?
    (void)buffer;

    BitBlt(hdc, // destination
        x,
        y,
        500, // width of the region
        500, // height
        context_hdc, // source
        0,   // x
        0,   // y
        SRCCOPY);

    UpdateWindow(hwnd);
}

main.c

static const char g_szClassName[] = "myWindowClass";

static void paint(HWND hwnd) {
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    EndPaint(hwnd, &ps);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_PAINT:
        paint(hwnd);
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

int main(void) {
    MSG             Msg;
    HINSTANCE       hInstance;
    HWND            hwnd;
    STARTUPINFOA    startup_info;
    WNDCLASSEX      wc;
    HWND            hwnd;

    GetStartupInfoA(&startup_info);

    hInstance = GetModuleHandle(NULL);

    memset(&wc, 0, sizeof(wc));

    // Registering the Window Class
    wc.cbSize = sizeof(WNDCLASSEX);
    // ... etc
    wc.lpszClassName = TEXT(g_szClassName);

    if (!RegisterClassEx(&wc)) {
        return (-1);
    }

    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        g_szClassName,
        "Title,
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        500, 
        500,
        NULL,
        NULL,
        hInstance,
        NULL);

    if (hwnd == NULL) {
        return (-1);
    }

    ShowWindow(hwnd, startup_info.wShowWindow);


    image = new_image(hwnd, 500, 500);

    put_image_to_window(hwnd, image, 0, 0);

    UpdateWindow(hwnd);

    // The Message Loop
    while (GetMessage(&Msg, NULL, 0, 0)) {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }

    return (Msg.wParam);
}

Here is in new_image.c


Solution

  • Solution

    Thanks to the answers from @RemyLebeau @IInspectable @Raymond Chen and @BarmakShemirani answers, here is a solution.

    Now i successfully update the window according to buffer obtained with the CreateDIBSection() function, without passing through the WM_PAINT event.

    I use the UpdateLayeredWindow() function to update the window's pixels.

    Here is the solution code :

    new_image.c

    // global variables
    static HBITMAP hDib;
    
    static void     set_bmi_object(BITMAPINFO *bmi, int width, int height) {
        memset(bmi, 0, sizeof(BITMAPINFO));
    
        bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bmi->bmiHeader.biWidth = width;
        bmi->bmiHeader.biHeight = -height;
        bmi->bmiHeader.biPlanes = 1;
        bmi->bmiHeader.biBitCount = 32;
        bmi->bmiHeader.biCompression = BI_RGB;
    }
    
    // Allocate a new image buffer
    void             *new_image(HWND hwnd, int width, int height)
    {
        BITMAPINFO  bmi;
        void        *buffer;
        HDC         hdc;
    
        set_bmi_object(&bmi, width, height);
    
        hdc = GetDC(hwnd);
    
        hDib = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**)(&buffer), NULL, 0);
    
        ReleaseDC(instance->win_list->hwnd, hdc);
    
        return (buffer);
    }
    
    // Print the buffer of pixel on the window
    void             put_image_to_window(HWND hwnd, void *buffer, int x, int y)
    {
        HDC                     hdc;
        HDC                     context_hdc;
        HGDIOBJ                 old_obj;
    
        hdc = GetDC(hwnd);
    
        context_hdc = CreateCompatibleDC(hdc);
    
        old_obj = SelectObject(context_hdc, hDib);
    
        BitBlt(hdc,
            0,
            0,
           500,
           500,
           context_hdc,
           0,
           0,
           SRCCOPY);
    
        SelectObject(context_hdc, old_obj);
    
        DeleteDC(context_hdc);
        ReleaseDC(hwnd, hdc);
    
        // Call UpdateLayeredWindow
        BLENDFUNCTION blend = {0};
        blend.BlendOp = AC_SRC_OVER;
        blend.SourceConstantAlpha = 128;// half transparent
        blend.AlphaFormat = AC_SRC_ALPHA;
        POINT ptLocation = {x, y};
        SIZE szWnd = {500, 500};
        POINT ptSrc = {0, 0};
        UpdateLayeredWindow(hwnd, hdc, &ptLocation, &szWnd, context_hdc, &ptSrc, 0, &blend, ULW_ALPHA);
    }
    

    main.c

    static const char g_szClassName[] = "myWindowClass";
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        switch (msg)
        {
            case WM_CLOSE:
                DestroyWindow(hwnd);
            break;
            case WM_DESTROY:
                PostQuitMessage(0);
                break;
        }
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    
    int main(void) {
        MSG             Msg;
        HINSTANCE       hInstance;
        HWND            hwnd;
        STARTUPINFOA    startup_info;
        WNDCLASSEX      wc;
        HWND            hwnd;
    
        GetStartupInfoA(&startup_info);
    
        hInstance = GetModuleHandle(NULL);
    
        memset(&wc, 0, sizeof(wc));
    
        // Registering the Window Class
        wc.cbSize = sizeof(WNDCLASSEX);
        // ... etc
        wc.lpszClassName = TEXT(g_szClassName);
    
        if (!RegisterClassEx(&wc)) {
            return (-1);
        }
    
        hwnd = CreateWindowEx(
            WS_EX_CLIENTEDGE,
            g_szClassName,
            "Title,
            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            500, 
            500,
            NULL,
            NULL,
            hInstance,
            NULL);
    
        if (hwnd == NULL) {
            return (-1);
        }
    
        ShowWindow(hwnd, startup_info.wShowWindow);
    
        image = new_image(hwnd, 500, 500);
    
        put_image_to_window(hwnd, image, 0, 0);
    
        // The Message Loop
        while (GetMessage(&Msg, NULL, 0, 0)) {
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
        }
    
        return (Msg.wParam);
    }
    

    Here is a must read that @IInspectable gave, for WinAPI beginners like me : Painting and Drawing.

    Should have read this before questionning ...