在没有 OpenGL 的 Linux 窗口中用 C 绘制像素

Oli*_*ver 2 c linux graphics pixel

如何以最佳性能在 Linux 窗口内手动绘制像素?

我不想直接写入帧缓冲区,但我也不想使用 OpenGL 或类似的库/API 为您做所有事情。是否可以创建彩色像素的二维数组,然后将它们打印在窗口内?

像这样(但有更多颜色):

_________________
| My App      -ox|
_________________|
|RRRRGGBBRRRGGBBB|
|RRGGRGRGGRGRGGRR|
|RRGGGGBBBBRRGGBB|
|________________|
Run Code Online (Sandbox Code Playgroud)

dat*_*olf 7

\n

如何在 Linux 窗口中手动绘制像素?

\n
\n

Linux 本身不了解 Windows(或任何超出屏幕帧缓冲区的图形)。

\n

您是否正在寻址 X11、Wayland(或 Mir 或 DirectFB \xe2\x80\x93,后面两个现在几乎不使用)。

\n
\n

OpenGL或类似的库

\n
\n

OpenGL 不是一个库。它是一个 API,允许您或多或少地直接与 GPU 对话(幕后有很多簿记工作)。如果您想要更实用的 API,请使用 Vulkan。这些是使用 GPU 最直接的方法,无需编写自己的驱动程序。

\n
\n

是否可以在窗口中逐像素地写一些东西?

\n
\n

是的,但它的效率非常低,因为逐像素地执行将涉及从程序通过多个抽象层直到到达目的地的完整行程。

\n

仅发送完整的图片或请求获得对帧缓冲区的原始访问权限以直接写入它会更有效。当然,要真正提高效率,您需要利用 GPU 的功能。这需要通过 OpenGL 或 Vulkan 等 API 与其进行通信。

\n

使用 X11,您可以创建一个X MIT-SHM像素图并将其映射到进程的地址空间并直接操作其内容。要在屏幕上显示它,您可以使用 XPutImage 将其复制到窗口中。

\n

使用 Wayland,您可以获得窗口帧缓冲区本身的映射,这样您就不必执行额外的复制步骤。

\n

更新/如何将 MIT-SHM 与 Xcb 结合使用的最小示例

\n

此示例说明如何使用 X11 MIT-SHM 获取用于直接像素操作的进程地址空间帧缓冲区。基于 Xcb 教程https://xcb.freedesktop.org/tutorial/basicwindowsanddrawing/我添加了自己的代码。

