diff --git a/include/buffer.h b/include/buffer.h index 0ca6819..47d9767 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -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); diff --git a/include/swappy.h b/include/swappy.h index 0d16f01..91a20a5 100644 --- a/include/swappy.h +++ b/include/swappy.h @@ -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; diff --git a/src/application.c b/src/application.c index 962a775..e7b7a37 100644 --- a/src/application.c +++ b/src/application.c @@ -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, diff --git a/src/buffer.c b/src/buffer.c index 5b8549c..5e7106d 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -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; diff --git a/src/pixbuf.c b/src/pixbuf.c index 69c5908..d955b58 100644 --- a/src/pixbuf.c +++ b/src/pixbuf.c @@ -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; } diff --git a/src/render.c b/src/render.c index 7424838..7e1509a 100644 --- a/src/render.c +++ b/src/render.c @@ -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);