使用gtk + cairo的文本渲染滚动性能

问题描述 投票:0回答:1
// gcc `pkg-config --cflags gtk+-3.0` main.c `pkg-config --libs gtk+-3.0 cairo`

#include <gtk/gtk.h>
#include <cairo.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LINE_LENGTH 1024
#define LINE_SPACING 5
#define FONT_SIZE 20

typedef struct {
    char **lines;
    int line_count;
    int max_line_length;
} AppData;

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, AppData *data) {
    cairo_select_font_face(cr, "Monospace", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
    cairo_set_font_size(cr, FONT_SIZE);
    cairo_set_source_rgb(cr, 0, 0, 0);

    for (int i = 0; i < data->line_count; i++) {
        cairo_move_to(cr, 0, FONT_SIZE + i * (FONT_SIZE + LINE_SPACING));
        cairo_show_text(cr, data->lines[i]);
    }

    return FALSE;
}

static void on_destroy(GtkWidget *widget, gpointer data) {
    gtk_main_quit();
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        g_printerr("Usage: %s <filename>\n", argv[0]);
        return EXIT_FAILURE;
    }

    gtk_init(&argc, &argv);

    FILE *file = fopen(argv[1], "r");
    if (!file) {
        g_printerr("Could not open file: %s\n", argv[1]);
        return EXIT_FAILURE;
    }

    AppData data;
    data.lines = NULL;
    data.line_count = 0;
    data.max_line_length = 0;

    char line[MAX_LINE_LENGTH];
    while (fgets(line, sizeof(line), file)) {
        data.lines = realloc(data.lines, sizeof(char *) * (data.line_count + 1));
        if (data.lines == NULL) {
            g_printerr("Memory allocation error.\n");
            fclose(file);
            return EXIT_FAILURE;
        }
        data.lines[data.line_count] = strdup(line);
        if (strlen(line) > data.max_line_length) {
            data.max_line_length = strlen(line);
        }
        data.line_count++;
    }
    fclose(file);

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Text Renderer");
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
    gtk_window_set_decorated(GTK_WINDOW(window), TRUE);
    g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), NULL);

    GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
    GtkWidget *drawing_area = gtk_drawing_area_new();

    int height = data.line_count * (FONT_SIZE + LINE_SPACING);
    int width = data.max_line_length * (FONT_SIZE / 2);

    gtk_widget_set_size_request(drawing_area, width, height);
    gtk_widget_set_margin_top(drawing_area, 0);
    gtk_widget_set_margin_bottom(drawing_area, 0);
    gtk_widget_set_margin_start(drawing_area, 0);
    gtk_widget_set_margin_end(drawing_area, 0);

    gtk_container_add(GTK_CONTAINER(scrolled_window), drawing_area);
    gtk_container_add(GTK_CONTAINER(window), scrolled_window);

    g_signal_connect(G_OBJECT(drawing_area), "draw", G_CALLBACK(on_draw_event), &data);

    gtk_widget_show_all(window);
    gtk_main();

    return EXIT_SUCCESS;
}

在滚动时,这在区区 2M 文本文件下执行得非常糟糕

是的,我知道 Gtk.TextView 存在。 Gtk 文本小部件对于我想要做的事情来说是有限的(像文本编辑器这样的项目,具有多个光标、终端模拟器(我也知道 vte 存在)等奇特功能),而且我只是出于教育原因而想这样做。

我想专注于提高渲染性能,而不是从磁盘读取文件性能(因为,我的假设 - 渲染是主要问题)。

第一个想法是在最接近查看区域的块中渲染文本,我不知道这是否可能。

另一个想法是使用opengl而不是cairo来渲染它,我认为这不会有太大帮助。

这是我用来生成随机文本文件以测试滚动性能的命令:

dd if=/dev/urandom bs=1 count=2M | tr -dc '[:print:]\n' > random_text.txt

如有任何建议,我们将不胜感激:)

c gtk gtk3 cairo
1个回答
0
投票

是的,我知道 Gtk.TextView 存在。

您是否查看过它的实现以寻找想法?

我发现了

gtk_widget_set_overflow(widget, GTK_OVERFLOW_HIDDEN);
。我不清楚它的作用,但听起来它可能会有所帮助。

除此之外……这似乎是个坏主意。我什至不明白它是如何绘制任何东西的。

我做的另一件事是看

Gtk.ScrolledWindow
。它的文档说它对可滚动小部件有特殊支持:

直接添加具有本机滚动支持的小部件,即那些类实现了 GtkScrollable 接口的小部件。对于其他类型的小部件,GtkViewport 类充当适配器,为其他小部件提供可滚动性。

Gtk.TextView 实现了 GtkScrollable,所以...也许这是用来不绘制屏幕外部分的?

第一个想法是在最接近查看区域的块中渲染文本,我不知道这是否可能。

这听起来是个好主意。 :-)

https://docs.gtk.org/gtk3/class.DrawingArea.html,我发现:

并且该绘图被隐式剪切到暴露区域。

因此,您也许可以使用

cairo_clip_extents()
获取剪辑区域的边界框,然后“执行某些操作”以仅在此处绘制。您的绘图代码似乎知道每条线的位置及其大小(
FONT_SIZE
LINE_SPACING
)。

另一个想法是使用opengl而不是cairo来渲染它,我认为这不会有太大帮助。

是的...直觉,但我也不这么认为。

免责声明:我真的不太清楚我在说什么。我主要是猜测。

© www.soinside.com 2019 - 2024. All rights reserved.