From 1be7798a8bcfc494b20489e2e1f8b0245f4b5e84 Mon Sep 17 00:00:00 2001 From: Jeremy Attali Date: Mon, 1 Jun 2020 23:15:49 -0400 Subject: [PATCH] feat(blur): use rect blur instead of brush Use a rectangle to blur instead of brushes. Does not improve the performance that much, will come in a later patch. Will fix the UX of the feature though. Closes #17 --- README.md | 2 ++ include/application.h | 8 ++--- include/config.h | 2 +- include/swappy.h | 16 +++++----- res/swappy.ui | 8 ++--- src/application.c | 58 +++++++++++++++++------------------ src/config.c | 20 ++++++------- src/paint.c | 23 ++++++-------- src/render.c | 70 +++++++++++++++++++++---------------------- swappy.1.scd | 4 +-- 10 files changed, 103 insertions(+), 108 deletions(-) diff --git a/README.md b/README.md index ebf757f..9eb462e 100644 --- a/README.md +++ b/README.md @@ -59,12 +59,14 @@ The following lines can be used as swappy's default: ``` [Default] save_dir=$HOME/Desktop + blur_level=80 line_size=5 text_size=20 text_font=sans-serif ``` - `save_dir` is where swappshots will be saved, can contain env variables and must exist in your filesystem +- `blur_level` is the default blur level (must be between 1 and 500) - `line_size` is the default line size (must be between 1 and 50) - `text_size` is the default text size (must be between 10 and 50) - `text_font` is the font used to render text, its format is pango friendly diff --git a/include/application.h b/include/application.h index 3a162c5..4f63aa2 100644 --- a/include/application.h +++ b/include/application.h @@ -27,11 +27,9 @@ void draw_area_button_release_handler(GtkWidget *widget, GdkEventButton *event, void draw_area_motion_notify_handler(GtkWidget *widget, GdkEventMotion *event, struct swappy_state *state); -void blur_radius_decrease_handler(GtkWidget *widget, - struct swappy_state *state); -void blur_radius_increase_handler(GtkWidget *widget, - struct swappy_state *state); -void blur_radius_reset_handler(GtkWidget *widget, struct swappy_state *state); +void blur_level_decrease_handler(GtkWidget *widget, struct swappy_state *state); +void blur_level_increase_handler(GtkWidget *widget, struct swappy_state *state); +void blur_level_reset_handler(GtkWidget *widget, struct swappy_state *state); void brush_clicked_handler(GtkWidget *widget, struct swappy_state *state); void text_clicked_handler(GtkWidget *widget, struct swappy_state *state); diff --git a/include/config.h b/include/config.h index 841443d..78e31b3 100644 --- a/include/config.h +++ b/include/config.h @@ -1,6 +1,6 @@ #include "swappy.h" -#define CONFIG_BLUR_RADIUS_DEFAULT 15 +#define CONFIG_BLUR_LEVEL_DEFAULT 80 #define CONFIG_LINE_SIZE_DEFAULT 5 #define CONFIG_TEXT_FONT_DEFAULT "sans-serif" #define CONFIG_TEXT_SIZE_DEFAULT 20 diff --git a/include/swappy.h b/include/swappy.h index bbb9864..a244da6 100644 --- a/include/swappy.h +++ b/include/swappy.h @@ -19,8 +19,8 @@ #define SWAPPY_LINE_SIZE_MIN 1 #define SWAPPY_LINE_SIZE_MAX 50 -#define SWAPPY_BLUR_RADIUS_MIN 1 -#define SWAPPY_BLUR_RADIUS_MAX 50 +#define SWAPPY_BLUR_LEVEL_MIN 1 +#define SWAPPY_BLUR_LEVEL_MAX 500 #define SWAPPY_TEXT_SIZE_MIN 10 #define SWAPPY_TEXT_SIZE_MAX 50 @@ -79,13 +79,15 @@ struct swappy_paint_brush { }; struct swappy_paint_blur { - double radius; - GList *points; + double bluriness; + struct swappy_point from; + struct swappy_point to; }; struct swappy_paint { enum swappy_paint_type type; bool can_draw; + bool is_committed; union { struct swappy_paint_brush brush; struct swappy_paint_shape shape; @@ -108,7 +110,7 @@ struct swappy_state_settings { double a; double w; double t; - guint32 blur_radius; + guint32 blur_level; }; struct swappy_state_ui { @@ -134,7 +136,7 @@ struct swappy_state_ui { GtkRadioButton *custom; GtkColorButton *color; - GtkButton *blur_radius; + GtkButton *blur_level; GtkButton *line_size; GtkButton *text_size; }; @@ -186,7 +188,7 @@ struct swappy_config { char *save_dir; guint32 line_size; guint32 text_size; - guint32 blur_radius; + guint32 blur_level; char *text_font; }; diff --git a/res/swappy.ui b/res/swappy.ui index 3f4580d..b115723 100644 --- a/res/swappy.ui +++ b/res/swappy.ui @@ -701,7 +701,7 @@ True False - Blur Radius + Blur Level False @@ -716,7 +716,7 @@ True zoom-out2 True - + False @@ -730,7 +730,7 @@ False True True - + False @@ -745,7 +745,7 @@ True zoom-in2 True - + False diff --git a/src/application.c b/src/application.c index c8c058a..51e442c 100644 --- a/src/application.c +++ b/src/application.c @@ -23,10 +23,10 @@ static void update_ui_undo_redo(struct swappy_state *state) { gtk_widget_set_sensitive(redo, redo_sensitive); } -static void update_ui_blur_radius_widget(struct swappy_state *state) { - GtkButton *button = GTK_BUTTON(state->ui->blur_radius); +static void update_ui_blur_level_widget(struct swappy_state *state) { + GtkButton *button = GTK_BUTTON(state->ui->blur_level); char label[255]; - snprintf(label, 255, "%u", state->settings.blur_radius); + snprintf(label, 255, "%u", state->settings.blur_level); gtk_button_set_label(button, label); } @@ -123,31 +123,31 @@ static void switch_mode_to_blur(struct swappy_state *state) { state->mode = SWAPPY_PAINT_MODE_BLUR; } -static void action_blur_radius_decrease(struct swappy_state *state) { - guint step = state->settings.blur_radius <= 10 ? 1 : 5; +static void action_blur_level_decrease(struct swappy_state *state) { + guint step = state->settings.blur_level <= 50 ? 5 : 10; - state->settings.blur_radius -= step; + state->settings.blur_level -= step; - if (state->settings.blur_radius < SWAPPY_BLUR_RADIUS_MIN) { - state->settings.blur_radius = SWAPPY_BLUR_RADIUS_MIN; + if (state->settings.blur_level < SWAPPY_BLUR_LEVEL_MIN) { + state->settings.blur_level = SWAPPY_BLUR_LEVEL_MIN; } - update_ui_blur_radius_widget(state); + update_ui_blur_level_widget(state); } -static void action_blur_radius_increase(struct swappy_state *state) { - guint step = state->settings.blur_radius >= 10 ? 5 : 1; - state->settings.blur_radius += step; +static void action_blur_level_increase(struct swappy_state *state) { + guint step = state->settings.blur_level >= 50 ? 10 : 5; + state->settings.blur_level += step; - if (state->settings.blur_radius > SWAPPY_BLUR_RADIUS_MAX) { - state->settings.blur_radius = SWAPPY_BLUR_RADIUS_MAX; + if (state->settings.blur_level > SWAPPY_BLUR_LEVEL_MAX) { + state->settings.blur_level = SWAPPY_BLUR_LEVEL_MAX; } - update_ui_blur_radius_widget(state); + update_ui_blur_level_widget(state); } -static void action_blur_radius_reset(struct swappy_state *state) { - state->settings.blur_radius = state->config->blur_radius; +static void action_blur_level_reset(struct swappy_state *state) { + state->settings.blur_level = state->config->blur_level; - update_ui_blur_radius_widget(state); + update_ui_blur_level_widget(state); } static void action_stroke_size_decrease(struct swappy_state *state) { @@ -496,17 +496,17 @@ void draw_area_button_release_handler(GtkWidget *widget, GdkEventButton *event, } } -void blur_radius_decrease_handler(GtkWidget *widget, - struct swappy_state *state) { - action_blur_radius_decrease(state); +void blur_level_decrease_handler(GtkWidget *widget, + struct swappy_state *state) { + action_blur_level_decrease(state); } -void blur_radius_increase_handler(GtkWidget *widget, - struct swappy_state *state) { - action_blur_radius_increase(state); +void blur_level_increase_handler(GtkWidget *widget, + struct swappy_state *state) { + action_blur_level_increase(state); } -void blur_radius_reset_handler(GtkWidget *widget, struct swappy_state *state) { - action_blur_radius_reset(state); +void blur_level_reset_handler(GtkWidget *widget, struct swappy_state *state) { + action_blur_level_reset(state); } void color_red_clicked_handler(GtkWidget *widget, struct swappy_state *state) { @@ -654,7 +654,7 @@ static bool load_layout(struct swappy_state *state) { state->ui->color = GTK_COLOR_BUTTON(gtk_builder_get_object(builder, "custom-color-button")); - state->ui->blur_radius = + state->ui->blur_level = GTK_BUTTON(gtk_builder_get_object(builder, "blur-radius-button")); state->ui->line_size = GTK_BUTTON(gtk_builder_get_object(builder, "stroke-size-button")); @@ -694,7 +694,7 @@ static bool init_gtk_window(struct swappy_state *state) { return false; } - update_ui_blur_radius_widget(state); + update_ui_blur_level_widget(state); update_ui_stroke_size_widget(state); update_ui_text_size_widget(state); update_ui_undo_redo(state); @@ -721,7 +721,7 @@ static void init_settings(struct swappy_state *state) { state->settings.a = 1; state->settings.w = state->config->line_size; state->settings.t = state->config->text_size; - state->settings.blur_radius = state->config->blur_radius; + state->settings.blur_level = state->config->blur_level; } static gint command_line_handler(GtkApplication *app, diff --git a/src/config.c b/src/config.c index 6f038cb..3fdaea8 100644 --- a/src/config.c +++ b/src/config.c @@ -13,7 +13,7 @@ static void print_config(struct swappy_config *config) { g_info("printing config:"); g_info("config_dir: %s", config->config_file); g_info("save_dir: %s", config->save_dir); - g_info("blur_radius: %d", config->blur_radius); + g_info("blur_level: %d", config->blur_level); g_info("line_size: %d", config->line_size); g_info("text_font: %s", config->text_font); g_info("text_size: %d", config->text_size); @@ -69,7 +69,7 @@ static void load_config_from_file(struct swappy_config *config, const gchar *group = "Default"; gchar *save_dir = NULL; gchar *save_dir_expanded = NULL; - guint64 line_size, text_size, blur_radius; + guint64 line_size, text_size, blur_level; gchar *text_font = NULL; GError *error = NULL; @@ -140,19 +140,19 @@ static void load_config_from_file(struct swappy_config *config, error = NULL; } - blur_radius = g_key_file_get_uint64(gkf, group, "blur_radius", &error); + blur_level = g_key_file_get_uint64(gkf, group, "blur_level", &error); if (error == NULL) { - if (blur_radius >= SWAPPY_BLUR_RADIUS_MIN && - blur_radius <= SWAPPY_BLUR_RADIUS_MAX) { - config->blur_radius = blur_radius; + if (blur_level >= SWAPPY_BLUR_LEVEL_MIN && + blur_level <= SWAPPY_BLUR_LEVEL_MAX) { + config->blur_level = blur_level; } else { g_warning( - "blur_radius is not a valid value: %ld - see man page for details", - blur_radius); + "blur_level is not a valid value: %ld - see man page for details", + blur_level); } } else { - g_info("blur_radius is missing in %s (%s)", file, error->message); + g_info("blur_level is missing in %s (%s)", file, error->message); g_error_free(error); error = NULL; } @@ -177,7 +177,7 @@ static void load_default_config(struct swappy_config *config) { } config->save_dir = get_default_save_dir(); - config->blur_radius = CONFIG_BLUR_RADIUS_DEFAULT; + config->blur_level = CONFIG_BLUR_LEVEL_DEFAULT; config->line_size = CONFIG_LINE_SIZE_DEFAULT; config->text_font = g_strdup(CONFIG_TEXT_FONT_DEFAULT); config->text_size = CONFIG_TEXT_SIZE_DEFAULT; diff --git a/src/paint.c b/src/paint.c index e4e047d..1f39323 100644 --- a/src/paint.c +++ b/src/paint.c @@ -23,7 +23,6 @@ void paint_free(gpointer data) { switch (paint->type) { case SWAPPY_PAINT_MODE_BLUR: - g_list_free_full(paint->content.blur.points, g_free); break; case SWAPPY_PAINT_MODE_BRUSH: g_list_free_full(paint->content.brush.points, g_free); @@ -65,6 +64,7 @@ void paint_add_temporary(struct swappy_state *state, double x, double y, double t = state->settings.t; paint->type = type; + paint->is_committed = false; if (state->temp_paint) { if (type == SWAPPY_PAINT_MODE_TEXT) { @@ -77,14 +77,11 @@ void paint_add_temporary(struct swappy_state *state, double x, double y, switch (type) { case SWAPPY_PAINT_MODE_BLUR: - paint->can_draw = true; - - paint->content.blur.radius = state->settings.blur_radius; - point = g_new(struct swappy_point, 1); - point->x = x; - point->y = y; + paint->can_draw = false; - paint->content.blur.points = g_list_prepend(NULL, point); + paint->content.blur.bluriness = state->settings.blur_level; + paint->content.blur.from.x = x; + paint->content.blur.from.y = y; break; case SWAPPY_PAINT_MODE_BRUSH: paint->can_draw = true; @@ -152,12 +149,9 @@ void paint_update_temporary_shape(struct swappy_state *state, double x, switch (paint->type) { case SWAPPY_PAINT_MODE_BLUR: - points = paint->content.blur.points; - point = g_new(struct swappy_point, 1); - point->x = x; - point->y = y; - - paint->content.blur.points = g_list_prepend(points, point); + paint->can_draw = true; + paint->content.blur.to.x = x; + paint->content.blur.to.y = y; break; case SWAPPY_PAINT_MODE_BRUSH: points = paint->content.brush.points; @@ -267,6 +261,7 @@ void paint_commit_temporary(struct swappy_state *state) { if (!paint->can_draw) { paint_free(paint); } else { + paint->is_committed = true; state->paints = g_list_prepend(state->paints, paint); } diff --git a/src/render.c b/src/render.c index 20a4b92..162bf45 100644 --- a/src/render.c +++ b/src/render.c @@ -18,19 +18,11 @@ #define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a)[0]) -static gboolean is_point_within_circle(struct swappy_point *point, - struct swappy_point *center, - guint32 radius) { - return pow(point->x - center->x, 2) + pow(point->y - center->y, 2) < - pow(radius, 2); -} - /* * This code was largely taken from Kristian Høgsberg and Chris Wilson from: * https://www.cairographics.org/cookbook/blur.c/ */ -static void blur_image_surface_at_point(cairo_t *cr, int radius, - struct swappy_point *point) { +static void blur_paint(cairo_t *cr, struct swappy_paint_blur *blur) { cairo_surface_t *tmp; int width, height; int src_stride, dst_stride; @@ -41,7 +33,9 @@ static void blur_image_surface_at_point(cairo_t *cr, int radius, uint8_t kernel[17]; const int size = ARRAY_LENGTH(kernel); const int half = size / 2; - const int radius_extra = radius * 1.5; + double bluriness = blur->bluriness; + struct swappy_point from = blur->from; + struct swappy_point to = blur->to; cairo_surface_t *surface = cairo_get_target(cr); @@ -76,25 +70,28 @@ static void blur_image_surface_at_point(cairo_t *cr, int radius, src = cairo_image_surface_get_data(surface); src_stride = cairo_image_surface_get_stride(surface); + g_debug("sizeof(src): %lu", sizeof(src)); + g_debug("width*height*stride: %d", width * height * src_stride); + dst = cairo_image_surface_get_data(tmp); dst_stride = cairo_image_surface_get_stride(tmp); a = 0; for (i = 0; i < size; i++) { double f = i - half; - a += kernel[i] = exp(-f * f / 30.0) * 160; + a += kernel[i] = exp(-f * f / bluriness) * 80; } - int start_x = fmax(point->x - radius_extra, 0); - int start_y = fmax(point->y - radius_extra, 0); + int start_x = fmax(fmin(from.x, to.x), 0); + int start_y = fmax(fmin(from.y, to.y), 0); - int max_x = fmin(point->x + radius_extra, width); - int max_y = fmin(point->y + radius_extra, height); + int max_x = fmin(fmax(from.x, to.x), width); + int max_y = fmin(fmax(from.y, to.y), height); - for (i = start_y; i < max_y; i++) { + for (i = 0; i < height; i++) { s = (uint32_t *)(src + i * src_stride); d = (uint32_t *)(dst + i * dst_stride); - for (j = start_x; j < max_x; j++) { + for (j = 0; j < width; j++) { d[j] = s[j]; } } @@ -115,10 +112,7 @@ static void blur_image_surface_at_point(cairo_t *cr, int radius, z += ((p >> 8) & 0xff) * kernel[k]; w += ((p >> 0) & 0xff) * kernel[k]; } - struct swappy_point pixel = {.x = j, .y = i}; - if (is_point_within_circle(&pixel, point, radius)) { - d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a; - } + d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a; } } @@ -141,10 +135,7 @@ static void blur_image_surface_at_point(cairo_t *cr, int radius, z += ((p >> 8) & 0xff) * kernel[k]; w += ((p >> 0) & 0xff) * kernel[k]; } - struct swappy_point pixel = {.x = j, .y = i}; - if (is_point_within_circle(&pixel, point, radius)) { - d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a; - } + d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a; } } @@ -347,15 +338,23 @@ static void render_background(cairo_t *cr) { cairo_paint(cr); } -static void render_blur(cairo_t *cr, struct swappy_paint_blur blur) { - cairo_set_source_rgba(cr, 0, 0, 0, 1); - cairo_set_line_width(cr, 1); - - for (GList *elem = blur.points; elem; elem = elem->next) { - struct swappy_point *point = elem->data; - - blur_image_surface_at_point(cr, blur.radius, point); +static void render_blur(cairo_t *cr, struct swappy_paint_blur blur, + bool is_committed) { + if (!is_committed) { + // Blur not committed yet, draw bounding rectangle + struct swappy_paint_shape rect = { + .r = 0, + .g = 0.5, + .b = 1, + .a = 0.5, + .w = 5, + .from = blur.from, + .to = blur.to, + .type = SWAPPY_PAINT_MODE_RECTANGLE, + }; + render_shape_rectangle(cr, rect); } + blur_paint(cr, &blur); } static void render_brush(cairo_t *cr, struct swappy_paint_brush brush) { @@ -382,10 +381,9 @@ static void render_paint(cairo_t *cr, struct swappy_paint *paint) { if (!paint->can_draw) { return; } - switch (paint->type) { case SWAPPY_PAINT_MODE_BLUR: - render_blur(cr, paint->content.blur); + render_blur(cr, paint->content.blur, paint->is_committed); break; case SWAPPY_PAINT_MODE_BRUSH: render_brush(cr, paint->content.brush); @@ -399,7 +397,7 @@ static void render_paint(cairo_t *cr, struct swappy_paint *paint) { render_text(cr, paint->content.text); break; default: - g_info("unable to draw paint with type: %d", paint->type); + g_info("unable to render paint with type: %d", paint->type); break; } } diff --git a/swappy.1.scd b/swappy.1.scd index d92da06..71e5eac 100644 --- a/swappy.1.scd +++ b/swappy.1.scd @@ -62,14 +62,14 @@ The following lines can be used as swappy's default: ``` [Default] save_dir=$HOME/Desktop - blur_radius=15 + blur_level=80 line_size=5 text_size=20 text_font=sans-serif ``` - *save_dir* is where swappshots will be saved, can contain env variables and must exist in your filesystem -- *blur_raidus* is the default blur radius (must be between 1 and 50) +- *blur_level* is the default blur level (must be between 1 and 500) - *line_size* is the default line size (must be between 1 and 50) - *text_size* is the default text size (must be between 10 and 50) - *text_font* is the font used to render text, its format is pango friendly