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
if (priv->file->is_directory) { /* only show "folder-drag-accept" image for folders. Otherwise just prelight */
479
nicon = marlin_icon_info_lookup_from_name ("folder-drag-accept", priv->size);
480
temp = marlin_icon_info_get_pixbuf_nodefault (nicon);
481
g_object_unref (nicon);
482
g_object_unref (pixbuf);
485
} else if (priv->file->is_directory) {
486
if (priv->file->is_expanded) {
487
nicon = marlin_icon_info_lookup_from_name ("folder-open", priv->size);
488
temp = marlin_icon_info_get_pixbuf_nodefault (nicon);
489
g_object_unref (nicon);
490
g_object_unref (pixbuf);
496
if (marlin_clipboard_manager_has_cutted_file (priv->clipboard, priv->file))
498
/* 50% translucent for cutted files */
499
temp = eel_gdk_pixbuf_lucent (pixbuf, 50);
500
g_object_unref (pixbuf);
503
else if (priv->file->is_hidden)
505
/* 75% translucent for hidden files */
506
temp = eel_gdk_pixbuf_lucent (pixbuf, 75);
507
g_object_unref (pixbuf);
511
context = gtk_widget_get_style_context (gtk_widget_get_parent (widget));
512
gtk_style_context_save (context);
513
state = GTK_STATE_FLAG_NORMAL;
515
if (!gtk_widget_get_sensitive (widget) ||
516
!gtk_cell_renderer_get_sensitive (cell))
517
state |= GTK_STATE_FLAG_INSENSITIVE;
518
else if (priv->follow_state &&
519
(flags & (GTK_CELL_RENDERER_SELECTED |
520
GTK_CELL_RENDERER_PRELIT)) != 0) {
521
if ((flags & GTK_CELL_RENDERER_SELECTED) != 0)
523
state = GTK_STATE_FLAG_SELECTED;
524
/* compute the state with the state of the widget; this way we handle the backdrop */
525
state |= gtk_widget_get_state_flags (widget);
527
gtk_style_context_get_background_color (context, state, &color);
528
temp = eel_create_colorized_pixbuf (pixbuf, &color);
529
g_object_unref (pixbuf);
533
if ((flags & GTK_CELL_RENDERER_PRELIT) != 0)
535
temp = eel_create_spotlight_pixbuf (pixbuf);
536
g_object_unref (pixbuf);
540
//state = gtk_cell_renderer_get_state (cell, widget, flags);
543
/*if (state != GTK_STATE_FLAG_NORMAL)
545
stated = create_symbolic_pixbuf (cellpixbuf, widget, state);
548
stated = transform_pixbuf_state (pixbuf, context);
550
g_object_unref (pixbuf);
554
if (pixbuf != NULL) {
555
if (priv->file->flags == GOF_FILE_THUMB_STATE_READY
556
&& gof_file_get_thumbnail_path (priv->file)
557
&& gof_file_thumb_can_frame (priv->file)
558
&& thumbnail_needs_frame (pixbuf, pix_rect.width, pix_rect.height))
560
cairo_make_shadow_for_rect (cr, pix_rect.x+4, pix_rect.y+4,
561
pix_rect.width-4, pix_rect.height-6,
565
gtk_render_icon (context, cr, pixbuf,
566
pix_rect.x, pix_rect.y);
568
/* let the theme draw a frame for loaded thumbnails */
569
if (priv->file->flags == GOF_FILE_THUMB_STATE_READY
570
&& gof_file_get_thumbnail_path (priv->file)
571
&& gof_file_thumb_can_frame (priv->file))
573
gtk_render_frame (context, cr,
574
pix_rect.x, pix_rect.y,
575
pix_rect.width, pix_rect.height);
578
gtk_style_context_restore (context);
579
g_object_unref (pixbuf);
582
/* add remove helpers +/- */
585
/* Do not show selection helpers or emblems for very small icons */
586
if (priv->selection_helpers &&
587
(flags & (GTK_CELL_RENDERER_PRELIT | GTK_CELL_RENDERER_SELECTED)) != 0 &&
588
priv->file != priv->drop_file)
590
if((flags & GTK_CELL_RENDERER_SELECTED) != 0 && (flags & GTK_CELL_RENDERER_PRELIT) != 0)
591
nicon = marlin_icon_info_lookup_from_name ("selection-remove", priv->helper_size);
592
else if ((flags & GTK_CELL_RENDERER_SELECTED) != 0)
593
nicon = marlin_icon_info_lookup_from_name ("selection-checked", priv->helper_size);
594
else if ((flags & GTK_CELL_RENDERER_PRELIT) != 0)
595
nicon = marlin_icon_info_lookup_from_name ("selection-add", priv->helper_size);
597
pix = marlin_icon_info_get_pixbuf_nodefault (nicon);
599
gdk_cairo_set_source_pixbuf (cr, pix, pix_rect.x, pix_rect.y);
601
g_object_unref (pix);
604
g_object_unref (nicon);
608
/* check if we should render emblems as well */
609
/* Still show emblems when selection helpers hidden in double click mode */
610
if (G_LIKELY (priv->emblems))
613
GList* emblems = g_list_first(priv->file->emblems_list);
615
/* render the emblems
616
* show number of emblems depending on the zoom lvl. */
617
while (emblems != NULL && priv->zoom_level > 0 && position < priv->zoom_level)
619
/* check if we have the emblem in the icon theme */
620
nicon = marlin_icon_info_lookup_from_name (emblems->data, MARLIN_EMBLEM_SIZE);
621
pix = marlin_icon_info_get_pixbuf_nodefault (nicon);
623
g_warning ("Can't load icon %s", (char *) emblems->data);
627
/* determine the dimensions of the emblem */
628
emblem_area.width = gdk_pixbuf_get_width (pix);
629
emblem_area.height = gdk_pixbuf_get_height (pix);
631
/* stack emblem on a vertical line begging from the bottom */
632
guint overlap = MIN (8 + priv->zoom_level, pix_rect.width / 4);
633
emblem_area.x = pix_rect.x + pix_rect.width - overlap;
634
emblem_area.y = pix_rect.y + pix_rect.height - emblem_area.width * (position + 1);
635
/* don't show cutted emblems */
636
if (emblem_area.y < background_area->y)
640
/* nice square shape */
641
/* determine a good position for the emblem, depending on the position index */
644
case 0: /* bottom/right */
645
emblem_area.x = MIN (pix_rect.x + pix_rect.width - emblem_area.width/2, background_area->x + background_area->width - emblem_area.width);
646
emblem_area.y = pix_rect.y + pix_rect.height - emblem_area.width;
648
case 1: /* top/right */
649
emblem_area.x = MIN (pix_rect.x + pix_rect.width - emblem_area.width/2, background_area->x + background_area->width - emblem_area.width);
650
emblem_area.y = pix_rect.y + pix_rect.height - emblem_area.width * 2;
652
case 2: /* bottom/left */
653
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);
654
emblem_area.y = pix_rect.y + pix_rect.height - emblem_area.width;
656
case 3: /* top/left */
657
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);
658
emblem_area.y = pix_rect.y + pix_rect.height - emblem_area.width * 2;
663
gdk_cairo_set_source_pixbuf (cr, pix, emblem_area.x, emblem_area.y);
668
emblems = g_list_next(emblems);
669
g_object_unref (nicon);
670
g_object_unref (pix);
674
/* The render call should always be preceded by a set_property call from
675
GTK. It should be safe to unreference or free the allocate memory
677
_g_object_unref0 (priv->file);
678
_g_object_unref0 (priv->drop_file);
682
* Shadows code snippet took from synapse (ui/utils.vala) and converted to C.
683
* Authored by Michal Hruby <michal.mhr@gmail.com>
684
* Alberto Aldegheri <albyrock87+dev@gmail.com>
687
#define _cairo_pattern_destroy0(var) ((var == NULL) ? NULL : (var = (cairo_pattern_destroy (var), NULL)))
690
add_shadow_stops (cairo_pattern_t* pat, gdouble r, gdouble g, gdouble b, gdouble size, gdouble alpha)
692
g_return_if_fail (pat != NULL);
694
cairo_pattern_add_color_stop_rgba (pat, 1.0, r, g, b, (gdouble) 0);
695
cairo_pattern_add_color_stop_rgba (pat, 0.8, r, g, b, alpha * 0.07);
696
cairo_pattern_add_color_stop_rgba (pat, 0.6, r, g, b, alpha * 0.24);
697
cairo_pattern_add_color_stop_rgba (pat, 0.4, r, g, b, alpha * 0.46);
698
cairo_pattern_add_color_stop_rgba (pat, 0.2, r, g, b, alpha * 0.77);
699
cairo_pattern_add_color_stop_rgba (pat, 0.0, r, g, b, alpha);
704
cairo_make_shadow_for_rect (cairo_t* cr,
705
gdouble x1, gdouble y1, gdouble w, gdouble h,
706
gdouble rad, gdouble r, gdouble g, gdouble b, gdouble size)
716
cairo_pattern_t* pat = NULL;
718
g_return_if_fail (cr != NULL);
719
if (size < ((gdouble) 1))
724
cairo_translate (cr, 0.5, 0.5);
735
/* Top left corner */
737
_cairo_pattern_destroy0 (pat);
738
pat = cairo_pattern_create_radial (x2, y2, rad, x2, y2, thick);
739
add_shadow_stops (pat, r, g, b, size, a);
740
cairo_set_source (cr, pat);
741
cairo_rectangle (cr, x1-size, y1-size, thick, thick);
746
/* Bottom left corner */
748
_cairo_pattern_destroy0 (pat);
749
pat = cairo_pattern_create_radial (x2, y3, rad, x2, y3, thick);
750
add_shadow_stops (pat, r, g, b, size, a);
751
cairo_set_source (cr, pat);
752
cairo_rectangle (cr, x1-size, y3, thick, thick);
757
/* Top right corner */
759
_cairo_pattern_destroy0 (pat);
760
pat = cairo_pattern_create_radial (x3, y2, rad, x3, y2, thick);
761
add_shadow_stops (pat, r, g, b, size, a);
762
cairo_set_source (cr, pat);
763
cairo_rectangle (cr, x3, y1-size, thick, thick);
768
/* Bottom right corner */
770
_cairo_pattern_destroy0 (pat);
771
pat = cairo_pattern_create_radial (x3, y3, rad, x3, y3, thick);
772
add_shadow_stops (pat, r, g, b, size, a);
773
cairo_set_source (cr, pat);
774
cairo_rectangle (cr, x3, y3, thick, thick);
781
_cairo_pattern_destroy0 (pat);
782
pat = cairo_pattern_create_linear (x4, 0, x4+size, 0);
783
add_shadow_stops (pat, r, g, b, size, a);
784
cairo_set_source (cr, pat);
785
cairo_rectangle (cr, x4, y2, size, y3-y2);
792
_cairo_pattern_destroy0 (pat);
793
pat = cairo_pattern_create_linear (x1, 0, x1-size, 0);
794
add_shadow_stops (pat, r, g, b, size, a);
795
cairo_set_source (cr, pat);
796
cairo_rectangle (cr, x1-size, y2, size, y3-y2);
803
_cairo_pattern_destroy0 (pat);
804
pat = cairo_pattern_create_linear (0, y4, 0, y4+size);
805
add_shadow_stops (pat, r, g, b, size, a);
806
cairo_set_source (cr, pat);
807
cairo_rectangle (cr, x2, y4, x3-x2, size);
814
_cairo_pattern_destroy0 (pat);
815
pat = cairo_pattern_create_linear (0, y1, 0, y1-size);
816
add_shadow_stops (pat, r, g, b, size, a);
817
cairo_set_source (cr, pat);
818
cairo_rectangle (cr, x2, y1-size, x3-x2, size);
824
_cairo_pattern_destroy0 (pat);
827
static inline gboolean
828
thumbnail_needs_frame (const GdkPixbuf *thumbnail,
832
const guchar *pixels;
836
/* don't add frames to small thumbnails */
837
if (width < 48 && height < 48)
840
/* always add a frame to thumbnails w/o alpha channel */
841
if (G_LIKELY (!gdk_pixbuf_get_has_alpha (thumbnail)))
844
/* get a pointer to the thumbnail data */
845
pixels = gdk_pixbuf_get_pixels (thumbnail);
847
/* check if we have a transparent pixel on the first row */
848
for (n = width * 4; n > 0; n -= 4)
849
if (pixels[n - 1] < 255u)
851
g_debug("transparent pixel");
853
/* determine the rowstride */
854
rowstride = gdk_pixbuf_get_rowstride (thumbnail);
856
/* skip the first row */
859
/* check if we have a transparent pixel in the first or last column */
860
for (n = height - 2; n > 0; --n, pixels += rowstride)
861
if (pixels[3] < 255u || pixels[width * 4 - 1] < 255u)
864
/* check if we have a transparent pixel on the last row */
865
for (n = width * 4; n > 0; n -= 4)
866
if (pixels[n - 1] < 255u)