简体   繁体   English

将原始像素数组绘制到 window (C++ WinAPI)

[英]Draw raw array of pixels onto a window (C++ WinAPI)

I have two buffers, both of the same size and type ( uint32_t* ).我有两个缓冲区,大小和类型都相同( uint32_t* )。 These buffers are supposed to represent the front and back buffers of a rendering/drawing system.这些缓冲区应该代表渲染/绘图系统的前缓冲区和后缓冲区。 They store 32-bit pixel data.它们存储 32 位像素数据。

I declare and initialize these buffers using;我使用以下方法声明和初始化这些缓冲区;

private:
    ...
    uint32_t* frontBuf;
    uint32_t* backBuf;
    size_t    gbufSize;
    ...
public:
    void Initialize() {
        ...  
        // prepare for rendering
        gbufSize = sizeof(uint32_t) * w * h;
        backBuf  = (uint32_t*)malloc(gbufSize);
        frontBuf = (uint32_t*)malloc(gbufSize);
        ...
    }

I also get the output, window and device handles for the console in the Initialize() method using:我还在Initialize()方法中使用以下方法获得了控制台的 output、window 和设备句柄:

        // get handles
        cwd = GetDC(GetConsoleWindow());
        chd = GetStdHandle(STD_OUTPUT_HANDLE);
        cwn = GetConsoleWindow();

I then have a few drawing methods, like a SetPixel method:然后我有一些绘图方法,比如SetPixel方法:

    size_t GFlatten(int x, int y) {
        return y * h + x;
    }

    void GSetPixel(int x, int y, uint32_t color) {
        backBuf[GFlatten(x, y)] = color;
    }

And finally, I have a GSwap method.最后,我有一个GSwap方法。 This method is supposed to swap the pointers of the buffers, clear the new back buffer and copy the front buffer to the screen.这个方法应该交换缓冲区的指针,清除新的后台缓冲区并将前台缓冲区复制到屏幕上。

The first two work (I think), but I have no idea how to implement the 3rd one (copying to the screen).前两个工作(我认为),但我不知道如何实现第三个(复制到屏幕)。 I would prefer to not use any external libraries.我宁愿不使用任何外部库。

Code for GSwap method: GSwap方法的代码:

    void GSwap() {
        // swap pointers
        uint32_t* frontTmp = frontBuf;
        frontBuf = backBuf;
        backBuf = frontTmp;

        // clear new back buffer
        memset(backBuf, 0, gbufSize);

        // draw on screen
        /* ??? */
    }

The full code: Pastebin完整代码: Pastebin

You can't draw to an arbitrary window by poking bytes into some buffer.您不能通过将字节插入某个缓冲区来绘制任意 window 。 Win32 is a higher level API than that. Win32 是比它更高级别的 API。

It is possible to create a bitmap and be able to draw into the bitmap by poking bytes into a buffer that Windows returns to you when you create the bitmap, however. It is possible to create a bitmap and be able to draw into the bitmap by poking bytes into a buffer that Windows returns to you when you create the bitmap, however. You can then paint that bitmap to a Window via BitBlt and appropriate device contexts, etc.然后,您可以通过BitBlt和适当的设备上下文等将 bitmap 绘制到 Window 等。

