From 364bce1ed0366dc3b24ed8eac12881e033858d2a Mon Sep 17 00:00:00 2001 From: Jonas Berlin Date: Fri, 29 Nov 2019 21:28:22 +0200 Subject: [PATCH] Scale icons during loading to get best quality for e.g. vector images --- src/icon.c | 65 ++++++++++++++++++++++++++++++++++++---------- src/icon.h | 19 +++----------- src/notification.c | 2 -- 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/icon.c b/src/icon.c index f24ef4b..2701a27 100644 --- a/src/icon.c +++ b/src/icon.c @@ -110,16 +110,18 @@ cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf) return icon_surface; } -GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf) -{ - ASSERT_OR_RET(pixbuf, NULL); - - int w = gdk_pixbuf_get_width(pixbuf); - int h = gdk_pixbuf_get_height(pixbuf); - int landscape = w > h; - int orig_larger = landscape ? w : h; +/** + * Scales the given image dimensions if necessary according to the settings. + * + * @param w a pointer to the image width, to be modified in-place + * @param h a pointer to the image height, to be modified in-place + */ +static bool icon_size_clamp(int *w, int *h) { + int _w = *w, _h = *h; + int landscape = _w > _h; + int orig_larger = landscape ? _w : _h; double larger = orig_larger; - double smaller = landscape ? h : w; + double smaller = landscape ? _h : _w; if (settings.min_icon_size && smaller < settings.min_icon_size) { larger = larger / smaller * settings.min_icon_size; smaller = settings.min_icon_size; @@ -129,11 +131,35 @@ GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf) larger = settings.max_icon_size; } if ((int) larger != orig_larger) { + *w = (int) (landscape ? larger : smaller); + *h = (int) (landscape ? smaller : larger); + return TRUE; + } + return FALSE; +} + +/** + * Scales the given GdkPixbuf if necessary according to the settings. + * + * @param pixbuf (nullable) The pixbuf, which may be too big. + * Takes ownership of the reference. + * @return the scaled version of the pixbuf. If scaling wasn't + * necessary, it returns the same pixbuf. Transfers full + * ownership of the reference. + */ +static GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf) +{ + ASSERT_OR_RET(pixbuf, NULL); + + int w = gdk_pixbuf_get_width(pixbuf); + int h = gdk_pixbuf_get_height(pixbuf); + + if (icon_size_clamp(&w, &h)) { GdkPixbuf *scaled; scaled = gdk_pixbuf_scale_simple(pixbuf, - (int) (landscape ? larger : smaller), - (int) (landscape ? smaller : larger), - GDK_INTERP_BILINEAR); + w, + h, + GDK_INTERP_BILINEAR); g_object_unref(pixbuf); pixbuf = scaled; } @@ -145,8 +171,19 @@ GdkPixbuf *get_pixbuf_from_file(const char *filename) { char *path = string_to_path(g_strdup(filename)); GError *error = NULL; + gint w, h; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &error); + if (!gdk_pixbuf_get_file_info (path, &w, &h)) { + LOG_W("Failed to load image info for %s", filename); + g_error_free(error); + return NULL; + } + icon_size_clamp(&w, &h); + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale(path, + w, + h, + TRUE, + &error); if (error) { LOG_W("%s", error->message); @@ -324,6 +361,8 @@ GdkPixbuf *icon_get_for_data(GVariant *data, char **id) g_free(data_chk); g_variant_unref(data_variant); + pixbuf = icon_pixbuf_scale(pixbuf); + return pixbuf; } diff --git a/src/icon.h b/src/icon.h index 37cbea0..2de0740 100644 --- a/src/icon.h +++ b/src/icon.h @@ -8,18 +8,7 @@ cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf); -/** - * Scales the given GdkPixbuf if necessary according to the settings. - * - * @param pixbuf (nullable) The pixbuf, which may be too big. - * Takes ownership of the reference. - * @return the scaled version of the pixbuf. If scaling wasn't - * necessary, it returns the same pixbuf. Transfers full - * ownership of the reference. - */ -GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf); - -/** Retrieve an icon by its full filepath. +/** Retrieve an icon by its full filepath, scaled according to settings. * * @param filename A string representing a readable file path * @@ -28,7 +17,7 @@ GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf); */ GdkPixbuf *get_pixbuf_from_file(const char *filename); -/** Retrieve an icon by its name sent via the notification bus +/** Retrieve an icon by its name sent via the notification bus, scaled according to settings * * @param iconname A string describing a `file://` URL, an arbitary filename * or an icon name, which then gets searched for in the @@ -39,7 +28,7 @@ GdkPixbuf *get_pixbuf_from_file(const char *filename); */ GdkPixbuf *get_pixbuf_from_icon(const char *iconname); -/** Read an icon from disk and convert it to a GdkPixbuf. +/** Read an icon from disk and convert it to a GdkPixbuf, scaled according to settings * * The returned id will be a unique identifier. To check if two given * GdkPixbufs are equal, it's sufficient to just compare the id strings. @@ -54,7 +43,7 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname); */ GdkPixbuf *icon_get_for_name(const char *name, char **id); -/** Convert a GVariant like described in GdkPixbuf +/** Convert a GVariant like described in GdkPixbuf, scaled according to settings * * The returned id will be a unique identifier. To check if two given * GdkPixbufs are equal, it's sufficient to just compare the id strings. diff --git a/src/notification.c b/src/notification.c index f1cc14d..6518b49 100644 --- a/src/notification.c +++ b/src/notification.c @@ -252,7 +252,6 @@ void notification_icon_replace_path(struct notification *n, const char *new_icon g_clear_pointer(&n->icon_id, g_free); n->icon = icon_get_for_name(new_icon, &n->icon_id); - n->icon = icon_pixbuf_scale(n->icon); } void notification_icon_replace_data(struct notification *n, GVariant *new_icon) @@ -264,7 +263,6 @@ void notification_icon_replace_data(struct notification *n, GVariant *new_icon) g_clear_pointer(&n->icon_id, g_free); n->icon = icon_get_for_data(new_icon, &n->icon_id); - n->icon = icon_pixbuf_scale(n->icon); } /* see notification.h */