\n
/* \n * compile with \n * \n * gcc -o minimal_xcb_shm_image \\\n *        minimal_xcb_shm_image.c \\\n *     -lxcb -lxcb-image -lxcb-shm\n */\n#include <stdlib.h>\n#include <stdio.h>\n\n#include <sys/ipc.h>\n#include <sys/shm.h>\n\n#include <xcb/xcb.h>\n#include <xcb/shm.h>\n#include <xcb/xcb_image.h>\n\n#if __ORDER_LITTLE_ENDIAN == __BYTE_ORDER__\n# define NATIVE_XCB_IMAGE_ORDER    XCB_IMAGE_ORDER_LSB_FIRST\n#else\n# define NATIVE_XCB_IMAGE_ORDER    XCB_IMAGE_ORDER_MSB_FIRST\n#endif\n\nenum  {\n    IMAGE_WIDTH  = 512,\n    IMAGE_HEIGHT = 512,\n    IMAGE_DEPTH  = 24,\n};\n\nstatic xcb_format_t const *query_xcb_format_for_depth(\n    xcb_connection_t *const connection,\n    unsigned depth )\n{\n    xcb_setup_t const *const setup = xcb_get_setup(connection);\n    xcb_format_iterator_t it;\n    for( it = xcb_setup_pixmap_formats_iterator(setup)\n       ; it.rem\n       ; xcb_format_next(&it)\n    ){\n        xcb_format_t const *const format = it.data;\n        if( format->depth == depth ){\n            return format;\n        }\n    }\n    return NULL;\n}\n\ntypedef struct shm_xcb_image_t {\n    xcb_connection_t *connection;\n    xcb_image_t  *image;\n    xcb_shm_seg_t shm_seg;\n    int           shm_id;\n} shm_xcb_image_t;\n\nstatic shm_xcb_image_t *shm_xcb_image_create(\n    xcb_connection_t *const connection,\n    unsigned const width,\n    unsigned const height,\n    unsigned const depth )\n{\n    xcb_generic_error_t *error = NULL;\n\n    shm_xcb_image_t *shmimg = calloc(1, sizeof(*shmimg));\n    if( !shmimg ){ goto fail; }\n    shmimg->connection = connection;\n\n    xcb_format_t const *const format = query_xcb_format_for_depth(connection, depth);\n    if( !format ){ goto fail; }\n    shmimg->image = xcb_image_create(\n        width, height,\n        XCB_IMAGE_FORMAT_Z_PIXMAP,\n        format->scanline_pad,\n        format->depth, format->bits_per_pixel, 0,\n        NATIVE_XCB_IMAGE_ORDER,\n        XCB_IMAGE_ORDER_MSB_FIRST,\n        NULL, ~0, 0);\n    if( !shmimg->image ){ goto fail; }\n\n    size_t const image_segment_size = shmimg->image->stride * shmimg->image->height;\n\n    shmimg->shm_id = shmget(IPC_PRIVATE, image_segment_size, IPC_CREAT | 0600);\n    if( 0 > shmimg->shm_id ){ goto fail; }\n\n    shmimg->image->data = shmat(shmimg->shm_id, 0, 0);\n    if( (void*)-1 == (void*)(shmimg->image->data) ){ goto fail; }\n\n    shmimg->shm_seg = xcb_generate_id(connection),\n    error = xcb_request_check(connection,\n        xcb_shm_attach_checked(\n            connection,\n            shmimg->shm_seg, shmimg->shm_id, 0) );\nfail:\n    if( shmimg ){\n        if( shmimg->image ){\n            if( shmimg->image->data && error ){\n                shmdt(shmimg->image->data);\n                shmimg->image->data = NULL;\n            }\n            if( !shmimg->image->data ){\n                shmctl(shmimg->shm_id, IPC_RMID, 0);\n                shmimg->shm_id = -1;\n            }\n        }\n        if( 0 > shmimg->shm_id ){\n            xcb_image_destroy(shmimg->image);\n            shmimg->image = NULL;\n        }\n        if( !shmimg->image ){\n            free(shmimg);\n            shmimg = NULL;\n        }\n    }\n    free(error);\n\n    return shmimg;\n}\n\nstatic void shm_xcb_image_destroy(shm_xcb_image_t *shmimg)\n{\n    xcb_shm_detach(shmimg->connection, shmimg->shm_seg);\n    shmdt(shmimg->image->data);\n    shmctl(shmimg->shm_id, IPC_RMID, 0);\n    xcb_image_destroy(shmimg->image);\n    free(shmimg);\n}\n\nstatic void generate_image(\n    shm_xcb_image_t *shmimg,\n    unsigned t )\n{\n    for( unsigned j = 0; j < shmimg->image->height; ++j ){\n        uint8_t *const line = shmimg->image->data + j * shmimg->image->stride;\n        for( unsigned i = 0; i < shmimg->image->width; ++i ){\n            unsigned const bytes_per_pixel = shmimg->image->bpp/8;\n            uint8_t *pixel = line + i * bytes_per_pixel;\n\n            unsigned const a = (i + t);\n            unsigned const b = (j + (i >> 8) & 0xFF);\n            unsigned const c = (j >> 8) & 0xFF;\n\n            switch( bytes_per_pixel ){\n            case 4: pixel[3] = 0xFF; /* fallthrough */\n            case 3: pixel[2] = a & 0xFF; /* fallthrough */\n            case 2: pixel[1] = b & 0xFF; /* fallthrough */\n            case 1: pixel[0] = c & 0xFF; /* fallthrough */\n            default: break;\n            }\n        }\n    }\n}\n\nint main(int argc, char *argv[])\n{\n    /* Open the connection to the X server */\n    xcb_connection_t *connection = xcb_connect(NULL, NULL);\n\n    /* Check that X MIT-SHM is available (should be). */\n    const xcb_query_extension_reply_t *shm_extension = xcb_get_extension_data(connection, &xcb_shm_id);\n    if( !shm_extension || !shm_extension->present ){\n        fprintf(stderr, "Query for X MIT-SHM extension failed.\\n");\n        return 1;\n    }\n\n    shm_xcb_image_t *shmimg = shm_xcb_image_create(connection, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DEPTH);\n    if( !shmimg ){\n        fprintf(stderr, "Creating shared memory image failed");\n    }\n\n    /* Get the first screen */\n    xcb_screen_t *const screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;\n\n    /* Create a window */\n    uint32_t const window_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;\n    uint32_t const window_values[] = { screen->white_pixel, XCB_EVENT_MASK_EXPOSURE};\n    xcb_drawable_t const window = xcb_generate_id(connection);\n    xcb_create_window(connection,\n        XCB_COPY_FROM_PARENT,          /* depth */\n        window,                        /* window Id */\n        screen->root,                  /* parent window */\n        0, 0,                          /* x, y */\n        IMAGE_WIDTH, IMAGE_HEIGHT,     /* width, height */\n        0,                             /* border_width */\n        XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */\n        screen->root_visual,           /* visual */\n        window_mask, window_values     /* masks */\n    );\n\n    /* Create black (foreground) graphic context */\n    xcb_gcontext_t const gc = xcb_generate_id( connection );\n    uint32_t const gc_mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;\n    uint32_t const gc_values[] = {screen->black_pixel, 0};\n    xcb_create_gc(connection, gc, window, gc_mask, gc_values);\n\n    /* Map the window on the screen and flush*/\n    xcb_map_window(connection, window);\n    xcb_flush(connection);\n\n    /* Event loop */\n    unsigned counter = 0;\n    for( xcb_generic_event_t *event\n       ; (event = xcb_wait_for_event(connection))\n       ; free(event)\n    ){\n        switch( event->response_type & ~0x80 ){\n        case XCB_EXPOSE:\n            generate_image(shmimg, counter++);\n            xcb_shm_put_image(connection, window, gc,\n                shmimg->image->width, shmimg->image->height, 0, 0,\n                shmimg->image->width, shmimg->image->height, 0, 0,\n                shmimg->image->depth, shmimg->image->format, 0, \n                shmimg->shm_seg, 0);\n\n            /* flush the request */\n            xcb_flush(connection);\n            break;\n        default:\n            /* Unknown event type, ignore it */\n            break;\n        }\n\n    }\n\n    shm_xcb_image_destroy(shmimg);\n\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n