[Xfce4-commits] [xfce/xfdesktop] 01/01: 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.

noreply at xfce.org noreply at xfce.org
Wed Oct 15 18:19:53 CEST 2014


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 <mati86dl at 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.koegel at 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 <jstpierre at mecheye.net>
+ *     Owen Taylor <otaylor at 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.cimitan at canonical.com>
+ * Original code from Mirco Mueller <mirco.mueller at 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.


More information about the Xfce4-commits mailing list