卓越飞翔博客卓越飞翔博客

卓越飞翔 - 您值得收藏的技术分享站
技术文章75303本站已运行4311

RGFW 底层:软件渲染

rgfw 底层:软件渲染

介绍

rgfw是一个轻量级的单头窗口库,它的源代码可以在这里找到。
本教程基于其源代码。

软件渲染的基本思想很简单。归根结底就是绘制到缓冲区并将其传输到屏幕。
然而,使用低级 api 时软件渲染会更加复杂,因为您必须
正确初始化渲染上下文,告诉 api 如何期望数据。然后要绘制就必须使用api​​的函数
blit 到屏幕,这可能很复杂。

本教程解释了rgfw如何处理软件渲染,以便您可以了解如何自己实现。

注意:macos 代码将在编写时考虑到 cocoa c 包装器(请参阅 rgfw.h 或 silicon.h)

概述

所需步骤的快速概述

  1. 初始化缓冲区和渲染上下文
  2. 绘制到缓冲区
  3. 将缓冲区传输到屏幕
  4. 免费剩余数据

第 1 步(初始化缓冲区和渲染上下文)

注意:您可能希望缓冲区的大小大于窗口,这样您就可以缩放缓冲区的大小而无需重新分配它。

在 x11 上,您首先创建一个视觉(或像素格式)来告诉窗口如何处理绘制数据。
然后为缓冲区创建一个位图来渲染,rgfw 使用 ximage 结构作为位图。
接下来,您使用显示和窗口数据创建图形上下文 (gc)。 gc是用来告诉x11如何给
窗口绘制数据。

这也是你可以分配缓冲区的地方。必须为除 windows 之外的每个平台分配缓冲区。

为此,您需要使用 xmatchvisualinfo、xcreateimage 和 xcreategc

xvisualinfo vi;
vi.visual = defaultvisual(display, defaultscreen(display));

xmatchvisualinfo(display, defaultscreen(display), 32, truecolor, &vi);

ximage* bitmap = xcreateimage(
            display, xdefaultvisual(display, vi.screen),
            vi.depth,
            zpixmap, 0, null, rgfw_buffersize.w, rgfw_buffersize.h,
                32, 0
);

/* ..... */
/* now this visual can be used to create a window and colormap */

xsetwindowattributes swa;
colormap cmap;

swa.colormap = cmap = xcreatecolormap((display*) display, defaultrootwindow(display), vi.visual, allocnone);

swa.background_pixmap = none;
swa.border_pixel = 0;
swa.event_mask = event_mask;

swa.background_pixel = 0;

window window = xcreatewindow((display*) display, defaultrootwindow((display*) display), x, y, w, h,
                0, vi.depth, inputoutput, vi.visual,
                cwcolormap | cwborderpixel | cwbackpixel | cweventmask, &swa);
/* .... */

gc gc = xcreategc(display, window, 0, null);

u8* buffer = (u8*)malloc(rgfw_buffersize.w * rgfw_buffersize.h * 4);

在 windows 上,您将首先创建位图标头,该标头用于创建指定格式的位图。
格式结构用于告诉 windows api 如何将缓冲区渲染到屏幕上。

接下来,创建一个分配在内存中的绘图上下文句柄(hdc),用于稍后选择位图。

注意:windows 不需要分配缓冲区,因为 winapi 会为我们处理该内存。您也可以手动分配内存。

相关文档:bitmapv5header、createdibsection 和 createcompatibledc

bitmapv5header bi;
zeromemory(&bi, sizeof(bi));
bi.bv5size = sizeof(bi);
bi.bv5width = rgfw_buffersize.w;
bi.bv5height = -((long) rgfw_buffersize.h);
bi.bv5planes = 1;
bi.bv5bitcount = 32;
bi.bv5compression = bi_bitfields;

// where it can expect to find the rgba data
// (note: this might need to be changed according to the endianness) 
bi.bv5bluemask = 0x00ff0000;
bi.bv5greenmask = 0x0000ff00;
bi.bv5redmask = 0x000000ff;
bi.bv5alphamask = 0xff000000;

u8* buffer;

hbitmap bitmap = createdibsection(hdc,
    (bitmapinfo*) &bi,
    dib_rgb_colors,
    (void**) &buffer,
    null,
    (dword) 0);

hdc hdcmem = createcompatibledc(hdc);

在macos上,没有太多设置,大部分工作都是在渲染过程中完成的。

你只需要分配缓冲区数据即可。

u8* buffer = malloc(rgfw_buffersize.w * rgfw_buffersize.h * 4);

第2步(绘制到缓冲区)

在本教程中,我将使用 silk.h 绘制到缓冲区。 silk.h是一个单头软件渲染图形库。

首先包括丝绸,

#define silk_pixelbuffer_width w
#define silk_pixelbuffer_height h
#define silk_implementation
#include "silk.h"

现在可以使用silk渲染了。

84646715536​​5

步骤 3(将缓冲区传输到屏幕)