Below is a minimal example.下面是一个最小的例子。 (I retained your usage of a back buffer and front buffer even though it isn't really necessary here. The window itself is essentially a front buffer so you would only need "a swap chain" of one back buffer to not have flickering.) (我保留了您对后缓冲区和前缓冲区的使用,即使在这里实际上并不需要。window 本身本质上是一个前缓冲区,因此您只需要一个后缓冲区的“交换链”就不会出现闪烁。)

#include <windows.h>
#include <stdint.h>
#include <utility>
#include <algorithm>

constexpr int kTimerID = 101;

LRESULT CALLBACK wndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

struct graphics_buffer {
    HBITMAP hbm;
    uint32_t* data;
};

graphics_buffer create_graphics_buffer(int wd, int hgt)
{
    HDC hdcScreen = GetDC(NULL);

    BITMAPINFO bmi = {};
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = wd;
    bmi.bmiHeader.biHeight = -hgt; // top-down
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biCompression = BI_RGB;

    graphics_buffer gb;
    gb.hbm = CreateDIBSection(hdcScreen, &bmi, DIB_RGB_COLORS, reinterpret_cast<void**>(&gb.data), NULL, NULL);

    ReleaseDC(NULL, hdcScreen);
    return gb;
}

class graphic_buffers {
    graphics_buffer front_;
    graphics_buffer back_;
    int wd_; 
    int hgt_;

public:

    graphic_buffers(int wd, int hgt) :
        wd_(wd),
        hgt_(hgt),
        front_(create_graphics_buffer(wd, hgt)),
        back_(create_graphics_buffer(wd, hgt))
    {
        clear();
    }

    HBITMAP front_bmp() {
        return front_.hbm;
    }

    void swap() {
        std::swap(front_, back_);
    }

    size_t size() const {
        return static_cast<size_t>(wd_ * hgt_);
    }

    int width() const {
        return wd_;
    }

    int height() const {
        return hgt_;
    }

    void clear() {
        std::fill(back_.data, back_.data + size(), 0);
    }

    void set_pixel(int x, int y, uint32_t pix) {
        back_.data[y * wd_ + x] = pix;
    }

    ~graphic_buffers() {
        DeleteObject(front_.hbm);
        DeleteObject(back_.hbm);
    }
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{

    MSG msg = { 0 };
    WNDCLASS wc = { 0 };
    wc.lpfnWndProc = wndproc;
    wc.hInstance = hInstance;
    wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
    wc.lpszClassName = L"swap_buffers_window";
    if (!RegisterClass(&wc))
        return 1;

    if (!CreateWindow(wc.lpszClassName,
        L"buffered window",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        0, 0, 640, 480, 0, 0, hInstance, NULL))
        return 2;

    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

void draw_something(graphic_buffers& buffs) {
    int wd = buffs.width();
    int hgt = buffs.height();

    static int x = 0;
    static int y = 0;
    static int x_vel = 4;
    static int y_vel = 7;

    if (x >= 0 && x < wd && y >= 0 && y < hgt) {
        buffs.set_pixel(x, y, 0xffffffff);
    }

    x += x_vel;
    y += y_vel;
    if (x < 0 || x > wd) {
        x_vel *= -1;
    }
    if (y < 0 || y > hgt) {
        y_vel *= -1;
    }
}

LRESULT CALLBACK wndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    switch (message)
    {
    case WM_CREATE: {
            RECT r;
            GetClientRect(hWnd, &r);
            SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG>(new graphic_buffers(r.right - r.left, r.bottom - r.top)));
            SetTimer(hWnd, kTimerID, 1, NULL);
        }
        break;
    case WM_TIMER: {
            auto buffs = reinterpret_cast<graphic_buffers*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
            draw_something(*buffs);
            buffs->swap();
            buffs->clear();
            InvalidateRect(hWnd, NULL, FALSE);
        }
        break;
    case WM_PAINT: {
            auto buffs = reinterpret_cast<graphic_buffers*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            HDC hdc_bmp = CreateCompatibleDC(hdc);
            auto old_bmp = SelectObject(hdc_bmp, buffs->front_bmp());

            BitBlt(hdc, 0, 0, buffs->width(), buffs->height(), hdc_bmp, 0, 0, SRCCOPY);

            SelectObject(hdc, old_bmp);
            DeleteDC(hdc_bmp);
            EndPaint(hWnd, &ps);
        }
        break;

    case WM_DESTROY: {
            auto buffs = reinterpret_cast<graphic_buffers*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
            delete buffs;
        }
        break;

    case WM_CLOSE:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM