2
* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
3
* Copyright (c) 2011 ammonkey <am.monkeyd@gmail.com>
5
* Originaly Written in gtk+: gtkcellrendererpixbuf.h
7
* This library is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Library General Public
9
* License as published by the Free Software Foundation; either
10
* version 2 of the License, or (at your option) any later version.
12
* This library is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Library General Public License for more details.
17
* You should have received a copy of the GNU Library General Public
18
* License along with this library; if not, write to the
19
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20
* Boston, MA 02111-1307, USA.
27
#include <glib-object.h>
29
#include "marlin-icon-renderer.h"
30
#include "eel-gdk-pixbuf-extensions.h"
31
#include "marlin-vala.h"
34
#define EXO_PARAM_READWRITE (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
36
#define MARLIN_EMBLEM_SIZE 16
38
static void marlin_icon_renderer_get_property (GObject *object,
42
static void marlin_icon_renderer_set_property (GObject *object,
46
static void marlin_icon_renderer_finalize (GObject *object);
47
static void marlin_icon_renderer_get_size (GtkCellRenderer *cell,
49
const GdkRectangle *rectangle,
54
static void marlin_icon_renderer_render (GtkCellRenderer *cell,
57
const GdkRectangle *background_area,
58
const GdkRectangle *cell_area,
59
GtkCellRendererState flags);
60
static inline gboolean thumbnail_needs_frame (const GdkPixbuf *thumbnail,
74
PROP_SELECTION_HELPERS,
78
struct _MarlinIconRendererPrivate
85
MarlinZoomLevel zoom_level;
89
gboolean follow_state;
90
gboolean selection_helpers;
92
MarlinClipboardManager *clipboard;
96
G_DEFINE_TYPE (MarlinIconRenderer, marlin_icon_renderer, GTK_TYPE_CELL_RENDERER);
98
static gpointer _g_object_ref0 (gpointer self) {
99
return self ? g_object_ref (self) : NULL;
103
marlin_icon_renderer_init (MarlinIconRenderer *cellpixbuf)
105
MarlinIconRendererPrivate *priv;
107
cellpixbuf->priv = G_TYPE_INSTANCE_GET_PRIVATE (cellpixbuf,
108
MARLIN_TYPE_ICON_RENDERER,
109
MarlinIconRendererPrivate);
110
priv = cellpixbuf->priv;
112
priv->clipboard = marlin_clipboard_manager_get_for_display (gdk_display_get_default ());
113
priv->emblems = TRUE;
117
marlin_icon_renderer_class_init (MarlinIconRendererClass *class)
119
GObjectClass *object_class = G_OBJECT_CLASS (class);
120
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
122
object_class->finalize = marlin_icon_renderer_finalize;
124
object_class->get_property = marlin_icon_renderer_get_property;
125
object_class->set_property = marlin_icon_renderer_set_property;
127
cell_class->get_size = marlin_icon_renderer_get_size;
128
cell_class->render = marlin_icon_renderer_render;
130
/*g_object_class_install_property (object_class,
132
g_param_spec_object ("pixbuf",
134
"The pixbuf to render",
136
EXO_PARAM_READWRITE));*/
138
g_object_class_install_property (object_class,
140
g_param_spec_enum ("size", "size", "size",
141
MARLIN_TYPE_ICON_SIZE,
142
MARLIN_ICON_SIZE_SMALL,
143
G_PARAM_CONSTRUCT | EXO_PARAM_READWRITE));
145
g_object_class_install_property (object_class,
147
g_param_spec_enum ("zoom-level", "zoom-level", "zoom-level",
148
MARLIN_TYPE_ZOOM_LEVEL,
149
MARLIN_ZOOM_LEVEL_NORMAL,
150
EXO_PARAM_READWRITE));
152
g_object_class_install_property (object_class,
154
g_param_spec_object ("drop-file", "drop-file", "drop-file",
156
EXO_PARAM_READWRITE));
158
g_object_class_install_property (object_class,
160
g_param_spec_object ("file", "file", "file",
162
EXO_PARAM_READWRITE));
166
* MarlinIconRenderer:emblems:
168
* Specifies whether to render emblems in addition to the file icons.
170
g_object_class_install_property (object_class,
172
g_param_spec_boolean ("emblems",
176
EXO_PARAM_READWRITE));
179
* MarlinIconRenderer:follow-state:
181
* Specifies whether the rendered pixbuf should be colorized
182
* according to the #GtkCellRendererState.
186
g_object_class_install_property (object_class,
188
g_param_spec_boolean ("follow-state",
190
"Whether the rendered pixbuf should be "
191
"colorized according to the state",
193
EXO_PARAM_READWRITE));
195
g_object_class_install_property (object_class,
196
PROP_SELECTION_HELPERS,
197
g_param_spec_boolean ("selection-helpers",
199
"Whether the selection helpers +/- aree rendered",
201
EXO_PARAM_READWRITE));
204
g_type_class_add_private (object_class, sizeof (MarlinIconRendererPrivate));
208
marlin_icon_renderer_finalize (GObject *object)
210
MarlinIconRenderer *cellpixbuf = MARLIN_ICON_RENDERER (object);
211
MarlinIconRendererPrivate *priv = cellpixbuf->priv;
214
g_object_unref (priv->pixbuf);*/
216
g_object_unref (priv->file);
218
g_object_unref (priv->drop_file);
220
g_object_unref (priv->clipboard);
222
G_OBJECT_CLASS (marlin_icon_renderer_parent_class)->finalize (object);
226
marlin_icon_renderer_get_property (GObject *object,
231
MarlinIconRenderer *cellpixbuf = MARLIN_ICON_RENDERER (object);
232
MarlinIconRendererPrivate *priv = cellpixbuf->priv;
237
g_value_set_object (value, priv->pixbuf);
240
g_value_set_object (value, priv->drop_file);
243
g_value_set_object (value, priv->file);
246
g_value_set_enum (value, priv->size);
248
case PROP_ZOOM_LEVEL:
249
g_value_set_enum (value, priv->zoom_level);
252
g_value_set_boolean (value, priv->emblems);
254
case PROP_FOLLOW_STATE:
255
g_value_set_boolean (value, priv->follow_state);
257
case PROP_SELECTION_HELPERS:
258
g_value_set_boolean (value, priv->selection_helpers);
261
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
267
marlin_icon_renderer_set_property (GObject *object,
272
MarlinIconRenderer *cellpixbuf = MARLIN_ICON_RENDERER (object);
273
MarlinIconRendererPrivate *priv = cellpixbuf->priv;
279
g_object_unref (priv->pixbuf);
280
priv->pixbuf = (GdkPixbuf*) g_value_dup_object (value);
283
if (G_LIKELY (priv->drop_file != NULL))
284
g_object_unref (G_OBJECT (priv->drop_file));
285
priv->drop_file = (gpointer) g_value_dup_object (value);
288
//_g_object_unref0 (priv->pixbuf);
289
_g_object_unref0 (priv->file);
290
priv->file = (GOFFile*) g_value_dup_object (value);
292
gof_file_update_icon (priv->file, priv->size);
293
priv->pixbuf = priv->file->pix;
297
priv->size = g_value_get_enum (value);
299
case PROP_ZOOM_LEVEL:
300
priv->zoom_level = g_value_get_enum (value);
301
priv->helper_size = (priv->zoom_level > MARLIN_ZOOM_LEVEL_NORMAL) ? 24 : 16;
304
priv->emblems = g_value_get_boolean (value);
306
case PROP_FOLLOW_STATE:
307
priv->follow_state = g_value_get_boolean (value);
309
case PROP_SELECTION_HELPERS:
310
priv->selection_helpers = g_value_get_boolean (value);
313
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
319
* marlin_icon_renderer_new:
321
* Creates a new #MarlinIconRenderer. Adjust rendering
322
* parameters using object properties. Object properties can be set
323
* globally (with g_object_set()). Also, with #GtkTreeViewColumn, you
324
* can bind a property to a value in a #GtkTreeModel. For example, you
325
* can bind the "pixbuf" property on the cell renderer to a pixbuf value
326
* in the model, thus rendering a different image in each row of the
329
* Return value: the new cell renderer
332
marlin_icon_renderer_new (void)
334
return g_object_new (MARLIN_TYPE_ICON_RENDERER, NULL);
338
invalidate_size (gint *width, gint *height)
347
marlin_icon_renderer_get_helper_size (MarlinIconRenderer *renderer) {
348
return renderer->priv->helper_size;
352
marlin_icon_renderer_get_size (GtkCellRenderer *cell,
354
const GdkRectangle *cell_area,
360
MarlinIconRenderer *cellpixbuf = (MarlinIconRenderer *) cell;
361
MarlinIconRendererPrivate *priv = cellpixbuf->priv;
362
gint pixbuf_width = 0;
363
gint pixbuf_height = 0;
368
//g_return_if_fail (priv->pixbuf);
369
if (!(priv->pixbuf && GDK_IS_PIXBUF (priv->pixbuf))) {
370
invalidate_size (width, height);
376
pixbuf_width = gdk_pixbuf_get_width (priv->pixbuf);
377
pixbuf_height = gdk_pixbuf_get_height (priv->pixbuf);
380
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
381
calc_width = (gint) xpad * 2 + pixbuf_width;
382
calc_height = (gint) ypad * 2 + pixbuf_height;
384
if (cell_area && pixbuf_width > 0 && pixbuf_height > 0)
386
gfloat xalign, yalign;
388
gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
391
*x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
392
(1.0 - xalign) : xalign) *
393
(cell_area->width - calc_width));
394
*x_offset = MAX (*x_offset, 0);
398
*y_offset = (yalign *
399
(cell_area->height - calc_height));
400
*y_offset = MAX (*y_offset, 0);
405
if (x_offset) *x_offset = 0;
406
if (y_offset) *y_offset = 0;
409
/* Even if the last new pixbuf corresponding to the last requested size isn't generated
410
yet, we can still determine its dimensions. This allow to asyncronously load the thumbnails
412
int s = MAX (pixbuf_width, pixbuf_height);
413
priv->scale = MIN (1, (double)priv->size / s);
416
*width = calc_width * priv->scale;
419
*height = calc_height * priv->scale;
424
cairo_make_shadow_for_rect (cairo_t* cr,
425
gdouble x1, gdouble y1, gdouble w, gdouble h,
426
gdouble rad, gdouble r, gdouble g, gdouble b, gdouble size);
429
marlin_icon_renderer_render (GtkCellRenderer *cell,
432
const GdkRectangle *background_area,
433
const GdkRectangle *cell_area,
434
GtkCellRendererState flags)
437
MarlinIconRenderer *cellpixbuf = (MarlinIconRenderer *) cell;
438
MarlinIconRendererPrivate *priv = cellpixbuf->priv;
439
GtkStyleContext *context;
440
GdkPixbuf *pixbuf, *stated;
442
GdkRectangle pix_rect;
443
GdkRectangle emblem_area;
444
GdkRectangle draw_rect;
447
MarlinIconInfo *nicon;
449
if (!(priv->file && priv->pixbuf))
450
return; /* return silently - this is not an error - could be rendering blank line (e.g. expanded empty subdirectory */
452
g_return_if_fail (GDK_IS_PIXBUF (priv->pixbuf));
453
g_return_if_fail (priv->size >= 1);
456
marlin_icon_renderer_get_size (cell, widget, (GdkRectangle *) cell_area,
462
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
463
pix_rect.x += cell_area->x + xpad;
464
pix_rect.y += cell_area->y + ypad;
465
pix_rect.width -= xpad * 2;
466
pix_rect.height -= ypad * 2;
468
if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect))
471
pixbuf = g_object_ref (priv->pixbuf);
473
//g_debug ("%s %s %u %u\n", G_STRFUNC, priv->file->uri, G_OBJECT (priv->file)->ref_count, G_OBJECT (priv->pixbuf)->ref_count);
476
if (priv->file == priv->drop_file) {
477
flags |= GTK_CELL_RENDERER_PRELIT;
478
nicon = marlin_icon_info_lookup_from_name ("folder-drag-accept", priv->size);
479
temp = marlin_icon_info_get_pixbuf_nodefault (nicon);
480
g_object_unref (nicon);
481
g_object_unref (pixbuf);
483
} else if (priv->file->is_directory) {
484
if (priv->file->is_expanded) {
485
nicon = marlin_icon_info_lookup_from_name ("folder-open", priv->size);
486
temp = marlin_icon_info_get_pixbuf_nodefault (nicon);
487
g_object_unref (nicon);
488
g_object_unref (pixbuf);
494
if (marlin_clipboard_manager_has_cutted_file (priv->clipboard, priv->file))
496
/* 50% translucent for cutted files */
497
temp = eel_gdk_pixbuf_lucent (pixbuf, 50);
498
g_object_unref (pixbuf);
501
else if (priv->file->is_hidden)
503
/* 75% translucent for hidden files */
504
temp = eel_gdk_pixbuf_lucent (pixbuf, 75);
505
g_object_unref (pixbuf);
509
context = gtk_widget_get_style_context (gtk_widget_get_parent (widget));
510
gtk_style_context_save (context);
511
state = GTK_STATE_FLAG_NORMAL;
513
if (!gtk_widget_get_sensitive (widget) ||
514
!gtk_cell_renderer_get_sensitive (cell))
515
state |= GTK_STATE_FLAG_INSENSITIVE;
516
else if (priv->follow_state &&
517
(flags & (GTK_CELL_RENDERER_SELECTED |
518
GTK_CELL_RENDERER_PRELIT)) != 0) {
519
if ((flags & GTK_CELL_RENDERER_SELECTED) != 0)
521
state = GTK_STATE_FLAG_SELECTED;
522
/* compute the state with the state of the widget; this way we handle the backdrop */
523
state |= gtk_widget_get_state_flags (widget);
525
gtk_style_context_get_background_color (context, state, &color);
526
temp = eel_create_colorized_pixbuf (pixbuf, &color);
527
g_object_unref (pixbuf);
531
if ((flags & GTK_CELL_RENDERER_PRELIT) != 0)
533
temp = eel_create_spotlight_pixbuf (pixbuf);
534
g_object_unref (pixbuf);
538
//state = gtk_cell_renderer_get_state (cell, widget, flags);
541
/*if (state != GTK_STATE_FLAG_NORMAL)
543
stated = create_symbolic_pixbuf (cellpixbuf, widget, state);
546
stated = transform_pixbuf_state (pixbuf, context);
548
g_object_unref (pixbuf);
552
if (pixbuf != NULL) {
553
if (priv->file->flags == GOF_FILE_THUMB_STATE_READY
554
&& gof_file_get_thumbnail_path (priv->file)
555
&& gof_file_thumb_can_frame (priv->file)
556
&& thumbnail_needs_frame (pixbuf, pix_rect.width, pix_rect.height))
558
cairo_make_shadow_for_rect (cr, pix_rect.x+4, pix_rect.y+4,
559
pix_rect.width-4, pix_rect.height-6,
563
gtk_render_icon (context, cr, pixbuf,
564
pix_rect.x, pix_rect.y);
566
/* let the theme draw a frame for loaded thumbnails */
567
if (priv->file->flags == GOF_FILE_THUMB_STATE_READY
568
&& gof_file_get_thumbnail_path (priv->file)
569
&& gof_file_thumb_can_frame (priv->file))
571
gtk_render_frame (context, cr,
572
pix_rect.x, pix_rect.y,
573
pix_rect.width, pix_rect.height);
576
gtk_style_context_restore (context);
577
g_object_unref (pixbuf);
580
/* add remove helpers +/- */
583
/* Do not show selection helpers or emblems for very small icons */
584
if (priv->selection_helpers &&
585
(flags & (GTK_CELL_RENDERER_PRELIT | GTK_CELL_RENDERER_SELECTED)) != 0 &&
586
priv->file != priv->drop_file)
588
if((flags & GTK_CELL_RENDERER_SELECTED) != 0 && (flags & GTK_CELL_RENDERER_PRELIT) != 0)
589
nicon = marlin_icon_info_lookup_from_name ("selection-remove", priv->helper_size);
590
else if ((flags & GTK_CELL_RENDERER_SELECTED) != 0)
591
nicon = marlin_icon_info_lookup_from_name ("selection-checked", priv->helper_size);
592
else if ((flags & GTK_CELL_RENDERER_PRELIT) != 0)
593
nicon = marlin_icon_info_lookup_from_name ("selection-add", priv->helper_size);
595
pix = marlin_icon_info_get_pixbuf_nodefault (nicon);
597
gdk_cairo_set_source_pixbuf (cr, pix, pix_rect.x, pix_rect.y);
599
g_object_unref (pix);
602
g_object_unref (nicon);
606
/* check if we should render emblems as well */
607
/* Still show emblems when selection helpers hidden in double click mode */
608
if (G_LIKELY (priv->emblems))
611
GList* emblems = g_list_first(priv->file->emblems_list);
613
/* render the emblems
614
* show number of emblems depending on the zoom lvl. */
615
while (emblems != NULL && priv->zoom_level > 0 && position < priv->zoom_level)
617
/* check if we have the emblem in the icon theme */
618
nicon = marlin_icon_info_lookup_from_name (emblems->data, MARLIN_EMBLEM_SIZE);
619
pix = marlin_icon_info_get_pixbuf_nodefault (nicon);
621
g_warning ("Can't load icon %s", (char *) emblems->data);
625
/* determine the dimensions of the emblem */
626
emblem_area.width = gdk_pixbuf_get_width (pix);
627
emblem_area.height = gdk_pixbuf_get_height (pix);
629
/* stack emblem on a vertical line begging from the bottom */
630
guint overlap = MIN (8 + priv->zoom_level, pix_rect.width / 4);
631
emblem_area.x = pix_rect.x + pix_rect.width - overlap;
632
emblem_area.y = pix_rect.y + pix_rect.height - emblem_area.width * (position + 1);
633
/* don't show cutted emblems */
634
if (emblem_area.y < background_area->y)
638
/* nice square shape */
639
/* determine a good position for the emblem, depending on the position index */
642
case 0: /* bottom/right */
643
emblem_area.x = MIN (pix_rect.x + pix_rect.width - emblem_area.width/2, background_area->x + background_area->width - emblem_area.width);
644
emblem_area.y = pix_rect.y + pix_rect.height - emblem_area.width;
646
case 1: /* top/right */
647
emblem_area.x = MIN (pix_rect.x + pix_rect.width - emblem_area.width/2, background_area->x + background_area->width - emblem_area.width);
648
emblem_area.y = pix_rect.y + pix_rect.height - emblem_area.width * 2;
650
case 2: /* bottom/left */
651
emblem_area.x = MIN (pix_rect.x + pix_rect.width - emblem_area.width/2 - emblem_area.width, background_area->x + background_area->width - 2 * emblem_area.width);
652
emblem_area.y = pix_rect.y + pix_rect.height - emblem_area.width;
654
case 3: /* top/left */
655
emblem_area.x = MIN (pix_rect.x + pix_rect.width - emblem_area.width/2 - emblem_area.width, background_area->x + background_area->width - 2 * emblem_area.width);
656
emblem_area.y = pix_rect.y + pix_rect.height - emblem_area.width * 2;
661
gdk_cairo_set_source_pixbuf (cr, pix, emblem_area.x, emblem_area.y);
666
emblems = g_list_next(emblems);
667
g_object_unref (nicon);
668
g_object_unref (pix);
672
/* The render call should always be preceded by a set_property call from
673
GTK. It should be safe to unreference or free the allocate memory
675
_g_object_unref0 (priv->file);
676
_g_object_unref0 (priv->drop_file);
680
* Shadows code snippet took from synapse (ui/utils.vala) and converted to C.
681
* Authored by Michal Hruby <michal.mhr@gmail.com>
682
* Alberto Aldegheri <albyrock87+dev@gmail.com>
685
#define _cairo_pattern_destroy0(var) ((var == NULL) ? NULL : (var = (cairo_pattern_destroy (var), NULL)))
688
add_shadow_stops (cairo_pattern_t* pat, gdouble r, gdouble g, gdouble b, gdouble size, gdouble alpha)
690
g_return_if_fail (pat != NULL);
692
cairo_pattern_add_color_stop_rgba (pat, 1.0, r, g, b, (gdouble) 0);
693
cairo_pattern_add_color_stop_rgba (pat, 0.8, r, g, b, alpha * 0.07);
694
cairo_pattern_add_color_stop_rgba (pat, 0.6, r, g, b, alpha * 0.24);
695
cairo_pattern_add_color_stop_rgba (pat, 0.4, r, g, b, alpha * 0.46);
696
cairo_pattern_add_color_stop_rgba (pat, 0.2, r, g, b, alpha * 0.77);
697
cairo_pattern_add_color_stop_rgba (pat, 0.0, r, g, b, alpha);
702
cairo_make_shadow_for_rect (cairo_t* cr,
703
gdouble x1, gdouble y1, gdouble w, gdouble h,
704
gdouble rad, gdouble r, gdouble g, gdouble b, gdouble size)
714
cairo_pattern_t* pat = NULL;
716
g_return_if_fail (cr != NULL);
717
if (size < ((gdouble) 1))
722
cairo_translate (cr, 0.5, 0.5);
733
/* Top left corner */
735
_cairo_pattern_destroy0 (pat);
736
pat = cairo_pattern_create_radial (x2, y2, rad, x2, y2, thick);
737
add_shadow_stops (pat, r, g, b, size, a);
738
cairo_set_source (cr, pat);
739
cairo_rectangle (cr, x1-size, y1-size, thick, thick);
744
/* Bottom left corner */
746
_cairo_pattern_destroy0 (pat);
747
pat = cairo_pattern_create_radial (x2, y3, rad, x2, y3, thick);
748
add_shadow_stops (pat, r, g, b, size, a);
749
cairo_set_source (cr, pat);
750
cairo_rectangle (cr, x1-size, y3, thick, thick);
755
/* Top right corner */
757
_cairo_pattern_destroy0 (pat);
758
pat = cairo_pattern_create_radial (x3, y2, rad, x3, y2, thick);
759
add_shadow_stops (pat, r, g, b, size, a);
760
cairo_set_source (cr, pat);
761
cairo_rectangle (cr, x3, y1-size, thick, thick);
766
/* Bottom right corner */
768
_cairo_pattern_destroy0 (pat);
769
pat = cairo_pattern_create_radial (x3, y3, rad, x3, y3, thick);
770
add_shadow_stops (pat, r, g, b, size, a);
771
cairo_set_source (cr, pat);
772
cairo_rectangle (cr, x3, y3, thick, thick);
779
_cairo_pattern_destroy0 (pat);
780
pat = cairo_pattern_create_linear (x4, 0, x4+size, 0);
781
add_shadow_stops (pat, r, g, b, size, a);
782
cairo_set_source (cr, pat);
783
cairo_rectangle (cr, x4, y2, size, y3-y2);
790
_cairo_pattern_destroy0 (pat);
791
pat = cairo_pattern_create_linear (x1, 0, x1-size, 0);
792
add_shadow_stops (pat, r, g, b, size, a);
793
cairo_set_source (cr, pat);
794
cairo_rectangle (cr, x1-size, y2, size, y3-y2);
801
_cairo_pattern_destroy0 (pat);
802
pat = cairo_pattern_create_linear (0, y4, 0, y4+size);
803
add_shadow_stops (pat, r, g, b, size, a);
804
cairo_set_source (cr, pat);
805
cairo_rectangle (cr, x2, y4, x3-x2, size);
812
_cairo_pattern_destroy0 (pat);
813
pat = cairo_pattern_create_linear (0, y1, 0, y1-size);
814
add_shadow_stops (pat, r, g, b, size, a);
815
cairo_set_source (cr, pat);
816
cairo_rectangle (cr, x2, y1-size, x3-x2, size);
822
_cairo_pattern_destroy0 (pat);
825
static inline gboolean
826
thumbnail_needs_frame (const GdkPixbuf *thumbnail,
830
const guchar *pixels;
834
/* don't add frames to small thumbnails */
835
if (width < 48 && height < 48)
838
/* always add a frame to thumbnails w/o alpha channel */
839
if (G_LIKELY (!gdk_pixbuf_get_has_alpha (thumbnail)))
842
/* get a pointer to the thumbnail data */
843
pixels = gdk_pixbuf_get_pixels (thumbnail);
845
/* check if we have a transparent pixel on the first row */
846
for (n = width * 4; n > 0; n -= 4)
847
if (pixels[n - 1] < 255u)
849
g_debug("transparent pixel");
851
/* determine the rowstride */
852
rowstride = gdk_pixbuf_get_rowstride (thumbnail);
854
/* skip the first row */
857
/* check if we have a transparent pixel in the first or last column */
858
for (n = height - 2; n > 0; --n, pixels += rowstride)
859
if (pixels[3] < 255u || pixels[width * 4 - 1] < 255u)
862
/* check if we have a transparent pixel on the last row */
863
for (n = width * 4; n > 0; n -= 4)
864
if (pixels[n - 1] < 255u)