在x11上,首先将位图数据设置到缓冲区。
位图数据将使用bgr渲染,所以你必须

如果要使用 rgb,请转换数据。那你就得用xputimage
使用gc将ximage绘制到窗口。

相关文档:xputimage

bitmap->data = (char*) buffer;
#ifndef rgfw_x11_dont_convert_bgr
    u32 x, y;
    for (y = 0; y data[index];
            bitmap->data[index] = buffer[index + 2];
            bitmap->data[index + 2] = red;
        }
    }
#endif  
xputimage(display, (window)window, gc, bitmap, 0, 0, 0, 0, rgfw_buffersize.w, rgfw_buffersize.h);

在 windows 上,您必须首先选择位图并确保保存最后选择的对象,以便稍后可以重新选择它。
现在您可以将位图传输到屏幕并重新选择旧位图。

相关文档:selectobject 和 bitblt

hgdiobj oldbmp = selectobject(hdcmem, bitmap);
bitblt(hdc, 0, 0, window_width, window_height, hdcmem, 0, 0, srccopy);
selectobject(hdcmem, oldbmp);

在macos上,根据您的窗口设置视图的calayer,这用于将图像渲染到屏幕上。
接下来,使用缓冲区创建图像(位图)。
最后,您可以将图像添加到图层的图形上下文中,并将图层绘制并刷新到屏幕上。

相关文档:cgcolorspacecreatedevicergb、cgbitmapcontextcreate、cgbitmapcontextcreateimage、cgcolorspacerelease、cgcontextrelease、
calayer、nsgraphicscontext、cgcontextdrawimage、flushgraphics 和 cgi​​magerelease

cgimageref createimagefrombytes(unsigned char *buffer, int width, int height) {
    // define color space
    cgcolorspaceref colorspace = cgcolorspacecreatedevicergb();
    // create bitmap context
    cgcontextref context = cgbitmapcontextcreate(
            buffer, 
            width, height,
            8,
            rgfw_buffersize.w * 4, 
            colorspace,
            kcgimagealphapremultipliedlast);

    // create image from bitmap context
    cgimageref image = cgbitmapcontextcreateimage(context);
    // release the color space and context
    cgcolorspacerelease(colorspace);
    cgcontextrelease(context);

    return image;
}

...
void* view = nswindow_contentview(window);
void* layer = objc_msgsend_id(view, sel_registername("layer"));

((void(*)(id, sel, nsrect))objc_msgsend)(layer,
                sel_registername("setframe:"),
                (nsrect){{0, 0}, {window_width, window_height}});

cgimageref image = createimagefrombytes(buffer, window_width, window_height);

// get the current graphics context
id graphicscontext = objc_msgsend_class(objc_getclass("nsgraphicscontext"), sel_registername("currentcontext"));

// get the cgcontext from the current nsgraphicscontext
id cgcontext = objc_msgsend_id(graphicscontext, sel_registername("graphicsport"));

// draw the image in the context
nsrect bounds = (nsrect){{0,0}, {window_width, window_height}};
cgcontextdrawimage((void*)cgcontext, *(cgrect*)&bounds, image);

// flush the graphics context to ensure the drawing is displayed
objc_msgsend_id(graphicscontext, sel_registername("flushgraphics"));

objc_msgsend_void_id(layer, sel_registername("setcontents:"), (id)image);
objc_msgsend_id(layer, sel_registername("setneedsdisplay"));

cgimagerelease(image);

步骤 4(免费剩余数据)

渲染完成后,您应该使用相应的api函数释放位图和图像数据。

在 x11 和 macos 上,您还应该释放缓冲区。

在x11上你必须使用xdestoryimage和xfreegc。

2882​​54941586

在windows上,必须使用deletedc和deleteobject。

deletedc(hdcmem);
deleteobject(bitmap);

macos 上必须使用release.

release(bitmap);
release(image);
free(buffer);

完整的例子

x11

// this can be compiled with 
// gcc x11.c -lx11 -lm

#include <x11>
#include <x11>

#include <stdio.h>
#include <stdlib.h>


#define silk_pixelbuffer_width 500
#define silk_pixelbuffer_height 500
#define silk_implementation
#include "silk.h"

