perf(render): improve rendering for scaled surfaces

This commit is contained in:
Jeremy Attali 2020-06-14 16:46:22 -04:00
parent f405d5a473
commit 0e9f66ad5b
6 changed files with 83 additions and 46 deletions

View File

@ -12,4 +12,6 @@ bool buffer_init_from_file(struct swappy_state *state);
bool buffer_parse_geometry(struct swappy_state *state);
void buffer_resize_patterns(struct swappy_state *state);
void buffer_free_all(struct swappy_state *state);

View File

@ -196,7 +196,10 @@ struct swappy_state {
struct swappy_config *config;
struct swappy_wayland *wl;
cairo_surface_t *cairo_surface;
cairo_surface_t *original_image_surface;
cairo_surface_t *scaled_image_surface;
cairo_surface_t *rendered_surface;
GList *patterns; // List of cairo_pattern_t
enum swappy_paint_type mode;
@ -208,6 +211,9 @@ struct swappy_state {
struct swappy_box *window;
struct swappy_box *geometry;
cairo_rectangle_int_t *drawing_area_rect;
gint scaling_factor;
GList *paints;

View File

@ -13,13 +13,6 @@
#include "swappy.h"
#include "wayland.h"
static void on_area_size_allocate(GtkWidget *area, struct swappy_state *state) {
GtkAllocation *alloc = g_new(GtkAllocation, 1);
gtk_widget_get_allocation(area, alloc);
g_info("size of area to render: %ux%u", alloc->width, alloc->height);
g_free(alloc);
}
static void update_ui_undo_redo(struct swappy_state *state) {
GtkWidget *undo = GTK_WIDGET(state->ui->undo);
GtkWidget *redo = GTK_WIDGET(state->ui->redo);
@ -237,7 +230,10 @@ void blur_clicked_handler(GtkWidget *widget, struct swappy_state *state) {
void application_finish(struct swappy_state *state) {
paint_free_all(state);
buffer_free_all(state);
cairo_surface_destroy(state->cairo_surface);
g_free(state->drawing_area_rect);
cairo_surface_destroy(state->rendered_surface);
cairo_surface_destroy(state->original_image_surface);
cairo_surface_destroy(state->scaled_image_surface);
g_free(state->file_str);
g_free(state->geometry_str);
g_free(state->geometry);
@ -382,7 +378,7 @@ void redo_clicked_handler(GtkWidget *widget, struct swappy_state *state) {
gboolean draw_area_handler(GtkWidget *widget, cairo_t *cr,
struct swappy_state *state) {
cairo_set_source_surface(cr, state->cairo_surface, 0, 0);
cairo_set_source_surface(cr, state->rendered_surface, 0, 0);
cairo_paint(cr);
return FALSE;
@ -392,19 +388,26 @@ gboolean draw_area_configure_handler(GtkWidget *widget,
GdkEventConfigure *event,
struct swappy_state *state) {
g_debug("received configure_event handler");
cairo_surface_destroy(state->cairo_surface);
cairo_surface_destroy(state->rendered_surface);
g_free(state->drawing_area_rect);
cairo_surface_t *surface = gdk_window_create_similar_surface(
gtk_widget_get_window(widget), CAIRO_CONTENT_COLOR_ALPHA,
gtk_widget_get_allocated_width(widget),
gtk_widget_get_allocated_height(widget));
state->rendered_surface = surface;
GtkAllocation *alloc = g_new(GtkAllocation, 1);
gtk_widget_get_allocation(widget, alloc);
state->drawing_area_rect = alloc;
buffer_resize_patterns(state);
g_info("size of cairo_surface: %ux%u with type: %d",
cairo_image_surface_get_width(surface),
cairo_image_surface_get_height(surface),
cairo_image_surface_get_format(surface));
state->cairo_surface = surface;
g_info("size of area to render: %ux%u", alloc->width, alloc->height);
render_state(state);
@ -656,9 +659,6 @@ static bool load_layout(struct swappy_state *state) {
state->ui->area = area;
state->ui->window = window;
g_signal_connect(area, "size-allocate", G_CALLBACK(on_area_size_allocate),
state);
compute_window_size(state);
gtk_widget_set_size_request(area, state->window->width,

View File

@ -275,8 +275,7 @@ bool buffer_init_from_file(struct swappy_state *state) {
cairo_pattern_t *output_pattern = cairo_pattern_create_for_surface(surface);
state->patterns = g_list_append(state->patterns, output_pattern);
cairo_surface_destroy(surface);
state->original_image_surface = surface;
return true;
}
@ -294,6 +293,46 @@ bool buffer_parse_geometry(struct swappy_state *state) {
return true;
}
static void scale_pattern(gpointer data, gpointer user_data) {
struct swappy_state *state = (struct swappy_state *)user_data;
cairo_pattern_t *pattern = (cairo_pattern_t *)data;
int image_width, image_height;
int rendered_width, rendered_height;
image_width = state->geometry->width;
image_height = state->geometry->height;
rendered_width = state->drawing_area_rect->width;
rendered_height = state->drawing_area_rect->height;
cairo_surface_t *scaled = cairo_surface_create_similar(
state->rendered_surface, CAIRO_CONTENT_COLOR_ALPHA, rendered_width,
rendered_height);
cairo_t *cr = cairo_create(scaled);
double sx = (double)rendered_width / image_width;
double sy = (double)rendered_height / image_height;
cairo_matrix_t matrix;
cairo_matrix_init_scale(&matrix, 1.0 / sx, 1.0 / sy);
cairo_pattern_set_matrix(pattern, &matrix);
cairo_set_source_surface(cr, state->original_image_surface, 0, 0);
cairo_set_source(cr, pattern);
cairo_paint(cr);
cairo_destroy(cr);
if (state->scaled_image_surface) {
cairo_surface_destroy(state->scaled_image_surface);
}
state->scaled_image_surface = scaled;
}
void buffer_resize_patterns(struct swappy_state *state) {
g_list_foreach(state->patterns, scale_pattern, state);
}
void buffer_wayland_destroy(struct swappy_buffer *buffer) {
if (buffer == NULL) {
return;

View File

@ -6,10 +6,10 @@
#include "notification.h"
GdkPixbuf *pixbuf_get_from_state(struct swappy_state *state) {
guint width = cairo_image_surface_get_width(state->cairo_surface);
guint height = cairo_image_surface_get_height(state->cairo_surface);
guint width = cairo_image_surface_get_width(state->rendered_surface);
guint height = cairo_image_surface_get_height(state->rendered_surface);
GdkPixbuf *pixbuf =
gdk_pixbuf_get_from_surface(state->cairo_surface, 0, 0, width, height);
gdk_pixbuf_get_from_surface(state->rendered_surface, 0, 0, width, height);
return pixbuf;
}

View File

@ -335,29 +335,6 @@ static void render_shape(cairo_t *cr, struct swappy_paint_shape shape) {
cairo_restore(cr);
}
static void render_buffers(cairo_t *cr, struct swappy_state *state) {
if (!state->patterns) {
return;
}
cairo_save(cr);
double sx = (double)state->window->width / state->geometry->width;
double sy = (double)state->window->height / state->geometry->height;
// g_debug("scaling cairo context: (%.2lf, %.2lf)", sx, sy);
cairo_scale(cr, sx, sy);
for (GList *elem = state->patterns; elem; elem = elem->prev) {
cairo_pattern_t *pattern = elem->data;
cairo_set_source(cr, pattern);
cairo_paint(cr);
}
cairo_restore(cr);
}
static void render_background(cairo_t *cr, struct swappy_state *state) {
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_paint(cr);
@ -434,6 +411,19 @@ static void render_brush(cairo_t *cr, struct swappy_paint_brush brush) {
}
}
static void render_image(cairo_t *cr, struct swappy_state *state) {
cairo_surface_t *surface = state->scaled_image_surface;
cairo_save(cr);
if (surface && !cairo_surface_status(surface)) {
cairo_set_source_surface(cr, surface, 0, 0);
cairo_paint(cr);
}
cairo_restore(cr);
}
static void render_paint(cairo_t *cr, struct swappy_paint *paint) {
if (!paint->can_draw) {
return;
@ -471,11 +461,11 @@ static void render_paints(cairo_t *cr, struct swappy_state *state) {
}
void render_state(struct swappy_state *state) {
cairo_surface_t *surface = state->cairo_surface;
cairo_surface_t *surface = state->rendered_surface;
cairo_t *cr = cairo_create(surface);
render_background(cr, state);
render_buffers(cr, state);
render_image(cr, state);
render_paints(cr, state);
cairo_destroy(cr);