0

0

RGFW 底层:软件渲染

王林

王林

发布时间:2024-08-20 10:37:16

|

670人浏览过

|

来源于dev.to

转载

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上,没有太多设置,大部分工作都是在渲染过程中完成的。

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

荔枝个人发卡系统
荔枝个人发卡系统

荔枝发卡系统乃历尽数天开发完成,原生php开发,数据库底层使用Eloquent ORM组件,模板渲染使用Smarty3.1组件,会话保持使用session开发。

下载
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 < (u32)window_height; y++) {
        for (x = 0; x < (u32)window_width; x++) {
            u32 index = (y * 4 * area.w) + x * 4;

            u8 red = bitmap->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 
#include 

#include 
#include 


#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, &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, &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, &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->data = (char*) buffer;
        #ifndef rgfw_x11_dont_convert_bgr
            u32 x, y;
            for (y = 0; y < (u32)500; y++) {
                for (x = 0; x < (u32)500; x++) {
                    u32 index = (y * 4 * 500) + x * 4;

                    u8 red = bitmap->data[index];
                    bitmap->data[index] = buffer[index + 2];
                    bitmap->data[index + 2] = red;
                }
            }
        #endif  
        xputimage(display, (window) window, gc, bitmap, 0, 0, 0, 0, 500, 500);
    }

    xdestroyimage(bitmap);
    xfreegc(display, gc);
    free(buffer);
}

视窗

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

#include 

#include 
#include 
#include 

#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(&wc);

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


    BITMAPV5HEADER bi = { 0 };
    ZeroMemory(&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*) &bi,
        DIB_RGB_COLORS,
        (void**) &buffer,
        NULL,
        (DWORD) 0);

    HDC hdcMem = CreateCompatibleDC(hdc);   

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

    MSG msg;

    BOOL running = TRUE;

    while (running) {
        if (PeekMessageA(&msg, hwnd, 0u, 0u, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&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;
}

相关专题

更多
windows查看端口占用情况
windows查看端口占用情况

Windows端口可以认为是计算机与外界通讯交流的出入口。逻辑意义上的端口一般是指TCP/IP协议中的端口,端口号的范围从0到65535,比如用于浏览网页服务的80端口,用于FTP服务的21端口等等。怎么查看windows端口占用情况呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

513

2023.07.26

查看端口占用情况windows
查看端口占用情况windows

端口占用是指与端口关联的软件占用端口而使得其他应用程序无法使用这些端口,端口占用问题是计算机系统编程领域的一个常见问题,端口占用的根本原因可能是操作系统的一些错误,服务器也可能会出现端口占用问题。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1049

2023.07.27

windows照片无法显示
windows照片无法显示

当我们尝试打开一张图片时,可能会出现一个错误提示,提示说"Windows照片查看器无法显示此图片,因为计算机上的可用内存不足",本专题为大家提供windows照片无法显示相关的文章,帮助大家解决该问题。

743

2023.08.01

windows查看端口被占用的情况
windows查看端口被占用的情况

windows查看端口被占用的情况的方法:1、使用Windows自带的资源监视器;2、使用命令提示符查看端口信息;3、使用任务管理器查看占用端口的进程。本专题为大家提供windows查看端口被占用的情况的相关的文章、下载、课程内容,供大家免费下载体验。

412

2023.08.02

windows无法访问共享电脑
windows无法访问共享电脑

在现代社会中,共享电脑是办公室和家庭的重要组成部分。然而,有时我们可能会遇到Windows无法访问共享电脑的问题。这个问题可能会导致数据无法共享,影响工作和生活的正常进行。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2343

2023.08.08

windows自动更新
windows自动更新

Windows操作系统的自动更新功能可以确保系统及时获取最新的补丁和安全更新,以提高系统的稳定性和安全性。然而,有时候我们可能希望暂时或永久地关闭Windows的自动更新功能。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

766

2023.08.10

windows boot manager
windows boot manager

windows boot manager无法开机的解决方法:1、系统文件损坏,使用Windows安装光盘或USB启动盘进入恢复环境,选择修复计算机,然后选择自动修复;2、引导顺序错误,进入恢复环境,选择命令提示符,输入命令"bootrec /fixboot"和"bootrec /fixmbr",然后重新启动计算机;3、硬件问题,使用硬盘检测工具进行扫描和修复;4、重装操作系统。本专题还提供其他解决

1480

2023.08.28

windows锁屏快捷键
windows锁屏快捷键

windows锁屏快捷键是Windows键+L、Ctrl+Alt+Del、Windows键+D、Windows键+P和Windows键+R。本专题为大家提供windows相关的文章、下载、课程内容,供大家免费下载体验。

1589

2023.08.30

桌面文件位置介绍
桌面文件位置介绍

本专题整合了桌面文件相关教程,阅读专题下面的文章了解更多内容。

0

2025.12.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Excel 教程
Excel 教程

共162课时 | 10万人学习

Laravel 8 课程精讲(台湾同胞版)
Laravel 8 课程精讲(台湾同胞版)

共22课时 | 2.3万人学习

vscode其实很简单
vscode其实很简单

共72课时 | 28.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号