int main() {
    display* display = xopendisplay(null);
    xvisualinfo vi;
    vi.visual = defaultvisual(display, defaultscreen(display));

    xmatchvisualinfo(display, defaultscreen(display), 32, truecolor, &amp;vi);

    ximage* bitmap = xcreateimage(
            display, xdefaultvisual(display, vi.screen),
            vi.depth,
            zpixmap, 0, null, 500, 500,
            32, 0
    );

    /* ..... */
    /* now this visual can be used to create a window and colormap */

    xsetwindowattributes swa;
    colormap cmap;

    swa.colormap = cmap = xcreatecolormap((display*) display, defaultrootwindow(display), vi.visual, allocnone);

    swa.background_pixmap = none;
    swa.border_pixel = 0;
    swa.event_mask = cwcolormap | cwborderpixel | cwbackpixel | cweventmask;

    swa.background_pixel = 0;

    window window = xcreatewindow((display*) display, defaultrootwindow((display*) display), 500, 500, 500, 500,
                    0, vi.depth, inputoutput, vi.visual,
                    cwcolormap | cwborderpixel | cwbackpixel | cweventmask, &amp;swa);
    /* .... */

    gc gc = xcreategc(display, window, 0, null);

    u8* buffer = (u8*)malloc(500 * 500 * 4);

    xselectinput(display, window, exposuremask | keypressmask);
    xmapwindow(display, window);

    xevent event;
    for (;;) {
        xnextevent(display, &amp;event);

        silkclearpixelbuffercolor((pixel*)buffer, 0x11aa0033);

        silkdrawcircle(
                (pixel*)buffer, 
                (vec2i) { silk_pixelbuffer_width, silk_pixelbuffer_height },
                silk_pixelbuffer_width,
                (vec2i) { silk_pixelbuffer_center_x, silk_pixelbuffer_center_y - 60}, 
                60,
                0xff0000ff
        );

        bitmap-&gt;data = (char*) buffer;
        #ifndef rgfw_x11_dont_convert_bgr
            u32 x, y;
            for (y = 0; y data[index];
                    bitmap-&gt;data[index] = buffer[index + 2];
                    bitmap-&gt;data[index + 2] = red;
                }
            }
        #endif  
        xputimage(display, (window) window, gc, bitmap, 0, 0, 0, 0, 500, 500);
    }

    xdestroyimage(bitmap);
    xfreegc(display, gc);
    free(buffer);
}
</stdlib.h></stdio.h></x11></x11>

视窗

// This can be compiled with
// gcc win32.c -lgdi32 -lm

#include <windows.h>

#include <stdio.h>
#include <stdint.h>
#include <assert.h>

#define SILK_PIXELBUFFER_WIDTH 500
#define SILK_PIXELBUFFER_HEIGHT 500
#define SILK_IMPLEMENTATION
#include "silk.h"

int main() {
    WNDCLASS wc = {0};
    wc.lpfnWndProc   = DefWindowProc; // Default window procedure
    wc.hInstance     = GetModuleHandle(NULL);
    wc.lpszClassName = "SampleWindowClass";

    RegisterClass(&amp;wc);

    HWND hwnd = CreateWindowA(wc.lpszClassName, "Sample Window", 0,
            500, 500, 500, 500,
            NULL, NULL, wc.hInstance, NULL);


    BITMAPV5HEADER bi = { 0 };
    ZeroMemory(&amp;bi, sizeof(bi));
    bi.bV5Size = sizeof(bi);
    bi.bV5Width = 500;
    bi.bV5Height = -((LONG) 500);
    bi.bV5Planes = 1;
    bi.bV5BitCount = 32;
    bi.bV5Compression = BI_BITFIELDS;

        // where it can expect to find the RGB data
    // (note: this might need to be changed according to the endianness) 
    bi.bV5BlueMask = 0x00ff0000;
    bi.bV5GreenMask = 0x0000ff00;
    bi.bV5RedMask = 0x000000ff;
    bi.bV5AlphaMask = 0xff000000;

    u8* buffer;

    HDC hdc = GetDC(hwnd); 
    HBITMAP bitmap = CreateDIBSection(hdc,
        (BITMAPINFO*) &amp;bi,
        DIB_RGB_COLORS,
        (void**) &amp;buffer,
        NULL,
        (DWORD) 0);

    HDC hdcMem = CreateCompatibleDC(hdc);   

    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);

    MSG msg;

    BOOL running = TRUE;

    while (running) {
        if (PeekMessageA(&amp;msg, hwnd, 0u, 0u, PM_REMOVE)) {
            TranslateMessage(&amp;msg);
            DispatchMessage(&amp;msg);
        }

        running = IsWindow(hwnd);

        silkClearPixelBufferColor((pixel*)buffer, 0x11AA0033);

        silkDrawCircle(
            (pixel*)buffer, 
            (vec2i) { SILK_PIXELBUFFER_WIDTH, SILK_PIXELBUFFER_HEIGHT },
            SILK_PIXELBUFFER_WIDTH,
            (vec2i) { SILK_PIXELBUFFER_CENTER_X, SILK_PIXELBUFFER_CENTER_Y - 60}, 
            60,
            0xff0000ff
        );

        HGDIOBJ oldbmp = SelectObject(hdcMem, bitmap);
        BitBlt(hdc, 0, 0, 500, 500, hdcMem, 0, 0, SRCCOPY);
        SelectObject(hdcMem, oldbmp);
    }

    DeleteDC(hdcMem);
    DeleteObject(bitmap);
    return 0;
}
</assert.h></stdint.h></stdio.h></windows.h>
卓越飞翔博客
上一篇: vue2和vue3哪个用的多
下一篇: 返回列表
留言与评论(共有 0 条评论)
   
验证码:
隐藏边栏