This is an automated email from the git hooks/post-receive script. eric pushed a commit to branch master in repository xfce/xfdesktop.
commit 7e8e8669606e662d1ee1e85815000f068f48acd9 Author: Matias De lellis <mati8...@gmail.com> Date: Tue Oct 14 18:47:04 2014 -0300 Add optional blurring on shadow on text of icons. In general backported gtk+-3 code to implement it.. Add a new property called "shadow-blur-radius" on gtkrc file. If the "shadow-blur-radius" does not exist or is zero, it is shaded as before. If greater than 1, applies blur. Signed-off-by: Eric Koegel <eric.koe...@gmail.com> --- src/Makefile.am | 7 +- src/gtkcairoblur.c | 315 +++++++++++++++++++++++++++++++++++++++++++++ src/gtkcairoblurprivate.h | 47 +++++++ src/xfdesktop-icon-view.c | 55 ++++++-- 4 files changed, 413 insertions(+), 11 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 4b432ac..2ca46e8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,7 +39,9 @@ desktop_icon_sources = \ xfdesktop-window-icon.c \ xfdesktop-window-icon.h \ xfdesktop-window-icon-manager.c \ - xfdesktop-window-icon-manager.h + xfdesktop-window-icon-manager.h \ + gtkcairoblur.c \ + gtkcairoblurprivate.h desktop_menu_sources = \ xfce-desktop-menu.c \ @@ -106,7 +108,8 @@ xfdesktop_LDADD += \ $(LIBXFCE4SMCLIENT_PRIVATE_LIBS) \ $(LIBWNCK_LIBS) \ $(XFCONF_LIBS) \ - $(LIBEXO_LIBS) + $(LIBEXO_LIBS) \ + -lm if BUILD_DESKTOP_MENU diff --git a/src/gtkcairoblur.c b/src/gtkcairoblur.c new file mode 100644 index 0000000..1a12830 --- /dev/null +++ b/src/gtkcairoblur.c @@ -0,0 +1,315 @@ +/* GTK - The GIMP Toolkit + * + * Copyright (C) 2014 Red Hat + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpie...@mecheye.net> + * Owen Taylor <otay...@redhat.com> + */ + +/* + * All code in this file is based on Gtk+-3 code. + * Files: gtk/gtkcairoblur.c and gtk/gtkcssshadowvalue.c + */ + +#include "gtkcairoblurprivate.h" + +#include <math.h> +#include <string.h> + +/* This applies a single box blur pass to a horizontal range of pixels; + * since the box blur has the same weight for all pixels, we can + * implement an efficient sliding window algorithm where we add + * in pixels coming into the window from the right and remove + * them when they leave the windw to the left. + * + * d is the filter width; for even d shift indicates how the blurred + * result is aligned with the original - does ' x ' go to ' yy' (shift=1) + * or 'yy ' (shift=-1) + */ +static void +blur_xspan (guchar *row, + guchar *tmp_buffer, + int row_width, + int d, + int shift) +{ + int offset; + int sum = 0; + int i; + + if (d % 2 == 1) + offset = d / 2; + else + offset = (d - shift) / 2; + + /* All the conditionals in here look slow, but the branches will + * be well predicted and there are enough different possibilities + * that trying to write this as a series of unconditional loops + * is hard and not an obvious win. The main slow down here seems + * to be the integer division per pixel; one possible optimization + * would be to accumulate into two 16-bit integer buffers and + * only divide down after all three passes. (SSE parallel implementation + * of the divide step is possible.) + */ + for (i = -d + offset; i < row_width + offset; i++) + { + if (i >= 0 && i < row_width) + sum += row[i]; + + if (i >= offset) + { + if (i >= d) + sum -= row[i - d]; + + tmp_buffer[i - offset] = (sum + d / 2) / d; + } + } + + memcpy (row, tmp_buffer, row_width); +} + +static void +blur_rows (guchar *dst_buffer, + guchar *tmp_buffer, + int buffer_width, + int buffer_height, + int d) +{ + int i; + + for (i = 0; i < buffer_height; i++) + { + guchar *row = dst_buffer + i * buffer_width; + + /* We want to produce a symmetric blur that spreads a pixel + * equally far to the left and right. If d is odd that happens + * naturally, but for d even, we approximate by using a blur + * on either side and then a centered blur of size d + 1. + * (technique also from the SVG specification) + */ + if (d % 2 == 1) + { + blur_xspan (row, tmp_buffer, buffer_width, d, 0); + blur_xspan (row, tmp_buffer, buffer_width, d, 0); + blur_xspan (row, tmp_buffer, buffer_width, d, 0); + } + else + { + blur_xspan (row, tmp_buffer, buffer_width, d, 1); + blur_xspan (row, tmp_buffer, buffer_width, d, -1); + blur_xspan (row, tmp_buffer, buffer_width, d + 1, 0); + } + } +} + +/* Swaps width and height. + */ +static void +flip_buffer (guchar *dst_buffer, + guchar *src_buffer, + int width, + int height) +{ + /* Working in blocks increases cache efficiency, compared to reading + * or writing an entire column at once + */ +#define BLOCK_SIZE 16 + + int i0, j0; + + for (i0 = 0; i0 < width; i0 += BLOCK_SIZE) + for (j0 = 0; j0 < height; j0 += BLOCK_SIZE) + { + int max_j = MIN(j0 + BLOCK_SIZE, height); + int max_i = MIN(i0 + BLOCK_SIZE, width); + int i, j; + + for (i = i0; i < max_i; i++) + for (j = j0; j < max_j; j++) + dst_buffer[i * height + j] = src_buffer[j * width + i]; + } +#undef BLOCK_SIZE +} + +static void +_boxblur (guchar *buffer, + int width, + int height, + int radius) +{ + guchar *flipped_buffer; + + flipped_buffer = g_malloc (width * height); + + /* Step 1: swap rows and columns */ + flip_buffer (flipped_buffer, buffer, width, height); + + /* Step 2: blur rows (really columns) */ + blur_rows (flipped_buffer, buffer, height, width, radius); + + /* Step 3: swap rows and columns */ + flip_buffer (buffer, flipped_buffer, height, width); + + /* Step 4: blur rows */ + blur_rows (buffer, flipped_buffer, width, height, radius); + + g_free (flipped_buffer); +} + +static const cairo_user_data_key_t original_cr_key; + +static gboolean +gdk_cairo_get_clip_rectangle (cairo_t *cr, + cairo_rectangle_int_t *rect) +{ + double x1, y1, x2, y2; + gboolean clip_exists; + + cairo_clip_extents (cr, &x1, &y1, &x2, &y2); + + clip_exists = x1 < x2 && y1 < y2; + + if (rect) + { + x1 = floor (x1); + y1 = floor (y1); + x2 = ceil (x2); + y2 = ceil (y2); + + rect->x = CLAMP (x1, G_MININT, G_MAXINT); + rect->y = CLAMP (y1, G_MININT, G_MAXINT); + rect->width = CLAMP (x2 - x1, G_MININT, G_MAXINT); + rect->height = CLAMP (y2 - y1, G_MININT, G_MAXINT); + } + + return clip_exists; +} + +cairo_t * +gtk_css_shadow_value_start_drawing (cairo_t *cr, gdouble radius) +{ + cairo_rectangle_int_t clip_rect; + cairo_surface_t *surface; + cairo_t *blur_cr; + gdouble clip_radius; + + gdk_cairo_get_clip_rectangle (cr, &clip_rect); + + clip_radius = _gtk_cairo_blur_compute_pixels (radius); + + /* Create a larger surface to center the blur. */ + surface = cairo_surface_create_similar_image (cairo_get_target (cr), + CAIRO_FORMAT_A8, + clip_rect.width + 2 * clip_radius, + clip_rect.height + 2 * clip_radius); + cairo_surface_set_device_offset (surface, clip_radius - clip_rect.x, clip_radius - clip_rect.y); + blur_cr = cairo_create (surface); + cairo_set_user_data (blur_cr, &original_cr_key, cairo_reference (cr), (cairo_destroy_func_t) cairo_destroy); + + if (cairo_has_current_point (cr)) + { + double x, y; + + cairo_get_current_point (cr, &x, &y); + cairo_move_to (blur_cr, x, y); + } + + return blur_cr; +} + +cairo_t * +gtk_css_shadow_value_finish_drawing (cairo_t *cr, gdouble radius, GdkColor *color) +{ + cairo_t *original_cr; + cairo_surface_t *surface; + + original_cr = cairo_get_user_data (cr, &original_cr_key); + + /* Blur the surface. */ + surface = cairo_get_target (cr); + + _gtk_cairo_blur_surface (surface, radius); + + gdk_cairo_set_source_color(original_cr, color); + cairo_mask_surface (original_cr, surface, 0, 0); + + cairo_destroy (cr); + cairo_surface_destroy (surface); + + return original_cr; +} + +/* + * _gtk_cairo_blur_surface: + * @surface: a cairo image surface. + * @radius: the blur radius. + * + * Blurs the cairo image surface at the given radius. + */ +void +_gtk_cairo_blur_surface (cairo_surface_t* surface, + double radius_d) +{ + cairo_format_t format; + int radius = radius_d; + + g_return_if_fail (surface != NULL); + g_return_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE); + + format = cairo_image_surface_get_format (surface); + g_return_if_fail (format == CAIRO_FORMAT_A8); + + if (radius == 0) + return; + + /* Before we mess with the surface, execute any pending drawing. */ + cairo_surface_flush (surface); + + _boxblur (cairo_image_surface_get_data (surface), + cairo_image_surface_get_stride (surface), + cairo_image_surface_get_height (surface), + radius); + + /* Inform cairo we altered the surface contents. */ + cairo_surface_mark_dirty (surface); +} + +/* + * _gtk_cairo_blur_compute_pixels: + * @radius: the radius to compute the pixels for + * + * Computes the number of pixels necessary to extend an image in one + * direction to hold the image with shadow. + * + * This is just the number of pixels added by the blur radius, shadow + * offset and spread are not included. + * + * Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for + * approximating a Gaussian using box blurs. This yields quite a good + * approximation for a Gaussian. Then we multiply this by 1.5 since our + * code wants the radius of the entire triple-box-blur kernel instead of + * the diameter of an individual box blur. For more details, see: + * http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement + * https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19 + */ +#define GAUSSIAN_SCALE_FACTOR ((3.0 * sqrt(2 * G_PI) / 4) * 1.5) + +int +_gtk_cairo_blur_compute_pixels (double radius) +{ + return floor (radius * GAUSSIAN_SCALE_FACTOR + 0.5); +} diff --git a/src/gtkcairoblurprivate.h b/src/gtkcairoblurprivate.h new file mode 100644 index 0000000..7bfb770 --- /dev/null +++ b/src/gtkcairoblurprivate.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Canonical Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * Authored by Andrea Cimitan <andrea.cimi...@canonical.com> + * Original code from Mirco Mueller <mirco.muel...@canonical.com> + * + */ + +#ifndef _GTK_CAIRO_BLUR_H +#define _GTK_CAIRO_BLUR_H + +#include <gdk/gdk.h> +#include <glib.h> +#include <cairo.h> + +G_BEGIN_DECLS + +cairo_t *gtk_css_shadow_value_start_drawing (cairo_t *cr, + gdouble radius); + +cairo_t *gtk_css_shadow_value_finish_drawing (cairo_t *cr, + gdouble radius, + GdkColor *color); + +void _gtk_cairo_blur_surface (cairo_surface_t *surface, + double radius); + +int _gtk_cairo_blur_compute_pixels (double radius); + +G_END_DECLS + +#endif /* _GTK_CAIRO_BLUR_H */ diff --git a/src/xfdesktop-icon-view.c b/src/xfdesktop-icon-view.c index 02f86b9..cade043 100644 --- a/src/xfdesktop-icon-view.c +++ b/src/xfdesktop-icon-view.c @@ -47,6 +47,7 @@ #include "xfce-desktop.h" #include "xfdesktop-volume-icon.h" #include "xfdesktop-common.h" +#include "gtkcairoblurprivate.h" #include <libwnck/libwnck.h> #include <libxfce4ui/libxfce4ui.h> @@ -177,6 +178,7 @@ struct _XfdesktopIconViewPrivate gchar selected_shadow_x_offset; gchar selected_shadow_y_offset; GdkColor *selected_shadow_color; + gchar shadow_blur_radius; gint cell_padding; gint cell_spacing; @@ -539,6 +541,13 @@ xfdesktop_icon_view_class_init(XfdesktopIconViewClass *klass) G_PARAM_READABLE)); gtk_widget_class_install_style_property(widget_class, + g_param_spec_char("shadow-blur-radius", + "shadow blur radius", + "Blur radius for label text", + G_MININT8, G_MAXINT8, 0, + G_PARAM_READABLE)); + + gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("shadow-color", "Shadow color", "Color for label text shadows", @@ -1810,6 +1819,7 @@ xfdesktop_icon_view_style_set(GtkWidget *widget, "label-alpha", &icon_view->priv->label_alpha, "shadow-x-offset", &icon_view->priv->shadow_x_offset, "shadow-y-offset", &icon_view->priv->shadow_y_offset, + "shadow-blur-radius", &icon_view->priv->shadow_blur_radius, "shadow-color", &icon_view->priv->shadow_color, NULL); @@ -2959,15 +2969,39 @@ xfdesktop_icon_view_draw_image(cairo_t *cr, GdkPixbuf *pix, GdkRectangle *rect) } static void -xfdesktop_icon_view_draw_text(cairo_t *cr, PangoLayout *playout, - gint x, gint y, gint rtl_offset, GdkColor *color) +xfdesktop_icon_view_draw_text(cairo_t *cr, PangoLayout *playout, GdkRectangle *text_area, + gint x_offset, gint y_offset, gint rtl_offset, + gint blur_radius, GdkColor *color) { - cairo_save(cr); + GdkRectangle box_area; - cairo_move_to(cr, x - rtl_offset, y); + gint extents = _gtk_cairo_blur_compute_pixels(blur_radius); - gdk_cairo_set_source_color(cr, color); - pango_cairo_show_layout(cr, playout); + /* Extend even more the rectangle to not cut the shadows. */ + box_area = *text_area; + box_area.x -= extents; + box_area.y -= extents; + box_area.width += extents * 2; + box_area.height += extents * 2; + + /* Clip the cairo area to blur the minimum surface */ + gdk_cairo_rectangle(cr, &box_area); + cairo_clip(cr); + + cairo_move_to(cr, + box_area.x + extents + x_offset - rtl_offset, + box_area.y + extents + y_offset); + cairo_save(cr); + + if (blur_radius > 1) { + cr = gtk_css_shadow_value_start_drawing (cr, blur_radius); + pango_cairo_show_layout (cr, playout); + cr = gtk_css_shadow_value_finish_drawing (cr, blur_radius, color); + } + else { + gdk_cairo_set_source_color(cr, color); + pango_cairo_show_layout(cr, playout); + } cairo_restore(cr); } @@ -3061,11 +3095,14 @@ xfdesktop_icon_view_paint_icon(XfdesktopIconView *icon_view, } /* draw text shadow for the label text if an offset was defined */ - if(x_offset || y_offset) { + if(x_offset || y_offset || icon_view->priv->shadow_blur_radius) { + /* Draw the shadow */ xfdesktop_icon_view_draw_text(cr, playout, - text_extents.x + x_offset, - text_extents.y + y_offset, + &text_extents, + x_offset, + y_offset, rtl_offset, + icon_view->priv->shadow_blur_radius, sh_text_col); } -- To stop receiving notification emails like this one, please contact the administrator of this repository. _______________________________________________ Xfce4-commits mailing list Xfce4-commits@xfce.org https://mail.xfce.org/mailman/listinfo/xfce4-commits