在来自 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 表面。
我一直在开发 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 表面。