在来自 gtk window 的绘制调用上缩放 cairo 上下文花费的时间太长

Scaling cairo context on draw call from gtk window takes too long

我一直在开发 gtk+/cairo 应用程序,我需要在绘图区域显示图像并在其上绘制一些文本和线条。我已经制作了将所有必要图形绘制到 cairo 上下文中的函数,但是需要缩放图像以适合绘图区域(并且还要避免在更改 window 的大小时出现缩放问题) .我计划只使用 cairo_scale 函数来做到这一点,但这个选项似乎相当慢。

以下是我目前缩放 cairo 上下文的方式:

***
typedef struct diagram_properties_{
    cairo_surface_t * surface;
    d_tank_ tanks[DIAGRAM_TANK_COUNT];
    d_valve_ valves[DIAGRAM_VALVE_COUNT];
    d_info_ infos[DIAGRAM_INFO_COUNT];
    int last_error_;
    double width, height;
    bool is_saved;
    GtkDrawingArea * drawing_area;
}diagram_properties_;
***
gboolean on_draw_diagram_event(GtkWidget *widget, cairo_t *cr, gpointer user_data){
    //Initialize diagram (d_properties)
    draw_diagram(cr, &d_properties);
    return FALSE;
}
***
void draw_diagram( cairo_t *cr, diagram_properties_ *diagram ){

    GtkWidget * da_widget = GTK_WIDGET(diagram->drawing_area);
    gtk_widget_queue_draw(da_widget);
    GtkAllocation allocation;
    gtk_widget_get_allocation(da_widget, &allocation);
    diagram->width = cairo_image_surface_get_width(diagram->surface);
    diagram->height = cairo_image_surface_get_height(diagram->surface);
    cairo_scale(cr, allocation.width / diagram->width, allocation.height /     diagram->height);
    cairo_set_source_surface(cr, diagram->surface, 0, 0);
    cairo_paint(cr);
    update_diagram_tanks( cr, diagram );
    update_diagram_valves( cr, diagram );
    update_diagram_info( cr, diagram );
}
***

带有图表的 PNG 文件用于在代码开头创建图表表面。所以我的问题是,有没有办法将 cairo 上下文保存在缓冲区中,并且仅在 window 的尺寸发生变化时才对其进行缩放?我已经在尝试缩放表面本身,以及绘图和文本的坐标,但如果有更好的方法可用,我宁愿避免这样做。

您可以结合使用 cairo_push_group()cairo_get_group_target()cairo_pop_group_to_source()+cairo_paint() 来绘制缩放后的内容。

伪代码:

static cairo_surface_t *cache_surface = NULL;
static double cache_width = -1, cache_height = -1;

// s must be an image surface and it must always be the same image surface (= content must not change)
void draw_at_size(cairo_t *cr, cairo_surface_t *s, double width, double height)
{
  double diagram_width = cairo_image_surface_get_width(s);
  double diagram_height = cairo_image_surface_get_height(s);
  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
  if (width == cache_width && height == cache_height) {
    cairo_set_source_surface(cr, cache_surface, 0, 0);
    cairo_paint(cr);
  } else {
    cairo_push_group(cr);
    cairo_scale(cr, width / diagram_width, height / diagram_height);
    cairo_set_source_surface(cr, surface, 0, 0);
    cairo_paint(cr);

    cairo_surface_destroy(cache_surface);
    cache_surface = cairo_reference(cairo_get_group_target(cr));
    cache_width = width;
    cache_height = height;

    cairo_pop_group_to_source(cr);
    cairo_paint(cr);
  }
}

不知道这是否真的更快,但这意味着表面只缩放一次。当然你也可以通过在缩放表面上绘制其他东西来扩展它。

但是,这与您自己分配另一个表面基本相同。唯一的区别是表面分配是隐藏的,你可能会得到一个 "better" 表面,例如我猜你目前正在为临时表面分配图像表面,但 cairo 会在 X11 上使用 X11 表面。