fix(ui): compute window sizes and buffers properly

Compute maximum width and height of area container based on monitor size
and fix image ratio based on appropriate values.

Note that this will lower the resolution of the final image.

Closes #56
This commit is contained in:
Jeremy Attali 2021-02-14 21:06:41 -05:00 committed by Jeremy Attali
parent 4a0eb82369
commit 5bcffdbb01
7 changed files with 93 additions and 130 deletions

View File

@ -1,7 +0,0 @@
#pragma once
#include "swappy.h"
bool buffer_init_from_file(struct swappy_state *state);
void buffer_resize_patterns(struct swappy_state *state);
void buffer_free_all(struct swappy_state *state);

View File

@ -2,8 +2,11 @@
#include "swappy.h"
GdkPixbuf *pixbuf_init_from_file(struct swappy_state *state);
GdkPixbuf *pixbuf_get_from_state(struct swappy_state *state);
void pixbuf_save_state_to_folder(GdkPixbuf *pixbuf, char *folder,
char *filename_format);
void pixbuf_save_to_file(GdkPixbuf *pixbuf, char *file);
void pixbuf_save_to_stdout(GdkPixbuf *pixbuf);
void pixbuf_scale_surface_from_widget(struct swappy_state *state,
GtkWidget *widget);

View File

@ -148,6 +148,7 @@ struct swappy_state {
struct swappy_state_ui *ui;
struct swappy_config *config;
GdkPixbuf *original_image;
cairo_surface_t *original_image_surface;
cairo_surface_t *scaled_image_surface;
cairo_surface_t *rendered_surface;
@ -165,8 +166,6 @@ struct swappy_state {
struct swappy_box *window;
struct swappy_box *geometry;
cairo_rectangle_int_t *drawing_area_rect;
GList *paints;
GList *redo_paints;
struct swappy_paint *temp_paint;

View File

@ -59,7 +59,6 @@ executable(
'src/main.c',
'src/algebra.c',
'src/application.c',
'src/buffer.c',
'src/box.c',
'src/config.c',
'src/clipboard.c',

View File

@ -5,7 +5,6 @@
#include <stdio.h>
#include <time.h>
#include "buffer.h"
#include "clipboard.h"
#include "config.h"
#include "file.h"
@ -231,8 +230,6 @@ 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);
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);
@ -247,6 +244,7 @@ void application_finish(struct swappy_state *state) {
g_free(state->geometry);
g_free(state->window);
g_free(state->ui);
g_object_unref(state->original_image);
g_object_unref(state->app);
config_free(state);
@ -394,27 +392,21 @@ gboolean draw_area_handler(GtkWidget *widget, cairo_t *cr,
gboolean draw_area_configure_handler(GtkWidget *widget,
GdkEventConfigure *event,
struct swappy_state *state) {
g_debug("received configure_event handler");
g_debug("received configure_event callback");
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));
g_info("size of drawing area surface: %ux%u",
cairo_image_surface_get_width(surface),
cairo_image_surface_get_height(surface));
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));
g_info("size of area to render: %ux%u", alloc->width, alloc->height);
pixbuf_scale_surface_from_widget(state, widget);
render_state(state);
@ -562,18 +554,34 @@ static void compute_window_size(struct swappy_state *state) {
state->window->y = workarea.y;
double threshold = 0.75;
double scaling = 1.0;
if (state->geometry->width > workarea.width * threshold) {
scaling = workarea.width * threshold / state->geometry->width;
} else if (state->geometry->height > workarea.height * threshold) {
scaling = workarea.height * threshold / state->geometry->height;
int image_width = gdk_pixbuf_get_width(state->original_image);
int image_height = gdk_pixbuf_get_height(state->original_image);
int max_width = workarea.width * threshold;
int max_height = workarea.height * threshold;
g_info("size of image: %ux%u", image_width, image_height);
g_info("size of monitor at window: %ux%u", workarea.width, workarea.height);
g_info("maxium size allowed for window: %ux%u", max_width, max_height);
int scaled_width = image_width;
int scaled_height = image_height;
double scaling_factor_width = (double)max_width / image_width;
double scaling_factor_height = (double)max_height / image_height;
if (scaling_factor_height < 1.0 || scaling_factor_width < 1.0) {
double scaling_factor = MIN(scaling_factor_width, scaling_factor_height);
scaled_width = image_width * scaling_factor;
scaled_height = image_height * scaling_factor;
g_info("rendering area will be scaled by a factor of: %.2lf",
scaling_factor);
}
state->window->width = state->geometry->width * scaling;
state->window->height = state->geometry->height * scaling;
state->window->width = scaled_width;
state->window->height = scaled_height;
g_info("size of monitor at window: %ux%u", workarea.width, workarea.height);
g_info("size of window to render: %ux%u", state->window->width,
state->window->height);
}
@ -675,8 +683,8 @@ static bool load_layout(struct swappy_state *state) {
}
static bool init_gtk_window(struct swappy_state *state) {
if (!state->geometry) {
g_critical("no geometry found, did you use -f option?");
if (!state->original_image) {
g_critical("original image not loaded");
return false;
}
@ -725,7 +733,7 @@ static gint command_line_handler(GtkApplication *app,
state->temp_file_str = temp_file_str;
}
if (!buffer_init_from_file(state)) {
if (!pixbuf_init_from_file(state)) {
return EXIT_FAILURE;
}
}

View File

@ -1,94 +0,0 @@
#include "buffer.h"
#include <cairo.h>
#include "box.h"
#include "swappy.h"
bool buffer_init_from_file(struct swappy_state *state) {
char *file =
state->temp_file_str != NULL ? state->temp_file_str : state->file_str;
g_info("creating cairo image surface from file: %s", file);
cairo_surface_t *surface = cairo_image_surface_create_from_png(file);
cairo_status_t status = cairo_surface_status(surface);
if (status) {
g_warning("error while loading png file: %s - cairo status: %s", file,
cairo_status_to_string(status));
return false;
}
int width = cairo_image_surface_get_width(surface);
int height = cairo_image_surface_get_height(surface);
struct swappy_box *geometry = g_new(struct swappy_box, 1);
geometry->x = 0;
geometry->y = 0;
geometry->width = (int32_t)width;
geometry->height = (int32_t)height;
g_info("size of image: %dx%d", width, height);
state->geometry = geometry;
cairo_pattern_t *output_pattern = cairo_pattern_create_for_surface(surface);
state->patterns = g_list_append(state->patterns, output_pattern);
state->original_image_surface = surface;
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);
}
static void free_pattern(gpointer data) {
cairo_pattern_t *pattern = data;
cairo_pattern_destroy(pattern);
}
void buffer_free_all(struct swappy_state *state) {
if (state->patterns) {
g_list_free_full(state->patterns, free_pattern);
state->patterns = NULL;
}
}

View File

@ -63,6 +63,21 @@ void pixbuf_save_to_stdout(GdkPixbuf *pixbuf) {
g_object_unref(out);
}
GdkPixbuf *pixbuf_init_from_file(struct swappy_state *state) {
GError *error = NULL;
char *file =
state->temp_file_str != NULL ? state->temp_file_str : state->file_str;
GdkPixbuf *image = gdk_pixbuf_new_from_file(file, &error);
if (error != NULL) {
g_error("unable to load file: %s - reason: %s", file, error->message);
return NULL;
}
state->original_image = image;
return image;
}
void pixbuf_save_to_file(GdkPixbuf *pixbuf, char *file) {
if (g_strcmp0(file, "-") == 0) {
pixbuf_save_to_stdout(pixbuf);
@ -70,3 +85,43 @@ void pixbuf_save_to_file(GdkPixbuf *pixbuf, char *file) {
write_file(pixbuf, file);
}
}
void pixbuf_scale_surface_from_widget(struct swappy_state *state,
GtkWidget *widget) {
GtkAllocation *alloc = g_new(GtkAllocation, 1);
GdkPixbuf *image = state->original_image;
gtk_widget_get_allocation(widget, alloc);
gboolean has_alpha = gdk_pixbuf_get_has_alpha(image);
cairo_format_t format = has_alpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24;
gint image_width = gdk_pixbuf_get_width(image);
gint image_height = gdk_pixbuf_get_height(image);
cairo_surface_t *surface =
cairo_image_surface_create(format, image_width, image_height);
if (!surface) {
g_error("unable to create cairo surface from pixbuf");
goto cleanup;
} else {
cairo_t *cr;
cr = cairo_create(surface);
double scale_x = (double)alloc->width / image_width;
double scale_y = (double)alloc->height / image_height;
g_info("image scaled on x,y: %.2lf,%.2lf", scale_x, scale_y);
cairo_scale(cr, scale_x, scale_y);
gdk_cairo_set_source_pixbuf(cr, image, 0, 0);
cairo_paint(cr);
cairo_destroy(cr);
}
g_info("size of area to render: %ux%u", alloc->width, alloc->height);
if (state->scaled_image_surface) {
cairo_surface_destroy(state->scaled_image_surface);
state->scaled_image_surface = NULL;
}
state->scaled_image_surface = surface;
cleanup:
g_free(alloc);
}