Skip to content

Commit

Permalink
fix(ui): adjust rendering surface with proper scaling
Browse files Browse the repository at this point in the history
Closes #54
  • Loading branch information
jtheoof committed Feb 16, 2021
1 parent ecbcf2b commit 9b72571
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 36 deletions.
2 changes: 2 additions & 0 deletions include/swappy.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ struct swappy_state {
cairo_surface_t *scaled_image_surface;
cairo_surface_t *rendered_surface;

gdouble scaling_factor;

enum swappy_paint_type mode;

/* Options */
Expand Down
61 changes: 43 additions & 18 deletions src/application.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,25 @@ static void maybe_save_output_file(struct swappy_state *state) {
}
}

static void screen_coordinates_to_image_coordinates(struct swappy_state *state,
gdouble screen_x,
gdouble screen_y,
gdouble *image_x,
gdouble *image_y) {
gdouble x, y;

gint w = gdk_pixbuf_get_width(state->original_image);
gint h = gdk_pixbuf_get_height(state->original_image);

// Clamp coordinates to original image properties to avoid side effects in
// rendering pipeline
x = CLAMP(screen_x / state->scaling_factor, 0, w);
y = CLAMP(screen_y / state->scaling_factor, 0, h);

*image_x = x;
*image_y = y;
}

void on_destroy(GtkApplication *application, gpointer data) {
struct swappy_state *state = (struct swappy_state *)data;
maybe_save_output_file(state);
Expand Down Expand Up @@ -383,6 +402,16 @@ void redo_clicked_handler(GtkWidget *widget, struct swappy_state *state) {

gboolean draw_area_handler(GtkWidget *widget, cairo_t *cr,
struct swappy_state *state) {
GtkAllocation *alloc = g_new(GtkAllocation, 1);
gtk_widget_get_allocation(widget, alloc);

GdkPixbuf *image = state->original_image;
gint image_width = gdk_pixbuf_get_width(image);
gint image_height = gdk_pixbuf_get_height(image);
double scale_x = (double)alloc->width / image_width;
double scale_y = (double)alloc->height / image_height;

cairo_scale(cr, scale_x, scale_y);
cairo_set_source_surface(cr, state->rendered_surface, 0, 0);
cairo_paint(cr);

Expand All @@ -393,18 +422,6 @@ gboolean draw_area_configure_handler(GtkWidget *widget,
GdkEventConfigure *event,
struct swappy_state *state) {
g_debug("received configure_event callback");
cairo_surface_destroy(state->rendered_surface);

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;

pixbuf_scale_surface_from_widget(state, widget);

Expand All @@ -415,6 +432,10 @@ gboolean draw_area_configure_handler(GtkWidget *widget,

void draw_area_button_press_handler(GtkWidget *widget, GdkEventButton *event,
struct swappy_state *state) {
gdouble x, y;

screen_coordinates_to_image_coordinates(state, event->x, event->y, &x, &y);

if (event->button == 1) {
switch (state->mode) {
case SWAPPY_PAINT_MODE_BLUR:
Expand All @@ -423,7 +444,7 @@ void draw_area_button_press_handler(GtkWidget *widget, GdkEventButton *event,
case SWAPPY_PAINT_MODE_ELLIPSE:
case SWAPPY_PAINT_MODE_ARROW:
case SWAPPY_PAINT_MODE_TEXT:
paint_add_temporary(state, event->x, event->y, state->mode);
paint_add_temporary(state, x, y, state->mode);
render_state(state);
update_ui_undo_redo(state);
break;
Expand All @@ -434,8 +455,10 @@ void draw_area_button_press_handler(GtkWidget *widget, GdkEventButton *event,
}
void draw_area_motion_notify_handler(GtkWidget *widget, GdkEventMotion *event,
struct swappy_state *state) {
gdouble x = event->x;
gdouble y = event->y;
gdouble x, y;

screen_coordinates_to_image_coordinates(state, event->x, event->y, &x, &y);

GdkDisplay *display = gdk_display_get_default();
GdkWindow *window = event->window;
GdkCursor *crosshair = gdk_cursor_new_for_display(display, GDK_CROSSHAIR);
Expand Down Expand Up @@ -539,7 +562,7 @@ void text_size_increase_handler(GtkWidget *widget, struct swappy_state *state) {
action_text_size_increase(state);
}

static void compute_window_size(struct swappy_state *state) {
static void compute_window_size_and_scaling_factor(struct swappy_state *state) {
GdkRectangle workarea = {0};
GdkDisplay *display = gdk_display_get_default();
GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(state->ui->window));
Expand All @@ -554,6 +577,7 @@ static void compute_window_size(struct swappy_state *state) {
state->window->y = workarea.y;

double threshold = 0.75;
double scaling_factor = 1.0;

int image_width = gdk_pixbuf_get_width(state->original_image);
int image_height = gdk_pixbuf_get_height(state->original_image);
Expand All @@ -572,13 +596,14 @@ static void compute_window_size(struct swappy_state *state) {
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);
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->scaling_factor = scaling_factor;
state->window->width = scaled_width;
state->window->height = scaled_height;

Expand Down Expand Up @@ -672,7 +697,7 @@ static bool load_layout(struct swappy_state *state) {
state->ui->area = area;
state->ui->window = window;

compute_window_size(state);
compute_window_size_and_scaling_factor(state);
gtk_widget_set_size_request(area, state->window->width,
state->window->height);
action_toggle_painting_panel(state, &state->config->show_panel);
Expand Down
8 changes: 0 additions & 8 deletions src/paint.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,6 @@ void paint_update_temporary_shape(struct swappy_state *state, double x,
return;
}

int32_t width = state->window->width;
int32_t height = state->window->height;

// Bounding x and y to the window dimensions to avoid side effects in
// rendering.
x = MIN(MAX(x, 0), width);
y = MIN(MAX(y, 0), height);

switch (paint->type) {
case SWAPPY_PAINT_MODE_BLUR:
paint->can_draw = true;
Expand Down
35 changes: 25 additions & 10 deletions src/pixbuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,32 +96,47 @@ void pixbuf_scale_surface_from_widget(struct swappy_state *state,
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_surface_t *scaled_surface =
cairo_image_surface_create(format, image_width, image_height);

if (!surface) {
if (!scaled_surface) {
g_error("unable to create cairo surface from pixbuf");
goto cleanup;
goto finish;
} 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);
cr = cairo_create(scaled_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);
}

cairo_surface_t *rendered_surface =
cairo_image_surface_create(format, image_width, image_height);

if (!rendered_surface) {
g_error("unable to create rendering surface");
goto finish;
}

g_info("size of area to render: %ux%u", alloc->width, alloc->height);

finish:
if (state->scaled_image_surface) {
cairo_surface_destroy(state->scaled_image_surface);
state->scaled_image_surface = NULL;
}
state->scaled_image_surface = surface;
state->scaled_image_surface = scaled_surface;

if (state->rendered_surface) {
cairo_surface_destroy(state->rendered_surface);
state->rendered_surface = NULL;
}
state->rendered_surface = rendered_surface;

cleanup:
g_free(alloc);
}

0 comments on commit 9b72571

Please sign in to comment.