Permit icon field in desktop file to be an absolute path
[geeqie.git] / src / pixbuf_util.c
1 /*
2  * Copyright (C) 2004 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program 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
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "pixbuf_util.h"
24 #include "exif.h"
25 #include "ui_fileops.h"
26
27 #include "icons/icons_inline.h"
28
29 #include <math.h>
30
31
32 /*
33  *-----------------------------------------------------------------------------
34  * png save
35  *-----------------------------------------------------------------------------
36  */
37
38 gboolean pixbuf_to_file_as_png(GdkPixbuf *pixbuf, const gchar *filename)
39 {
40         GError *error = NULL;
41         gboolean ret;
42
43         if (!pixbuf || !filename) return FALSE;
44
45         ret = gdk_pixbuf_save(pixbuf, filename, "png", &error,
46                               "tEXt::Software", GQ_APPNAME " " VERSION, NULL);
47
48         if (error)
49                 {
50                 log_printf("Error saving png file: %s\n", error->message);
51                 g_error_free(error);
52                 }
53
54         return ret;
55 }
56
57 /*
58  *-----------------------------------------------------------------------------
59  * jpeg save
60  *-----------------------------------------------------------------------------
61  */
62
63 gboolean pixbuf_to_file_as_jpg(GdkPixbuf *pixbuf, const gchar *filename, gint quality)
64 {
65         GError *error = NULL;
66         gchar *qbuf;
67         gboolean ret;
68
69         if (!pixbuf || !filename) return FALSE;
70
71         if (quality == -1) quality = 75;
72         if (quality < 1 || quality > 100)
73                 {
74                 log_printf("Jpeg not saved, invalid quality %d\n", quality);
75                 return FALSE;
76                 }
77
78         qbuf = g_strdup_printf("%d", quality);
79         ret = gdk_pixbuf_save(pixbuf, filename, "jpeg", &error, "quality", qbuf, NULL);
80         g_free(qbuf);
81
82         if (error)
83                 {
84                 log_printf("Error saving jpeg to %s\n%s\n", filename, error->message);
85                 g_error_free(error);
86                 }
87
88         return ret;
89 }
90
91 /*
92  *-----------------------------------------------------------------------------
93  * pixbuf from inline
94  *-----------------------------------------------------------------------------
95  */
96
97 typedef struct _PixbufInline PixbufInline;
98 struct _PixbufInline
99 {
100         const gchar *key;
101         const guint8 *data;
102 };
103
104 static PixbufInline inline_pixbuf_data[] = {
105         { PIXBUF_INLINE_FOLDER_CLOSED,  folder_closed },
106         { PIXBUF_INLINE_FOLDER_LOCKED,  folder_locked },
107         { PIXBUF_INLINE_FOLDER_OPEN,    folder_open },
108         { PIXBUF_INLINE_FOLDER_UP,      folder_up },
109         { PIXBUF_INLINE_SCROLLER,       icon_scroller },
110         { PIXBUF_INLINE_BROKEN,         icon_broken },
111         { PIXBUF_INLINE_METADATA,       icon_metadata },
112         { PIXBUF_INLINE_UNKNOWN,        icon_unknown },
113         { PIXBUF_INLINE_VIDEO,          icon_video },
114         { PIXBUF_INLINE_COLLECTION,     icon_collection },
115         { PIXBUF_INLINE_ICON,           gqview_icon },
116         { PIXBUF_INLINE_LOGO,           geeqie_logo },
117         { PIXBUF_INLINE_ICON_FLOAT,     icon_float },
118         { PIXBUF_INLINE_ICON_THUMB,     icon_thumb },
119         { PIXBUF_INLINE_ICON_BOOK,      icon_book },
120         { PIXBUF_INLINE_ICON_CONFIG,    icon_config },
121         { PIXBUF_INLINE_ICON_TOOLS,     icon_tools },
122         { PIXBUF_INLINE_ICON_VIEW,      icon_view },
123         { PIXBUF_INLINE_ICON_GUIDELINES,        icon_guidelines },
124         { PIXBUF_INLINE_ICON_PANORAMA,  icon_panorama },
125         { PIXBUF_INLINE_ICON_MAINTENANCE,       icon_maintenance },
126         { PIXBUF_INLINE_ICON_ZOOMFILLHOR,       icon_zoomfillhor },
127         { PIXBUF_INLINE_ICON_ZOOMFILLVERT,      icon_zoomfillvert },
128         { PIXBUF_INLINE_ICON_HIDETOOLS, icon_hidetools },
129         { PIXBUF_INLINE_ICON_EXIF,      icon_exif },
130         { PIXBUF_INLINE_ICON_MARKS,     icon_marks },
131         { PIXBUF_INLINE_ICON_INFO,      icon_info },
132         { PIXBUF_INLINE_ICON_SORT,      icon_sort },
133         { PIXBUF_INLINE_ICON_PDF,       icon_pdf },
134         { PIXBUF_INLINE_ICON_DRAW_RECTANGLE,    icon_draw_rectangle },
135         { PIXBUF_INLINE_ICON_MOVE,      icon_move },
136         { PIXBUF_INLINE_ICON_RENAME,    icon_rename },
137         { PIXBUF_INLINE_ICON_SELECT_ALL,        icon_select_all },
138         { PIXBUF_INLINE_ICON_SELECT_NONE,       icon_select_none },
139         { PIXBUF_INLINE_ICON_SELECT_INVERT,     icon_select_invert },
140         { PIXBUF_INLINE_ICON_SELECT_RECTANGLE,  icon_select_rectangle },
141         { PIXBUF_INLINE_ICON_FILE_FILTER,       icon_file_filter },
142         { PIXBUF_INLINE_ICON_CW,        icon_rotate_clockwise },
143         { PIXBUF_INLINE_ICON_CCW,       icon_rotate_counter_clockwise },
144         { PIXBUF_INLINE_ICON_180,       icon_rotate_180 },
145         { PIXBUF_INLINE_ICON_MIRROR,    icon_mirror },
146         { PIXBUF_INLINE_ICON_FLIP,      icon_flip },
147         { PIXBUF_INLINE_ICON_ORIGINAL,  icon_original },
148         { NULL, NULL }
149 };
150
151 GdkPixbuf *pixbuf_inline(const gchar *key)
152 {
153         gint i;
154
155         if (!key) return NULL;
156
157         i = 0;
158         while (inline_pixbuf_data[i].key)
159                 {
160                 if (strcmp(inline_pixbuf_data[i].key, key) == 0)
161                         {
162                         return gdk_pixbuf_new_from_inline(-1, inline_pixbuf_data[i].data, FALSE, NULL);
163                         }
164                 i++;
165                 }
166
167         log_printf("warning: inline pixbuf key \"%s\" not found.\n", key);
168
169         return NULL;
170 }
171
172 static void register_stock_icon(const gchar *key, GdkPixbuf *pixbuf)
173 {
174         static GtkIconFactory *icon_factory = NULL;
175         GtkIconSet *icon_set;
176
177         if (!icon_factory)
178                 {
179                 icon_factory = gtk_icon_factory_new();
180                 gtk_icon_factory_add_default(icon_factory);
181                 }
182
183         icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
184         gtk_icon_factory_add(icon_factory, key, icon_set);
185 }
186
187
188 void pixbuf_inline_register_stock_icons(void)
189 {
190         gint i;
191
192         i = 0;
193         while (inline_pixbuf_data[i].key)
194                 {
195                 register_stock_icon(inline_pixbuf_data[i].key, pixbuf_inline(inline_pixbuf_data[i].key));
196                 i++;
197                 }
198 }
199
200 gboolean register_theme_icon_as_stock(const gchar *key, const gchar *icon)
201 {
202         GtkIconTheme *icon_theme;
203         GdkPixbuf *pixbuf;
204         GError *error = NULL;
205
206         icon_theme = gtk_icon_theme_get_default();
207
208         if (gtk_icon_theme_has_icon(icon_theme, key)) return FALSE;
209
210         pixbuf = gtk_icon_theme_load_icon(icon_theme,
211                            icon, /* icon name */
212                            64, /* size */
213                            0,  /* flags */
214                            &error);
215         if (!pixbuf)
216                 {
217                 if (error)
218                         {
219                         DEBUG_1("Couldn't load icon %s: %s", icon, error->message);
220                         g_error_free(error);
221                         error = NULL;
222                         }
223
224                 if (strchr(icon, '.'))
225                         {
226                         /* try again without extension */
227                         gchar *icon2 = remove_extension_from_path(icon);
228                         pixbuf = gtk_icon_theme_load_icon(icon_theme,
229                                            icon2, /* icon name */
230                                            64, /* size */
231                                            0,  /* flags */
232                                            &error);
233                         if (error)
234                                 {
235                                 DEBUG_1("Couldn't load icon %s: %s", icon2, error->message);
236                                 g_error_free(error);
237                                 error = NULL;
238
239                                 /* try as an absolute path */
240                                 pixbuf = gdk_pixbuf_new_from_file(icon, &error);
241                                 if (error)
242                                         {
243                                         DEBUG_1("Couldn't load icon as absolute path %s: %s", icon, error->message);
244                                         g_error_free(error);
245                                         }
246                                 }
247                         g_free(icon2);
248                         }
249                 }
250
251         if (!pixbuf) return FALSE;
252
253         register_stock_icon(key, pixbuf);
254         return TRUE;
255 }
256
257 gboolean pixbuf_scale_aspect(gint req_w, gint req_h,
258                              gint old_w, gint old_h,
259                              gint *new_w, gint *new_h)
260 {
261         if (((gdouble)req_w / old_w) < ((gdouble)req_h / old_h))
262                 {
263                 *new_w = req_w;
264                 *new_h = (gdouble)*new_w / old_w * old_h;
265                 if (*new_h < 1) *new_h = 1;
266                 }
267         else
268                 {
269                 *new_h = req_h;
270                 *new_w = (gdouble)*new_h / old_h * old_w;
271                 if (*new_w < 1) *new_w = 1;
272                 }
273
274         return (*new_w != old_w || *new_h != old_h);
275 }
276
277 GdkPixbuf *pixbuf_fallback(FileData *fd, gint requested_width, gint requested_height)
278 {
279         GdkPixbuf *pixbuf;
280
281         switch (fd->format_class)
282                 {
283                 case FORMAT_CLASS_UNKNOWN:
284                         pixbuf = pixbuf_inline(PIXBUF_INLINE_UNKNOWN);
285                         break;
286                 case FORMAT_CLASS_META:
287                         pixbuf = pixbuf_inline(PIXBUF_INLINE_METADATA);
288                         break;
289                 case FORMAT_CLASS_VIDEO:
290                         pixbuf = pixbuf_inline(PIXBUF_INLINE_VIDEO);
291                         break;
292                 case FORMAT_CLASS_COLLECTION:
293                         pixbuf = pixbuf_inline(PIXBUF_INLINE_COLLECTION);
294                         break;
295                 case FORMAT_CLASS_PDF:
296                         pixbuf = pixbuf_inline(PIXBUF_INLINE_ICON_PDF);
297                         break;
298                 default:
299                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
300                 }
301
302         if (requested_width && requested_height)
303                 {
304                 gint w = gdk_pixbuf_get_width(pixbuf);
305                 gint h = gdk_pixbuf_get_height(pixbuf);
306
307                 if (w > requested_width || h > requested_height)
308                         {
309                         gint nw, nh;
310
311                         if (pixbuf_scale_aspect(requested_width, requested_height,
312                                                           w, h, &nw, &nh))
313                                 {
314                                 GdkPixbuf *tmp;
315
316                                 tmp = pixbuf;
317                                 pixbuf = gdk_pixbuf_scale_simple(tmp, nw, nh, GDK_INTERP_TILES);
318                                 g_object_unref(G_OBJECT(tmp));
319                                 }
320                         }
321                 }
322         return pixbuf;
323 }
324
325
326 /*
327  *-----------------------------------------------------------------------------
328  * misc utils
329  *-----------------------------------------------------------------------------
330  */
331
332 gboolean util_clip_region(gint x, gint y, gint w, gint h,
333                           gint clip_x, gint clip_y, gint clip_w, gint clip_h,
334                           gint *rx, gint *ry, gint *rw, gint *rh)
335 {
336         if (clip_x + clip_w <= x ||
337             clip_x >= x + w ||
338             clip_y + clip_h <= y ||
339             clip_y >= y + h)
340                 {
341                 return FALSE;
342                 }
343
344         *rx = MAX(x, clip_x);
345         *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
346
347         *ry = MAX(y, clip_y);
348         *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
349
350         return TRUE;
351 }
352
353 /*
354  *-----------------------------------------------------------------------------
355  * pixbuf rotation
356  *-----------------------------------------------------------------------------
357  */
358
359 static void pixbuf_copy_block_rotate(guchar *src, gint src_row_stride, gint x, gint y,
360                                      guchar *dest, gint dest_row_stride, gint w, gint h,
361                                      gint bytes_per_pixel, gboolean counter_clockwise)
362 {
363         gint i, j;
364         guchar *sp;
365         guchar *dp;
366
367         for (i = 0; i < h; i++)
368                 {
369                 sp = src + ((i + y) * src_row_stride) + (x * bytes_per_pixel);
370                 for (j = 0; j < w; j++)
371                         {
372                         if (counter_clockwise)
373                                 {
374                                 dp = dest + ((w - j - 1) * dest_row_stride) + (i * bytes_per_pixel);
375                                 }
376                         else
377                                 {
378                                 dp = dest + (j * dest_row_stride) + ((h - i - 1) * bytes_per_pixel);
379                                 }
380                         *(dp++) = *(sp++);      /* r */
381                         *(dp++) = *(sp++);      /* g */
382                         *(dp++) = *(sp++);      /* b */
383                         if (bytes_per_pixel == 4) *(dp) = *(sp++);      /* a */
384                         }
385                 }
386
387 }
388
389 static void pixbuf_copy_block(guchar *src, gint src_row_stride, gint w, gint h,
390                               guchar *dest, gint dest_row_stride, gint x, gint y, gint bytes_per_pixel)
391 {
392         gint i;
393         guchar *sp;
394         guchar *dp;
395
396         for (i = 0; i < h; i++)
397                 {
398                 sp = src + (i * src_row_stride);
399                 dp = dest + ((y + i) * dest_row_stride) + (x * bytes_per_pixel);
400                 memcpy(dp, sp, w * bytes_per_pixel);
401                 }
402 }
403
404 #define ROTATE_BUFFER_WIDTH 48
405 #define ROTATE_BUFFER_HEIGHT 48
406
407 /*
408  * Returns a copy of pixbuf src rotated 90 degrees clockwise or 90 counterclockwise
409  *
410  */
411 GdkPixbuf *pixbuf_copy_rotate_90(GdkPixbuf *src, gboolean counter_clockwise)
412 {
413         GdkPixbuf *dest;
414         gboolean has_alpha;
415         gint sw, sh, srs;
416         gint dw, dh, drs;
417         guchar *s_pix;
418         guchar *d_pix;
419         gint i, j;
420         gint a;
421         GdkPixbuf *buffer;
422         guchar *b_pix;
423         gint brs;
424         gint w, h;
425
426         if (!src) return NULL;
427
428         sw = gdk_pixbuf_get_width(src);
429         sh = gdk_pixbuf_get_height(src);
430         has_alpha = gdk_pixbuf_get_has_alpha(src);
431         srs = gdk_pixbuf_get_rowstride(src);
432         s_pix = gdk_pixbuf_get_pixels(src);
433
434         dw = sh;
435         dh = sw;
436         dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, dw, dh);
437         drs = gdk_pixbuf_get_rowstride(dest);
438         d_pix = gdk_pixbuf_get_pixels(dest);
439
440         a = (has_alpha ? 4 : 3);
441
442         buffer = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8,
443                                 ROTATE_BUFFER_WIDTH, ROTATE_BUFFER_HEIGHT);
444         b_pix = gdk_pixbuf_get_pixels(buffer);
445         brs = gdk_pixbuf_get_rowstride(buffer);
446
447         for (i = 0; i < sh; i+= ROTATE_BUFFER_WIDTH)
448                 {
449                 w = MIN(ROTATE_BUFFER_WIDTH, (sh - i));
450                 for (j = 0; j < sw; j += ROTATE_BUFFER_HEIGHT)
451                         {
452                         gint x, y;
453
454                         h = MIN(ROTATE_BUFFER_HEIGHT, (sw - j));
455                         pixbuf_copy_block_rotate(s_pix, srs, j, i,
456                                                  b_pix, brs, h, w,
457                                                  a, counter_clockwise);
458
459                         if (counter_clockwise)
460                                 {
461                                 x = i;
462                                 y = sw - h - j;
463                                 }
464                         else
465                                 {
466                                 x = sh - w - i;
467                                 y = j;
468                                 }
469                         pixbuf_copy_block(b_pix, brs, w, h,
470                                           d_pix, drs, x, y, a);
471                         }
472                 }
473
474         g_object_unref(buffer);
475
476 #if 0
477         /* this is the simple version of rotation (roughly 2-4x slower) */
478
479         for (i = 0; i < sh; i++)
480                 {
481                 sp = s_pix + (i * srs);
482                 for (j = 0; j < sw; j++)
483                         {
484                         if (counter_clockwise)
485                                 {
486                                 dp = d_pix + ((dh - j - 1) * drs) + (i * a);
487                                 }
488                         else
489                                 {
490                                 dp = d_pix + (j * drs) + ((dw - i - 1) * a);
491                                 }
492
493                         *(dp++) = *(sp++);      /* r */
494                         *(dp++) = *(sp++);      /* g */
495                         *(dp++) = *(sp++);      /* b */
496                         if (has_alpha) *(dp) = *(sp++); /* a */
497                         }
498                 }
499 #endif
500
501         return dest;
502 }
503
504 /*
505  * Returns a copy of pixbuf mirrored and or flipped.
506  * TO do a 180 degree rotations set both mirror and flipped TRUE
507  * if mirror and flip are FALSE, result is a simple copy.
508  */
509 GdkPixbuf *pixbuf_copy_mirror(GdkPixbuf *src, gboolean mirror, gboolean flip)
510 {
511         GdkPixbuf *dest;
512         gboolean has_alpha;
513         gint w, h, srs;
514         gint drs;
515         guchar *s_pix;
516         guchar *d_pix;
517         guchar *sp;
518         guchar *dp;
519         gint i, j;
520         gint a;
521
522         if (!src) return NULL;
523
524         w = gdk_pixbuf_get_width(src);
525         h = gdk_pixbuf_get_height(src);
526         has_alpha = gdk_pixbuf_get_has_alpha(src);
527         srs = gdk_pixbuf_get_rowstride(src);
528         s_pix = gdk_pixbuf_get_pixels(src);
529
530         dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, w, h);
531         drs = gdk_pixbuf_get_rowstride(dest);
532         d_pix = gdk_pixbuf_get_pixels(dest);
533
534         a = has_alpha ? 4 : 3;
535
536         for (i = 0; i < h; i++)
537                 {
538                 sp = s_pix + (i * srs);
539                 if (flip)
540                         {
541                         dp = d_pix + ((h - i - 1) * drs);
542                         }
543                 else
544                         {
545                         dp = d_pix + (i * drs);
546                         }
547                 if (mirror)
548                         {
549                         dp += (w - 1) * a;
550                         for (j = 0; j < w; j++)
551                                 {
552                                 *(dp++) = *(sp++);      /* r */
553                                 *(dp++) = *(sp++);      /* g */
554                                 *(dp++) = *(sp++);      /* b */
555                                 if (has_alpha) *(dp) = *(sp++); /* a */
556                                 dp -= (a + 3);
557                                 }
558                         }
559                 else
560                         {
561                         for (j = 0; j < w; j++)
562                                 {
563                                 *(dp++) = *(sp++);      /* r */
564                                 *(dp++) = *(sp++);      /* g */
565                                 *(dp++) = *(sp++);      /* b */
566                                 if (has_alpha) *(dp++) = *(sp++);       /* a */
567                                 }
568                         }
569                 }
570
571         return dest;
572 }
573
574 GdkPixbuf *pixbuf_apply_orientation(GdkPixbuf *pixbuf, gint orientation)
575 {
576         GdkPixbuf *dest;
577         GdkPixbuf *tmp = NULL;
578
579         switch (orientation)
580                 {
581                 case EXIF_ORIENTATION_TOP_LEFT:
582                         dest = gdk_pixbuf_copy(pixbuf);
583                         break;
584                 case EXIF_ORIENTATION_TOP_RIGHT:
585                         /* mirrored */
586                         dest = pixbuf_copy_mirror(pixbuf, TRUE, FALSE);
587                         break;
588                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
589                         /* upside down */
590                         dest = pixbuf_copy_mirror(pixbuf, TRUE, TRUE);
591                         break;
592                 case EXIF_ORIENTATION_BOTTOM_LEFT:
593                         /* flipped */
594                         dest = pixbuf_copy_mirror(pixbuf, FALSE, TRUE);
595                         break;
596                 case EXIF_ORIENTATION_LEFT_TOP:
597                         tmp = pixbuf_copy_mirror(pixbuf, FALSE, TRUE);
598                         dest = pixbuf_copy_rotate_90(tmp, FALSE);
599                         break;
600                 case EXIF_ORIENTATION_RIGHT_TOP:
601                         /* rotated -90 (270) */
602                         dest = pixbuf_copy_rotate_90(pixbuf, FALSE);
603                         break;
604                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
605                         tmp = pixbuf_copy_mirror(pixbuf, FALSE, TRUE);
606                         dest = pixbuf_copy_rotate_90(tmp, TRUE);
607                         break;
608                 case EXIF_ORIENTATION_LEFT_BOTTOM:
609                         /* rotated 90 */
610                         dest = pixbuf_copy_rotate_90(pixbuf, TRUE);
611                         break;
612                 default:
613                         dest = gdk_pixbuf_copy(pixbuf);
614                         break;
615                 }
616         if (tmp) g_object_unref(tmp);
617         return dest;
618
619 }
620
621
622 /*
623  *-----------------------------------------------------------------------------
624  * pixbuf drawing (rectangles)
625  *-----------------------------------------------------------------------------
626  */
627
628 /*
629  * Fills region of pixbuf at x,y over w,h
630  * with colors red (r), green (g), blue (b)
631  * applying alpha (a), use a=255 for solid.
632  */
633 void pixbuf_draw_rect_fill(GdkPixbuf *pb,
634                            gint x, gint y, gint w, gint h,
635                            gint r, gint g, gint b, gint a)
636 {
637         gboolean has_alpha;
638         gint pw, ph, prs;
639         guchar *p_pix;
640         guchar *pp;
641         gint i, j;
642
643         if (!pb) return;
644
645         pw = gdk_pixbuf_get_width(pb);
646         ph = gdk_pixbuf_get_height(pb);
647
648         if (x < 0 || x + w > pw) return;
649         if (y < 0 || y + h > ph) return;
650
651         has_alpha = gdk_pixbuf_get_has_alpha(pb);
652         prs = gdk_pixbuf_get_rowstride(pb);
653         p_pix = gdk_pixbuf_get_pixels(pb);
654
655         for (i = 0; i < h; i++)
656                 {
657                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
658                 for (j = 0; j < w; j++)
659                         {
660                         *pp = (r * a + *pp * (256-a)) >> 8;
661                         pp++;
662                         *pp = (g * a + *pp * (256-a)) >> 8;
663                         pp++;
664                         *pp = (b * a + *pp * (256-a)) >> 8;
665                         pp++;
666                         if (has_alpha) pp++;
667                         }
668                 }
669 }
670
671 void pixbuf_draw_rect(GdkPixbuf *pb,
672                       gint x, gint y, gint w, gint h,
673                       gint r, gint g, gint b, gint a,
674                       gint left, gint right, gint top, gint bottom)
675 {
676         pixbuf_draw_rect_fill(pb, x + left, y, w - left - right, top,
677                               r, g, b ,a);
678         pixbuf_draw_rect_fill(pb, x + w - right, y, right, h,
679                               r, g, b ,a);
680         pixbuf_draw_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
681                               r, g, b ,a);
682         pixbuf_draw_rect_fill(pb, x, y, left, h,
683                               r, g, b ,a);
684 }
685
686 void pixbuf_set_rect_fill(GdkPixbuf *pb,
687                           gint x, gint y, gint w, gint h,
688                           gint r, gint g, gint b, gint a)
689 {
690         gboolean has_alpha;
691         gint pw, ph, prs;
692         guchar *p_pix;
693         guchar *pp;
694         gint i, j;
695
696         if (!pb) return;
697
698         pw = gdk_pixbuf_get_width(pb);
699         ph = gdk_pixbuf_get_height(pb);
700
701         if (x < 0 || x + w > pw) return;
702         if (y < 0 || y + h > ph) return;
703
704         has_alpha = gdk_pixbuf_get_has_alpha(pb);
705         prs = gdk_pixbuf_get_rowstride(pb);
706         p_pix = gdk_pixbuf_get_pixels(pb);
707
708         for (i = 0; i < h; i++)
709                 {
710                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
711                 for (j = 0; j < w; j++)
712                         {
713                         *pp = r; pp++;
714                         *pp = g; pp++;
715                         *pp = b; pp++;
716                         if (has_alpha) { *pp = a; pp++; }
717                         }
718                 }
719 }
720
721 void pixbuf_set_rect(GdkPixbuf *pb,
722                      gint x, gint y, gint w, gint h,
723                      gint r, gint g, gint b, gint a,
724                      gint left, gint right, gint top, gint bottom)
725 {
726         pixbuf_set_rect_fill(pb, x + left, y, w - left - right, top,
727                              r, g, b ,a);
728         pixbuf_set_rect_fill(pb, x + w - right, y, right, h,
729                              r, g, b ,a);
730         pixbuf_set_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
731                              r, g, b ,a);
732         pixbuf_set_rect_fill(pb, x, y, left, h,
733                              r, g, b ,a);
734 }
735
736 void pixbuf_pixel_set(GdkPixbuf *pb, gint x, gint y, gint r, gint g, gint b, gint a)
737 {
738         guchar *buf;
739         gboolean has_alpha;
740         gint rowstride;
741         guchar *p;
742
743         if (x < 0 || x >= gdk_pixbuf_get_width(pb) ||
744             y < 0 || y >= gdk_pixbuf_get_height(pb)) return;
745
746         buf = gdk_pixbuf_get_pixels(pb);
747         has_alpha = gdk_pixbuf_get_has_alpha(pb);
748         rowstride = gdk_pixbuf_get_rowstride(pb);
749
750         p = buf + (y * rowstride) + (x * (has_alpha ? 4 : 3));
751         *p = r; p++;
752         *p = g; p++;
753         *p = b; p++;
754         if (has_alpha) *p = a;
755 }
756
757
758 /*
759  *-----------------------------------------------------------------------------
760  * pixbuf text rendering
761  *-----------------------------------------------------------------------------
762  */
763
764 static void pixbuf_copy_font(GdkPixbuf *src, gint sx, gint sy,
765                              GdkPixbuf *dest, gint dx, gint dy,
766                              gint w, gint h,
767                              guint8 r, guint8 g, guint8 b, guint8 a)
768 {
769         gint sw, sh, srs;
770         gboolean s_alpha;
771         gint s_step;
772         guchar *s_pix;
773         gint dw, dh, drs;
774         gboolean d_alpha;
775         gint d_step;
776         guchar *d_pix;
777
778         guchar *sp;
779         guchar *dp;
780         gint i, j;
781
782         if (!src || !dest) return;
783
784         sw = gdk_pixbuf_get_width(src);
785         sh = gdk_pixbuf_get_height(src);
786
787         if (sx < 0 || sx + w > sw) return;
788         if (sy < 0 || sy + h > sh) return;
789
790         dw = gdk_pixbuf_get_width(dest);
791         dh = gdk_pixbuf_get_height(dest);
792
793         if (dx < 0 || dx + w > dw) return;
794         if (dy < 0 || dy + h > dh) return;
795
796         s_alpha = gdk_pixbuf_get_has_alpha(src);
797         d_alpha = gdk_pixbuf_get_has_alpha(dest);
798         srs = gdk_pixbuf_get_rowstride(src);
799         drs = gdk_pixbuf_get_rowstride(dest);
800         s_pix = gdk_pixbuf_get_pixels(src);
801         d_pix = gdk_pixbuf_get_pixels(dest);
802
803         s_step = (s_alpha) ? 4 : 3;
804         d_step = (d_alpha) ? 4 : 3;
805
806         for (i = 0; i < h; i++)
807                 {
808                 sp = s_pix + (sy + i) * srs + sx * s_step;
809                 dp = d_pix + (dy + i) * drs + dx * d_step;
810                 for (j = 0; j < w; j++)
811                         {
812                         if (*sp)
813                                 {
814                                 guint8 asub;
815
816                                 asub = a * sp[0] / 255;
817                                 *dp = (r * asub + *dp * (256-asub)) >> 8;
818                                 dp++;
819                                 asub = a * sp[1] / 255;
820                                 *dp = (g * asub + *dp * (256-asub)) >> 8;
821                                 dp++;
822                                 asub = a * sp[2] / 255;
823                                 *dp = (b * asub + *dp * (256-asub)) >> 8;
824                                 dp++;
825
826                                 if (d_alpha)
827                                         {
828                                         *dp = MAX(*dp, a * ((sp[0] + sp[1] + sp[2]) / 3) / 255);
829                                         dp++;
830                                         }
831                                 }
832                         else
833                                 {
834                                 dp += d_step;
835                                 }
836
837                         sp += s_step;
838                         }
839                 }
840 }
841
842 void pixbuf_draw_layout(GdkPixbuf *pixbuf, PangoLayout *layout, GtkWidget *widget,
843                         gint x, gint y,
844                         guint8 r, guint8 g, guint8 b, guint8 a)
845 {
846         GdkPixbuf *buffer;
847         gint w, h;
848         gint sx, sy;
849         gint dw, dh;
850         cairo_surface_t *source;
851         cairo_t *cr;
852
853         pango_layout_get_pixel_size(layout, &w, &h);
854         if (w < 1 || h < 1) return;
855
856         source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
857
858         cr = cairo_create (source);
859         cairo_set_source_rgb(cr, 0, 0, 0);
860         cairo_rectangle (cr, 0, 0, w, h);
861         cairo_fill (cr);
862         cairo_set_source_rgb(cr, 1, 1, 1);
863         pango_cairo_show_layout (cr, layout);
864         cairo_destroy (cr);
865
866         buffer = gdk_pixbuf_new_from_data (cairo_image_surface_get_data (source),
867                                            GDK_COLORSPACE_RGB,
868                                            cairo_image_surface_get_format (source) == CAIRO_FORMAT_ARGB32,
869                                            8,
870                                            cairo_image_surface_get_width (source),
871                                            cairo_image_surface_get_height (source),
872                                            cairo_image_surface_get_stride (source),
873                                            NULL,
874                                            NULL);
875
876         sx = 0;
877         sy = 0;
878         dw = gdk_pixbuf_get_width(pixbuf);
879         dh = gdk_pixbuf_get_height(pixbuf);
880
881         if (x < 0)
882                 {
883                 w += x;
884                 sx = -x;
885                 x = 0;
886                 }
887
888         if (y < 0)
889                 {
890                 h += y;
891                 sy = -y;
892                 y = 0;
893                 }
894
895         if (x + w > dw) w = dw - x;
896         if (y + h > dh) h = dh - y;
897
898         pixbuf_copy_font(buffer, sx, sy,
899                          pixbuf, x, y, w, h,
900                          r, g, b, a);
901
902         g_object_unref(buffer);
903         cairo_surface_destroy(source);
904 }
905
906 /*
907  *-----------------------------------------------------------------------------
908  * pixbuf drawing (triangle)
909  *-----------------------------------------------------------------------------
910  */
911
912 void util_clip_triangle(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
913                         gint *rx, gint *ry, gint *rw, gint *rh)
914 {
915         gint tx, ty, tw, th;
916
917         tx = MIN(x1, x2);
918         tx = MIN(tx, x3);
919         ty = MIN(y1, y2);
920         ty = MIN(ty, y3);
921         tw = MAX(abs(x1 - x2), abs(x2 - x3));
922         tw = MAX(tw, abs(x3 - x1));
923         th = MAX(abs(y1 - y2), abs(y2 - y3));
924         th = MAX(th, abs(y3 - y1));
925
926         *rx = tx;
927         *ry = ty;
928         *rw = tw;
929         *rh = th;
930 }
931
932 void pixbuf_draw_triangle(GdkPixbuf *pb,
933                           gint clip_x, gint clip_y, gint clip_w, gint clip_h,
934                           gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
935                           guint8 r, guint8 g, guint8 b, guint8 a)
936 {
937         gboolean has_alpha;
938         gint pw, ph, prs;
939         gint rx, ry, rw, rh;
940         gint tx, ty, tw, th;
941         gint fx1, fy1;
942         gint fx2, fy2;
943         gint fw, fh;
944         guchar *p_pix;
945         guchar *pp;
946         gint p_step;
947         gdouble slope1, slope2;
948         gint slope1_x, slope1_y;
949         gint y;
950         gint t;
951         gboolean middle = FALSE;
952
953         if (!pb) return;
954
955         pw = gdk_pixbuf_get_width(pb);
956         ph = gdk_pixbuf_get_height(pb);
957
958         if (!util_clip_region(0, 0, pw, ph,
959                               clip_x, clip_y, clip_w, clip_h,
960                               &rx, &ry, &rw, &rh)) return;
961
962         util_clip_triangle(x1, y1, x2, y2, x3, y3,
963                            &tx, &ty, &tw, &th);
964
965         if (!util_clip_region(rx, ry, rw, rh,
966                               tx, ty, tw, th,
967                               &fx1, &fy1, &fw, &fh)) return;
968         fx2 = fx1 + fw;
969         fy2 = fy1 + fh;
970
971         has_alpha = gdk_pixbuf_get_has_alpha(pb);
972         prs = gdk_pixbuf_get_rowstride(pb);
973         p_pix = gdk_pixbuf_get_pixels(pb);
974
975         p_step = (has_alpha) ? 4 : 3;
976
977         if (y1 > y2)
978                 {
979                 t = x1; x1 = x2; x2 = t;
980                 t = y1; y1 = y2; y2 = t;
981                 }
982         if (y2 > y3)
983                 {
984                 t = x2; x2 = x3; x3 = t;
985                 t = y2; y2 = y3; y3 = t;
986                 }
987         if (y1 > y2)
988                 {
989                 t = x1; x1 = x2; x2 = t;
990                 t = y1; y1 = y2; y2 = t;
991                 }
992
993         slope1 = (gdouble)(y2 - y1);
994         if (slope1) slope1 = (gdouble)(x2 - x1) / slope1;
995         slope1_x = x1;
996         slope1_y = y1;
997         slope2 = (gdouble)(y3 - y1);
998         if (slope2) slope2 = (gdouble)(x3 - x1) / slope2;
999
1000         for (y = fy1; y < fy2; y++)
1001                 {
1002                 gint xa, xb;
1003
1004                 if (!middle && y > y2)
1005                         {
1006                         slope1 = (gdouble)(y3 - y2);
1007                         if (slope1) slope1 = (gdouble)(x3 - x2) / slope1;
1008                         slope1_x = x2;
1009                         slope1_y = y2;
1010
1011                         middle = TRUE;
1012                         }
1013
1014                 xa = slope1_x + ((gdouble)slope1 * (y - slope1_y) + 0.5);
1015                 xb = x1 + ((gdouble)slope2 * (y - y1) + 0.5);
1016
1017                 if (xa > xb)
1018                         {
1019                         t = xa; xa = xb; xb = t;
1020                         }
1021
1022                 xa = CLAMP(xa, fx1, fx2);
1023                 xb = CLAMP(xb, fx1, fx2);
1024
1025                 pp = p_pix + y * prs + xa * p_step;
1026
1027                 while (xa < xb)
1028                         {
1029                         *pp = (r * a + *pp * (256-a)) >> 8;
1030                         pp++;
1031                         *pp = (g * a + *pp * (256-a)) >> 8;
1032                         pp++;
1033                         *pp = (b * a + *pp * (256-a)) >> 8;
1034                         pp++;
1035                         if (has_alpha) pp++;
1036
1037                         xa++;
1038                         }
1039                 }
1040 }
1041
1042 /*
1043  *-----------------------------------------------------------------------------
1044  * pixbuf drawing (line)
1045  *-----------------------------------------------------------------------------
1046  */
1047
1048 static gboolean util_clip_line(gdouble clip_x, gdouble clip_y, gdouble clip_w, gdouble clip_h,
1049                                gdouble x1, gdouble y1, gdouble x2, gdouble y2,
1050                                gdouble *rx1, gdouble *ry1, gdouble *rx2, gdouble *ry2)
1051 {
1052         gboolean flip = FALSE;
1053         gdouble d;
1054
1055         if (x1 > x2)
1056                 {
1057                 gdouble t;
1058
1059                 t = x1; x1 = x2; x2 = t;
1060                 t = y1; y1 = y2; y2 = t;
1061                 flip = TRUE;
1062                 }
1063
1064         if (x2 < clip_x || x1 > clip_x + clip_w) return FALSE;
1065
1066         if (y1 < y2)
1067                 {
1068                 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
1069                 }
1070         else
1071                 {
1072                 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
1073                 }
1074
1075         d = x2 - x1;
1076         if (d > 0.0)
1077                 {
1078                 gdouble slope;
1079
1080                 slope = (y2 - y1) / d;
1081                 if (x1 < clip_x)
1082                         {
1083                         y1 = y1 + slope * (clip_x - x1);
1084                         x1 = clip_x;
1085                         }
1086                 if (x2 > clip_x + clip_w)
1087                         {
1088                         y2 = y2 + slope * (clip_x + clip_w - x2);
1089                         x2 = clip_x + clip_w;
1090                         }
1091                 }
1092
1093         if (y1 < y2)
1094                 {
1095                 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
1096                 }
1097         else
1098                 {
1099                 gdouble t;
1100
1101                 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
1102
1103                 t = x1; x1 = x2; x2 = t;
1104                 t = y1; y1 = y2; y2 = t;
1105                 flip = !flip;
1106                 }
1107
1108         d = y2 - y1;
1109         if (d > 0.0)
1110                 {
1111                 gdouble slope;
1112
1113                 slope = (x2 - x1) / d;
1114                 if (y1 < clip_y)
1115                         {
1116                         x1 = x1 + slope * (clip_y - y1);
1117                         y1 = clip_y;
1118                         }
1119                 if (y2 > clip_y + clip_h)
1120                         {
1121                         x2 = x2 + slope * (clip_y + clip_h - y2);
1122                         y2 = clip_y + clip_h;
1123                         }
1124                 }
1125
1126         if (flip)
1127                 {
1128                 *rx1 = x2;
1129                 *ry1 = y2;
1130                 *rx2 = x1;
1131                 *ry2 = y1;
1132                 }
1133         else
1134                 {
1135                 *rx1 = x1;
1136                 *ry1 = y1;
1137                 *rx2 = x2;
1138                 *ry2 = y2;
1139                 }
1140
1141         return TRUE;
1142 }
1143
1144 void pixbuf_draw_line(GdkPixbuf *pb,
1145                       gint clip_x, gint clip_y, gint clip_w, gint clip_h,
1146                       gint x1, gint y1, gint x2, gint y2,
1147                       guint8 r, guint8 g, guint8 b, guint8 a)
1148 {
1149         gboolean has_alpha;
1150         gint pw, ph, prs;
1151         gint rx, ry, rw, rh;
1152         gdouble rx1, ry1, rx2, ry2;
1153         guchar *p_pix;
1154         guchar *pp;
1155         gint p_step;
1156         gdouble slope;
1157         gdouble x, y;
1158         gint px, py;
1159         gint cx1, cy1, cx2, cy2;
1160
1161         if (!pb) return;
1162
1163         pw = gdk_pixbuf_get_width(pb);
1164         ph = gdk_pixbuf_get_height(pb);
1165
1166         if (!util_clip_region(0, 0, pw, ph,
1167                               clip_x, clip_y, clip_w, clip_h,
1168                               &rx, &ry, &rw, &rh)) return;
1169         if (!util_clip_line((gdouble)rx, (gdouble)ry, (gdouble)rw, (gdouble)rh,
1170                             (gdouble)x1, (gdouble)y1, (gdouble)x2, (gdouble)y2,
1171                             &rx1, &ry1, &rx2, &ry2)) return;
1172
1173         cx1 = rx;
1174         cy1 = ry;
1175         cx2 = rx + rw;
1176         cy2 = ry + rh;
1177
1178         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1179         prs = gdk_pixbuf_get_rowstride(pb);
1180         p_pix = gdk_pixbuf_get_pixels(pb);
1181
1182         p_step = (has_alpha) ? 4 : 3;
1183
1184         if (fabs(rx2 - rx1) > fabs(ry2 - ry1))
1185                 {
1186                 if (rx1 > rx2)
1187                         {
1188                         gdouble t;
1189                         t = rx1; rx1 = rx2; rx2 = t;
1190                         t = ry1; ry1 = ry2; ry2 = t;
1191                         }
1192
1193                 slope = rx2 - rx1;
1194                 if (slope != 0.0) slope = (ry2 - ry1) / slope;
1195                 for (x = rx1; x < rx2; x += 1.0)
1196                         {
1197                         px = (gint)(x + 0.5);
1198                         py = (gint)(ry1 + (x - rx1) * slope + 0.5);
1199
1200                         if (px >=  cx1 && px < cx2 && py >= cy1 && py < cy2)
1201                                 {
1202                                 pp = p_pix + py * prs + px * p_step;
1203                                 *pp = (r * a + *pp * (256-a)) >> 8;
1204                                 pp++;
1205                                 *pp = (g * a + *pp * (256-a)) >> 8;
1206                                 pp++;
1207                                 *pp = (b * a + *pp * (256-a)) >> 8;
1208                                 }
1209                         }
1210                 }
1211         else
1212                 {
1213                 if (ry1 > ry2)
1214                         {
1215                         gdouble t;
1216                         t = rx1; rx1 = rx2; rx2 = t;
1217                         t = ry1; ry1 = ry2; ry2 = t;
1218                         }
1219
1220                 slope = ry2 - ry1;
1221                 if (slope != 0.0) slope = (rx2 - rx1) / slope;
1222                 for (y = ry1; y < ry2; y += 1.0)
1223                         {
1224                         px = (gint)(rx1 + (y - ry1) * slope + 0.5);
1225                         py = (gint)(y + 0.5);
1226
1227                         if (px >=  cx1 && px < cx2 && py >= cy1 && py < cy2)
1228                                 {
1229                                 pp = p_pix + py * prs + px * p_step;
1230                                 *pp = (r * a + *pp * (256-a)) >> 8;
1231                                 pp++;
1232                                 *pp = (g * a + *pp * (256-a)) >> 8;
1233                                 pp++;
1234                                 *pp = (b * a + *pp * (256-a)) >> 8;
1235                                 }
1236                         }
1237                 }
1238 }
1239
1240 /*
1241  *-----------------------------------------------------------------------------
1242  * pixbuf drawing (fades and shadows)
1243  *-----------------------------------------------------------------------------
1244  */
1245
1246 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gboolean has_alpha,
1247                                     gint s, gboolean vertical, gint border,
1248                                     gint x1, gint y1, gint x2, gint y2,
1249                                     guint8 r, guint8 g, guint8 b, guint8 a)
1250 {
1251         guchar *pp;
1252         gint p_step;
1253         guint8 n = a;
1254         gint i, j;
1255
1256         p_step = (has_alpha) ? 4 : 3;
1257         for (j = y1; j < y2; j++)
1258                 {
1259                 pp = p_pix + j * prs + x1 * p_step;
1260                 if (!vertical) n = a - a * abs(j - s) / border;
1261                 for (i = x1; i < x2; i++)
1262                         {
1263                         if (vertical) n = a - a * abs(i - s) / border;
1264                         *pp = (r * n + *pp * (256-n)) >> 8;
1265                         pp++;
1266                         *pp = (g * n + *pp * (256-n)) >> 8;
1267                         pp++;
1268                         *pp = (b * n + *pp * (256-n)) >> 8;
1269                         pp++;
1270                         if (has_alpha) pp++;
1271                         }
1272                 }
1273 }
1274
1275 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gboolean has_alpha,
1276                                     gint sx, gint sy, gint border,
1277                                     gint x1, gint y1, gint x2, gint y2,
1278                                     guint8 r, guint8 g, guint8 b, guint8 a)
1279 {
1280         guchar *pp;
1281         gint p_step;
1282         gint i, j;
1283
1284         p_step = (has_alpha) ? 4 : 3;
1285         for (j = y1; j < y2; j++)
1286                 {
1287                 pp = p_pix + j * prs + x1 * p_step;
1288                 for (i = x1; i < x2; i++)
1289                         {
1290                         guint8 n;
1291                         gint r;
1292
1293                         r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
1294                         n = a - a * r / border;
1295                         *pp = (r * n + *pp * (256-n)) >> 8;
1296                         pp++;
1297                         *pp = (g * n + *pp * (256-n)) >> 8;
1298                         pp++;
1299                         *pp = (b * n + *pp * (256-n)) >> 8;
1300                         pp++;
1301                         if (has_alpha) pp++;
1302                         }
1303                 }
1304 }
1305
1306 void pixbuf_draw_shadow(GdkPixbuf *pb,
1307                         gint clip_x, gint clip_y, gint clip_w, gint clip_h,
1308                         gint x, gint y, gint w, gint h, gint border,
1309                         guint8 r, guint8 g, guint8 b, guint8 a)
1310 {
1311         gint has_alpha;
1312         gint pw, ph, prs;
1313         gint rx, ry, rw, rh;
1314         gint fx, fy, fw, fh;
1315         guchar *p_pix;
1316
1317         if (!pb) return;
1318
1319         pw = gdk_pixbuf_get_width(pb);
1320         ph = gdk_pixbuf_get_height(pb);
1321
1322         if (!util_clip_region(0, 0, pw, ph,
1323                               clip_x, clip_y, clip_w, clip_h,
1324                               &rx, &ry, &rw, &rh)) return;
1325
1326         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1327         prs = gdk_pixbuf_get_rowstride(pb);
1328         p_pix = gdk_pixbuf_get_pixels(pb);
1329
1330         if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
1331                              rx, ry, rw, rh,
1332                              &fx, &fy, &fw, &fh))
1333                 {
1334                 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
1335                 }
1336
1337         if (border < 1) return;
1338
1339         if (util_clip_region(x, y + border, border, h - border * 2,
1340                              rx, ry, rw, rh,
1341                              &fx, &fy, &fw, &fh))
1342                 {
1343                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1344                                         x + border, TRUE, border,
1345                                         fx, fy, fx + fw, fy + fh,
1346                                         r, g, b, a);
1347                 }
1348         if (util_clip_region(x + w - border, y + border, border, h - border * 2,
1349                              rx, ry, rw, rh,
1350                              &fx, &fy, &fw, &fh))
1351                 {
1352                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1353                                         x + w - border, TRUE, border,
1354                                         fx, fy, fx + fw, fy + fh,
1355                                         r, g, b, a);
1356                 }
1357         if (util_clip_region(x + border, y, w - border * 2, border,
1358                              rx, ry, rw, rh,
1359                              &fx, &fy, &fw, &fh))
1360                 {
1361                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1362                                         y + border, FALSE, border,
1363                                         fx, fy, fx + fw, fy + fh,
1364                                         r, g, b, a);
1365                 }
1366         if (util_clip_region(x + border, y + h - border, w - border * 2, border,
1367                              rx, ry, rw, rh,
1368                              &fx, &fy, &fw, &fh))
1369                 {
1370                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1371                                         y + h - border, FALSE, border,
1372                                         fx, fy, fx + fw, fy + fh,
1373                                         r, g, b, a);
1374                 }
1375         if (util_clip_region(x, y, border, border,
1376                              rx, ry, rw, rh,
1377                              &fx, &fy, &fw, &fh))
1378                 {
1379                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1380                                         x + border, y + border, border,
1381                                         fx, fy, fx + fw, fy + fh,
1382                                         r, g, b, a);
1383                 }
1384         if (util_clip_region(x + w - border, y, border, border,
1385                              rx, ry, rw, rh,
1386                              &fx, &fy, &fw, &fh))
1387                 {
1388                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1389                                         x + w - border, y + border, border,
1390                                         fx, fy, fx + fw, fy + fh,
1391                                         r, g, b, a);
1392                 }
1393         if (util_clip_region(x, y + h - border, border, border,
1394                              rx, ry, rw, rh,
1395                              &fx, &fy, &fw, &fh))
1396                 {
1397                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1398                                         x + border, y + h - border, border,
1399                                         fx, fy, fx + fw, fy + fh,
1400                                         r, g, b, a);
1401                 }
1402         if (util_clip_region(x + w - border, y + h - border, border, border,
1403                              rx, ry, rw, rh,
1404                              &fx, &fy, &fw, &fh))
1405                 {
1406                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1407                                         x + w - border, y + h - border, border,
1408                                         fx, fy, fx + fw, fy + fh,
1409                                         r, g, b, a);
1410                 }
1411 }
1412
1413
1414 /*
1415  *-----------------------------------------------------------------------------
1416  * pixbuf color alterations
1417  *-----------------------------------------------------------------------------
1418  */
1419
1420 void pixbuf_desaturate_rect(GdkPixbuf *pb,
1421                             gint x, gint y, gint w, gint h)
1422 {
1423         gboolean has_alpha;
1424         gint pw, ph, prs;
1425         guchar *p_pix;
1426         guchar *pp;
1427         gint i, j;
1428
1429         if (!pb) return;
1430
1431         pw = gdk_pixbuf_get_width(pb);
1432         ph = gdk_pixbuf_get_height(pb);
1433
1434         if (x < 0 || x + w > pw) return;
1435         if (y < 0 || y + h > ph) return;
1436
1437         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1438         prs = gdk_pixbuf_get_rowstride(pb);
1439         p_pix = gdk_pixbuf_get_pixels(pb);
1440
1441         for (i = 0; i < h; i++)
1442                 {
1443                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
1444                 for (j = 0; j < w; j++)
1445                         {
1446                         guint8 grey;
1447
1448                         grey = (pp[0] + pp[1] + pp[2]) / 3;
1449                         *pp = grey;
1450                         pp++;
1451                         *pp = grey;
1452                         pp++;
1453                         *pp = grey;
1454                         pp++;
1455                         if (has_alpha) pp++;
1456                         }
1457                 }
1458 }
1459 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */