如何使用 GTK 绘制像素图?

Posted

技术标签:

【中文标题】如何使用 GTK 绘制像素图?【英文标题】:How do I draw a Pixmap with GTK? 【发布时间】:2021-04-18 06:09:25 【问题描述】:

使用 GTK3,我一直在尝试从内存缓冲区中绘制像素图。我刚刚创建了一个内存缓冲区,并用 32 位 RGBA 格式的交替颜色行填充它。我一直在尝试以下功能: gdk_pixbuf_new_from_data(const guchar *data, GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height, int rowstride, GdkPixbufDestroyNotify destroy_fn, gpointer destroy_fn_data)

使用它,我已经能够将内存缓冲区包装到 GdkPixbuf* 中,但是当我尝试使用 Cairo 将 pixbuf 绘制到屏幕上时,图像似乎失真了。

这是我一直在试验的测试程序:

#include <gtk/gtk.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

const int WIDTH = 1080;
const int HEIGHT = 720;

GtkWidget* mainWindow;

int currentCol = 0;

uint32_t* framebuffer = NULL;
GdkPixbuf* pixbuf = NULL;


typedef struct _rgbColor 
    uint8_t red;
    uint8_t green;
    uint8_t blue;
    uint8_t alpha;
rgbColor;


void onWindowDestroy (GtkWidget* object, gpointer user_data) 
    gtk_main_quit();


gboolean onTimerTick(gpointer user_data) 
    rgbColor c = 0, 0, 0, 255;
    if (currentCol == 0) 
        c.red = 255;
    
    if (currentCol == 1) 
        c.green = 255;
    
    if (currentCol == 2) 
        c.blue = 255;
        currentCol = -1;
    
    currentCol++;
    fillWithColour(framebuffer, c);

    rgbColor c1 = 0, 0, 255, 255;
    fillEveryInterval(framebuffer, c1, 20);
    gtk_widget_queue_draw(mainWindow);
    return 1;


gboolean onDraw(GtkWidget* widget, cairo_t *cr, gpointer user_data) 
    gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
    cairo_paint(cr);
    return 0;


void fillWithColour(uint32_t* fb, rgbColor c) 
    for (int y = 0; y < HEIGHT; y++) 
        for (int x = 0; x < WIDTH; x++) 
            encodePixel(fb, c, x, y);
        
    


void fillEveryInterval(uint32_t* fb, rgbColor c, int interval) 
    for (int y = 1; y < HEIGHT; y += interval) 
        for (int x = 0; x < WIDTH; x++) 
            encodePixel(fb, c, x, y);
        
    



void encodePixel(uint32_t* fb, rgbColor c, int x, int y) 
    uint32_t r, g, b, a;

    r = c.red;
    g = c.green << 8;
    b = c.blue << 16;
    a = c.alpha << 24;
    
    *(fb + (sizeof(uint32_t)*y+x)) = b | g | r | a;


int main() 
    framebuffer = malloc(sizeof(uint32_t)*WIDTH*HEIGHT);
    rgbColor c = 255, 0, 0, 255;
    fillWithColour(framebuffer, c);


    gtk_init(NULL, NULL);
    mainWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size(GTK_WINDOW(mainWindow), WIDTH, HEIGHT);
    gtk_window_set_title(GTK_WINDOW(mainWindow), "Framebuffer test");

    GtkWidget* drawingArea = gtk_drawing_area_new();

    gtk_container_add(GTK_CONTAINER(mainWindow), drawingArea);

    g_signal_connect(GTK_WINDOW(mainWindow), "destroy", (GCallback)onWindowDestroy, NULL);
    g_signal_connect(GTK_DRAWING_AREA(drawingArea), "draw", (GCallback)onDraw, NULL);

    g_timeout_add(500, onTimerTick, NULL);

    gtk_widget_show_all(GTK_WINDOW(mainWindow));
    pixbuf = gdk_pixbuf_new_from_data(framebuffer, GDK_COLORSPACE_RGB, true, 8, WIDTH, HEIGHT, WIDTH*4, NULL, NULL);
    gtk_main();

我似乎无法弄清楚是什么导致了这个问题,我尝试过消除 alpha 通道并将 RGB 值打包成 24 位,但是我也没有成功使用该方法。我认为这可能与行跨度有关,但是我无法找到纠正问题的值。我在正确的轨道上,还是有更好的方法来使用 GTK 将 RGB 缓冲区绘制到屏幕上?

【问题讨论】:

也许这会有所帮助:github.com/gaurav712/ebook_finder/blob/main/book_info.py。它在 python 中,但它做了类似的工作。查看LoadCover 类。 您可能希望删除stdlib.h 并替换为gmodule.h 并使用framebuffer = g_slice_alloc (sizeof(uint32_t) * WIDTH * HEIGHT); 进行分配,然后在完成后调用g_slice_free (uint32_t*, (void*)framebuffer); @DavidC.Rankin 在这种情况下,这只是调用mallocfree 的一种复杂方式,因为无论如何g_slice_alloc 可能都会这样做。 或者至少 g_malloc() ;) 【参考方案1】:

您只是将像素存储在错误的位置。在encodePixel 中,更改这一行:

*(fb + (sizeof(uint32_t)*y+x)) = b | g | r | a;`

到这里

fb[WIDTH*y+x] = b | g | r | a;

顺便说一句:你应该对编译器发出的许多警告做点什么。

【讨论】:

谢谢你,我不知道我怎么没看到那个,但现在它工作得很好。由于源代码中函数的顺序,这些警告很容易修复。在gdk_pixbuf_new_from_data 中将帧缓冲区转换为uint8_t * 可以消除其他警告,仅供可能遇到类似问题的任何人参考。

以上是关于如何使用 GTK 绘制像素图?的主要内容,如果未能解决你的问题,请参考以下文章

GTK+-2.0 获取窗口背景像素图

如何使用 GTK3 和 PyGObject 绘制 GdkPixbuf

canvas使用图像(绘制图、图像平铺、图像剪裁、像素处理、绘制文字)

如何使用 Cairo 和 Gtk3 在 GtkDrawingArea 中绘制一条线

如何在 Gtk3 中在开罗上下文中使用 Skia 绘制

gtkmm 绘制单个像素