4b2af513c8cba9ac63e935cf4b8291524f0bd163
[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                                 }
238                         g_free(icon2);
239                         }
240                 }
241
242         if (!pixbuf) return FALSE;
243
244         register_stock_icon(key, pixbuf);
245         return TRUE;
246 }
247
248 gboolean pixbuf_scale_aspect(gint req_w, gint req_h,
249                              gint old_w, gint old_h,
250                              gint *new_w, gint *new_h)
251 {
252         if (((gdouble)req_w / old_w) < ((gdouble)req_h / old_h))
253                 {
254                 *new_w = req_w;
255                 *new_h = (gdouble)*new_w / old_w * old_h;
256                 if (*new_h < 1) *new_h = 1;
257                 }
258         else
259                 {
260                 *new_h = req_h;
261                 *new_w = (gdouble)*new_h / old_h * old_w;
262                 if (*new_w < 1) *new_w = 1;
263                 }
264
265         return (*new_w != old_w || *new_h != old_h);
266 }
267
268 GdkPixbuf *pixbuf_fallback(FileData *fd, gint requested_width, gint requested_height)
269 {
270         GdkPixbuf *pixbuf;
271
272         switch (fd->format_class)
273                 {
274                 case FORMAT_CLASS_UNKNOWN:
275                         pixbuf = pixbuf_inline(PIXBUF_INLINE_UNKNOWN);
276                         break;
277                 case FORMAT_CLASS_META:
278                         pixbuf = pixbuf_inline(PIXBUF_INLINE_METADATA);
279                         break;
280                 case FORMAT_CLASS_VIDEO:
281                         pixbuf = pixbuf_inline(PIXBUF_INLINE_VIDEO);
282                         break;
283                 case FORMAT_CLASS_COLLECTION:
284                         pixbuf = pixbuf_inline(PIXBUF_INLINE_COLLECTION);
285                         break;
286                 case FORMAT_CLASS_PDF:
287                         pixbuf = pixbuf_inline(PIXBUF_INLINE_ICON_PDF);
288                         break;
289                 default:
290                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
291                 }
292
293         if (requested_width && requested_height)
294                 {
295                 gint w = gdk_pixbuf_get_width(pixbuf);
296                 gint h = gdk_pixbuf_get_height(pixbuf);
297
298                 if (w > requested_width || h > requested_height)
299                         {
300                         gint nw, nh;
301
302                         if (pixbuf_scale_aspect(requested_width, requested_height,
303                                                           w, h, &nw, &nh))
304                                 {
305                                 GdkPixbuf *tmp;
306
307                                 tmp = pixbuf;
308                                 pixbuf = gdk_pixbuf_scale_simple(tmp, nw, nh, GDK_INTERP_TILES);
309                                 g_object_unref(G_OBJECT(tmp));
310                                 }
311                         }
312                 }
313         return pixbuf;
314 }
315
316
317 /*
318  *-----------------------------------------------------------------------------
319  * misc utils
320  *-----------------------------------------------------------------------------
321  */
322
323 gboolean util_clip_region(gint x, gint y, gint w, gint h,
324                           gint clip_x, gint clip_y, gint clip_w, gint clip_h,
325                           gint *rx, gint *ry, gint *rw, gint *rh)
326 {
327         if (clip_x + clip_w <= x ||
328             clip_x >= x + w ||
329             clip_y + clip_h <= y ||
330             clip_y >= y + h)
331                 {
332                 return FALSE;
333                 }
334
335         *rx = MAX(x, clip_x);
336         *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
337
338         *ry = MAX(y, clip_y);
339         *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
340
341         return TRUE;
342 }
343
344 /*
345  *-----------------------------------------------------------------------------
346  * pixbuf rotation
347  *-----------------------------------------------------------------------------
348  */
349
350 static void pixbuf_copy_block_rotate(guchar *src, gint src_row_stride, gint x, gint y,
351                                      guchar *dest, gint dest_row_stride, gint w, gint h,
352                                      gint bytes_per_pixel, gboolean counter_clockwise)
353 {
354         gint i, j;
355         guchar *sp;
356         guchar *dp;
357
358         for (i = 0; i < h; i++)
359                 {
360                 sp = src + ((i + y) * src_row_stride) + (x * bytes_per_pixel);
361                 for (j = 0; j < w; j++)
362                         {
363                         if (counter_clockwise)
364                                 {
365                                 dp = dest + ((w - j - 1) * dest_row_stride) + (i * bytes_per_pixel);
366                                 }
367                         else
368                                 {
369                                 dp = dest + (j * dest_row_stride) + ((h - i - 1) * bytes_per_pixel);
370                                 }
371                         *(dp++) = *(sp++);      /* r */
372                         *(dp++) = *(sp++);      /* g */
373                         *(dp++) = *(sp++);      /* b */
374                         if (bytes_per_pixel == 4) *(dp) = *(sp++);      /* a */
375                         }
376                 }
377
378 }
379
380 static void pixbuf_copy_block(guchar *src, gint src_row_stride, gint w, gint h,
381                               guchar *dest, gint dest_row_stride, gint x, gint y, gint bytes_per_pixel)
382 {
383         gint i;
384         guchar *sp;
385         guchar *dp;
386
387         for (i = 0; i < h; i++)
388                 {
389                 sp = src + (i * src_row_stride);
390                 dp = dest + ((y + i) * dest_row_stride) + (x * bytes_per_pixel);
391                 memcpy(dp, sp, w * bytes_per_pixel);
392                 }
393 }
394
395 #define ROTATE_BUFFER_WIDTH 48
396 #define ROTATE_BUFFER_HEIGHT 48
397
398 /*
399  * Returns a copy of pixbuf src rotated 90 degrees clockwise or 90 counterclockwise
400  *
401  */
402 GdkPixbuf *pixbuf_copy_rotate_90(GdkPixbuf *src, gboolean counter_clockwise)
403 {
404         GdkPixbuf *dest;
405         gboolean has_alpha;
406         gint sw, sh, srs;
407         gint dw, dh, drs;
408         guchar *s_pix;
409         guchar *d_pix;
410         gint i, j;
411         gint a;
412         GdkPixbuf *buffer;
413         guchar *b_pix;
414         gint brs;
415         gint w, h;
416
417         if (!src) return NULL;
418
419         sw = gdk_pixbuf_get_width(src);
420         sh = gdk_pixbuf_get_height(src);
421         has_alpha = gdk_pixbuf_get_has_alpha(src);
422         srs = gdk_pixbuf_get_rowstride(src);
423         s_pix = gdk_pixbuf_get_pixels(src);
424
425         dw = sh;
426         dh = sw;
427         dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, dw, dh);
428         drs = gdk_pixbuf_get_rowstride(dest);
429         d_pix = gdk_pixbuf_get_pixels(dest);
430
431         a = (has_alpha ? 4 : 3);
432
433         buffer = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8,
434                                 ROTATE_BUFFER_WIDTH, ROTATE_BUFFER_HEIGHT);
435         b_pix = gdk_pixbuf_get_pixels(buffer);
436         brs = gdk_pixbuf_get_rowstride(buffer);
437
438         for (i = 0; i < sh; i+= ROTATE_BUFFER_WIDTH)
439                 {
440                 w = MIN(ROTATE_BUFFER_WIDTH, (sh - i));
441                 for (j = 0; j < sw; j += ROTATE_BUFFER_HEIGHT)
442                         {
443                         gint x, y;
444
445                         h = MIN(ROTATE_BUFFER_HEIGHT, (sw - j));
446                         pixbuf_copy_block_rotate(s_pix, srs, j, i,
447                                                  b_pix, brs, h, w,
448                                                  a, counter_clockwise);
449
450                         if (counter_clockwise)
451                                 {
452                                 x = i;
453                                 y = sw - h - j;
454                                 }
455                         else
456                                 {
457                                 x = sh - w - i;
458                                 y = j;
459                                 }
460                         pixbuf_copy_block(b_pix, brs, w, h,
461                                           d_pix, drs, x, y, a);
462                         }
463                 }
464
465         g_object_unref(buffer);
466
467 #if 0
468         /* this is the simple version of rotation (roughly 2-4x slower) */
469
470         for (i = 0; i < sh; i++)
471                 {
472                 sp = s_pix + (i * srs);
473                 for (j = 0; j < sw; j++)
474                         {
475                         if (counter_clockwise)
476                                 {
477                                 dp = d_pix + ((dh - j - 1) * drs) + (i * a);
478                                 }
479                         else
480                                 {
481                                 dp = d_pix + (j * drs) + ((dw - i - 1) * a);
482                                 }
483
484                         *(dp++) = *(sp++);      /* r */
485                         *(dp++) = *(sp++);      /* g */
486                         *(dp++) = *(sp++);      /* b */
487                         if (has_alpha) *(dp) = *(sp++); /* a */
488                         }
489                 }
490 #endif
491
492         return dest;
493 }
494
495 /*
496  * Returns a copy of pixbuf mirrored and or flipped.
497  * TO do a 180 degree rotations set both mirror and flipped TRUE
498  * if mirror and flip are FALSE, result is a simple copy.
499  */
500 GdkPixbuf *pixbuf_copy_mirror(GdkPixbuf *src, gboolean mirror, gboolean flip)
501 {
502         GdkPixbuf *dest;
503         gboolean has_alpha;
504         gint w, h, srs;
505         gint drs;
506         guchar *s_pix;
507         guchar *d_pix;
508         guchar *sp;
509         guchar *dp;
510         gint i, j;
511         gint a;
512
513         if (!src) return NULL;
514
515         w = gdk_pixbuf_get_width(src);
516         h = gdk_pixbuf_get_height(src);
517         has_alpha = gdk_pixbuf_get_has_alpha(src);
518         srs = gdk_pixbuf_get_rowstride(src);
519         s_pix = gdk_pixbuf_get_pixels(src);
520
521         dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, w, h);
522         drs = gdk_pixbuf_get_rowstride(dest);
523         d_pix = gdk_pixbuf_get_pixels(dest);
524
525         a = has_alpha ? 4 : 3;
526
527         for (i = 0; i < h; i++)
528                 {
529                 sp = s_pix + (i * srs);
530                 if (flip)
531                         {
532                         dp = d_pix + ((h - i - 1) * drs);
533                         }
534                 else
535                         {
536                         dp = d_pix + (i * drs);
537                         }
538                 if (mirror)
539                         {
540                         dp += (w - 1) * a;
541                         for (j = 0; j < w; j++)
542                                 {
543                                 *(dp++) = *(sp++);      /* r */
544                                 *(dp++) = *(sp++);      /* g */
545                                 *(dp++) = *(sp++);      /* b */
546                                 if (has_alpha) *(dp) = *(sp++); /* a */
547                                 dp -= (a + 3);
548                                 }
549                         }
550                 else
551                         {
552                         for (j = 0; j < w; j++)
553                                 {
554                                 *(dp++) = *(sp++);      /* r */
555                                 *(dp++) = *(sp++);      /* g */
556                                 *(dp++) = *(sp++);      /* b */
557                                 if (has_alpha) *(dp++) = *(sp++);       /* a */
558                                 }
559                         }
560                 }
561
562         return dest;
563 }
564
565 GdkPixbuf *pixbuf_apply_orientation(GdkPixbuf *pixbuf, gint orientation)
566 {
567         GdkPixbuf *dest;
568         GdkPixbuf *tmp = NULL;
569
570         switch (orientation)
571                 {
572                 case EXIF_ORIENTATION_TOP_LEFT:
573                         dest = gdk_pixbuf_copy(pixbuf);
574                         break;
575                 case EXIF_ORIENTATION_TOP_RIGHT:
576                         /* mirrored */
577                         dest = pixbuf_copy_mirror(pixbuf, TRUE, FALSE);
578                         break;
579                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
580                         /* upside down */
581                         dest = pixbuf_copy_mirror(pixbuf, TRUE, TRUE);
582                         break;
583                 case EXIF_ORIENTATION_BOTTOM_LEFT:
584                         /* flipped */
585                         dest = pixbuf_copy_mirror(pixbuf, FALSE, TRUE);
586                         break;
587                 case EXIF_ORIENTATION_LEFT_TOP:
588                         tmp = pixbuf_copy_mirror(pixbuf, FALSE, TRUE);
589                         dest = pixbuf_copy_rotate_90(tmp, FALSE);
590                         break;
591                 case EXIF_ORIENTATION_RIGHT_TOP:
592                         /* rotated -90 (270) */
593                         dest = pixbuf_copy_rotate_90(pixbuf, FALSE);
594                         break;
595                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
596                         tmp = pixbuf_copy_mirror(pixbuf, FALSE, TRUE);
597                         dest = pixbuf_copy_rotate_90(tmp, TRUE);
598                         break;
599                 case EXIF_ORIENTATION_LEFT_BOTTOM:
600                         /* rotated 90 */
601                         dest = pixbuf_copy_rotate_90(pixbuf, TRUE);
602                         break;
603                 default:
604                         dest = gdk_pixbuf_copy(pixbuf);
605                         break;
606                 }
607         if (tmp) g_object_unref(tmp);
608         return dest;
609
610 }
611
612
613 /*
614  *-----------------------------------------------------------------------------
615  * pixbuf drawing (rectangles)
616  *-----------------------------------------------------------------------------
617  */
618
619 /*
620  * Fills region of pixbuf at x,y over w,h
621  * with colors red (r), green (g), blue (b)
622  * applying alpha (a), use a=255 for solid.
623  */
624 void pixbuf_draw_rect_fill(GdkPixbuf *pb,
625                            gint x, gint y, gint w, gint h,
626                            gint r, gint g, gint b, gint a)
627 {
628         gboolean has_alpha;
629         gint pw, ph, prs;
630         guchar *p_pix;
631         guchar *pp;
632         gint i, j;
633
634         if (!pb) return;
635
636         pw = gdk_pixbuf_get_width(pb);
637         ph = gdk_pixbuf_get_height(pb);
638
639         if (x < 0 || x + w > pw) return;
640         if (y < 0 || y + h > ph) return;
641
642         has_alpha = gdk_pixbuf_get_has_alpha(pb);
643         prs = gdk_pixbuf_get_rowstride(pb);
644         p_pix = gdk_pixbuf_get_pixels(pb);
645
646         for (i = 0; i < h; i++)
647                 {
648                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
649                 for (j = 0; j < w; j++)
650                         {
651                         *pp = (r * a + *pp * (256-a)) >> 8;
652                         pp++;
653                         *pp = (g * a + *pp * (256-a)) >> 8;
654                         pp++;
655                         *pp = (b * a + *pp * (256-a)) >> 8;
656                         pp++;
657                         if (has_alpha) pp++;
658                         }
659                 }
660 }
661
662 void pixbuf_draw_rect(GdkPixbuf *pb,
663                       gint x, gint y, gint w, gint h,
664                       gint r, gint g, gint b, gint a,
665                       gint left, gint right, gint top, gint bottom)
666 {
667         pixbuf_draw_rect_fill(pb, x + left, y, w - left - right, top,
668                               r, g, b ,a);
669         pixbuf_draw_rect_fill(pb, x + w - right, y, right, h,
670                               r, g, b ,a);
671         pixbuf_draw_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
672                               r, g, b ,a);
673         pixbuf_draw_rect_fill(pb, x, y, left, h,
674                               r, g, b ,a);
675 }
676
677 void pixbuf_set_rect_fill(GdkPixbuf *pb,
678                           gint x, gint y, gint w, gint h,
679                           gint r, gint g, gint b, gint a)
680 {
681         gboolean has_alpha;
682         gint pw, ph, prs;
683         guchar *p_pix;
684         guchar *pp;
685         gint i, j;
686
687         if (!pb) return;
688
689         pw = gdk_pixbuf_get_width(pb);
690         ph = gdk_pixbuf_get_height(pb);
691
692         if (x < 0 || x + w > pw) return;
693         if (y < 0 || y + h > ph) return;
694
695         has_alpha = gdk_pixbuf_get_has_alpha(pb);
696         prs = gdk_pixbuf_get_rowstride(pb);
697         p_pix = gdk_pixbuf_get_pixels(pb);
698
699         for (i = 0; i < h; i++)
700                 {
701                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
702                 for (j = 0; j < w; j++)
703                         {
704                         *pp = r; pp++;
705                         *pp = g; pp++;
706                         *pp = b; pp++;
707                         if (has_alpha) { *pp = a; pp++; }
708                         }
709                 }
710 }
711
712 void pixbuf_set_rect(GdkPixbuf *pb,
713                      gint x, gint y, gint w, gint h,
714                      gint r, gint g, gint b, gint a,
715                      gint left, gint right, gint top, gint bottom)
716 {
717         pixbuf_set_rect_fill(pb, x + left, y, w - left - right, top,
718                              r, g, b ,a);
719         pixbuf_set_rect_fill(pb, x + w - right, y, right, h,
720                              r, g, b ,a);
721         pixbuf_set_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
722                              r, g, b ,a);
723         pixbuf_set_rect_fill(pb, x, y, left, h,
724                              r, g, b ,a);
725 }
726
727 void pixbuf_pixel_set(GdkPixbuf *pb, gint x, gint y, gint r, gint g, gint b, gint a)
728 {
729         guchar *buf;
730         gboolean has_alpha;
731         gint rowstride;
732         guchar *p;
733
734         if (x < 0 || x >= gdk_pixbuf_get_width(pb) ||
735             y < 0 || y >= gdk_pixbuf_get_height(pb)) return;
736
737         buf = gdk_pixbuf_get_pixels(pb);
738         has_alpha = gdk_pixbuf_get_has_alpha(pb);
739         rowstride = gdk_pixbuf_get_rowstride(pb);
740
741         p = buf + (y * rowstride) + (x * (has_alpha ? 4 : 3));
742         *p = r; p++;
743         *p = g; p++;
744         *p = b; p++;
745         if (has_alpha) *p = a;
746 }
747
748
749 /*
750  *-----------------------------------------------------------------------------
751  * pixbuf text rendering
752  *-----------------------------------------------------------------------------
753  */
754
755 static void pixbuf_copy_font(GdkPixbuf *src, gint sx, gint sy,
756                              GdkPixbuf *dest, gint dx, gint dy,
757                              gint w, gint h,
758                              guint8 r, guint8 g, guint8 b, guint8 a)
759 {
760         gint sw, sh, srs;
761         gboolean s_alpha;
762         gint s_step;
763         guchar *s_pix;
764         gint dw, dh, drs;
765         gboolean d_alpha;
766         gint d_step;
767         guchar *d_pix;
768
769         guchar *sp;
770         guchar *dp;
771         gint i, j;
772
773         if (!src || !dest) return;
774
775         sw = gdk_pixbuf_get_width(src);
776         sh = gdk_pixbuf_get_height(src);
777
778         if (sx < 0 || sx + w > sw) return;
779         if (sy < 0 || sy + h > sh) return;
780
781         dw = gdk_pixbuf_get_width(dest);
782         dh = gdk_pixbuf_get_height(dest);
783
784         if (dx < 0 || dx + w > dw) return;
785         if (dy < 0 || dy + h > dh) return;
786
787         s_alpha = gdk_pixbuf_get_has_alpha(src);
788         d_alpha = gdk_pixbuf_get_has_alpha(dest);
789         srs = gdk_pixbuf_get_rowstride(src);
790         drs = gdk_pixbuf_get_rowstride(dest);
791         s_pix = gdk_pixbuf_get_pixels(src);
792         d_pix = gdk_pixbuf_get_pixels(dest);
793
794         s_step = (s_alpha) ? 4 : 3;
795         d_step = (d_alpha) ? 4 : 3;
796
797         for (i = 0; i < h; i++)
798                 {
799                 sp = s_pix + (sy + i) * srs + sx * s_step;
800                 dp = d_pix + (dy + i) * drs + dx * d_step;
801                 for (j = 0; j < w; j++)
802                         {
803                         if (*sp)
804                                 {
805                                 guint8 asub;
806
807                                 asub = a * sp[0] / 255;
808                                 *dp = (r * asub + *dp * (256-asub)) >> 8;
809                                 dp++;
810                                 asub = a * sp[1] / 255;
811                                 *dp = (g * asub + *dp * (256-asub)) >> 8;
812                                 dp++;
813                                 asub = a * sp[2] / 255;
814                                 *dp = (b * asub + *dp * (256-asub)) >> 8;
815                                 dp++;
816
817                                 if (d_alpha)
818                                         {
819                                         *dp = MAX(*dp, a * ((sp[0] + sp[1] + sp[2]) / 3) / 255);
820                                         dp++;
821                                         }
822                                 }
823                         else
824                                 {
825                                 dp += d_step;
826                                 }
827
828                         sp += s_step;
829                         }
830                 }
831 }
832
833 void pixbuf_draw_layout(GdkPixbuf *pixbuf, PangoLayout *layout, GtkWidget *widget,
834                         gint x, gint y,
835                         guint8 r, guint8 g, guint8 b, guint8 a)
836 {
837         GdkPixbuf *buffer;
838         gint w, h;
839         gint sx, sy;
840         gint dw, dh;
841         cairo_surface_t *source;
842         cairo_t *cr;
843
844         pango_layout_get_pixel_size(layout, &w, &h);
845         if (w < 1 || h < 1) return;
846
847         source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
848
849         cr = cairo_create (source);
850         cairo_set_source_rgb(cr, 0, 0, 0);
851         cairo_rectangle (cr, 0, 0, w, h);
852         cairo_fill (cr);
853         cairo_set_source_rgb(cr, 1, 1, 1);
854         pango_cairo_show_layout (cr, layout);
855         cairo_destroy (cr);
856
857         buffer = gdk_pixbuf_new_from_data (cairo_image_surface_get_data (source),
858                                            GDK_COLORSPACE_RGB,
859                                            cairo_image_surface_get_format (source) == CAIRO_FORMAT_ARGB32,
860                                            8,
861                                            cairo_image_surface_get_width (source),
862                                            cairo_image_surface_get_height (source),
863                                            cairo_image_surface_get_stride (source),
864                                            NULL,
865                                            NULL);
866
867         sx = 0;
868         sy = 0;
869         dw = gdk_pixbuf_get_width(pixbuf);
870         dh = gdk_pixbuf_get_height(pixbuf);
871
872         if (x < 0)
873                 {
874                 w += x;
875                 sx = -x;
876                 x = 0;
877                 }
878
879         if (y < 0)
880                 {
881                 h += y;
882                 sy = -y;
883                 y = 0;
884                 }
885
886         if (x + w > dw) w = dw - x;
887         if (y + h > dh) h = dh - y;
888
889         pixbuf_copy_font(buffer, sx, sy,
890                          pixbuf, x, y, w, h,
891                          r, g, b, a);
892
893         g_object_unref(buffer);
894         cairo_surface_destroy(source);
895 }
896
897 /*
898  *-----------------------------------------------------------------------------
899  * pixbuf drawing (triangle)
900  *-----------------------------------------------------------------------------
901  */
902
903 void util_clip_triangle(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
904                         gint *rx, gint *ry, gint *rw, gint *rh)
905 {
906         gint tx, ty, tw, th;
907
908         tx = MIN(x1, x2);
909         tx = MIN(tx, x3);
910         ty = MIN(y1, y2);
911         ty = MIN(ty, y3);
912         tw = MAX(abs(x1 - x2), abs(x2 - x3));
913         tw = MAX(tw, abs(x3 - x1));
914         th = MAX(abs(y1 - y2), abs(y2 - y3));
915         th = MAX(th, abs(y3 - y1));
916
917         *rx = tx;
918         *ry = ty;
919         *rw = tw;
920         *rh = th;
921 }
922
923 void pixbuf_draw_triangle(GdkPixbuf *pb,
924                           gint clip_x, gint clip_y, gint clip_w, gint clip_h,
925                           gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
926                           guint8 r, guint8 g, guint8 b, guint8 a)
927 {
928         gboolean has_alpha;
929         gint pw, ph, prs;
930         gint rx, ry, rw, rh;
931         gint tx, ty, tw, th;
932         gint fx1, fy1;
933         gint fx2, fy2;
934         gint fw, fh;
935         guchar *p_pix;
936         guchar *pp;
937         gint p_step;
938         gdouble slope1, slope2;
939         gint slope1_x, slope1_y;
940         gint y;
941         gint t;
942         gboolean middle = FALSE;
943
944         if (!pb) return;
945
946         pw = gdk_pixbuf_get_width(pb);
947         ph = gdk_pixbuf_get_height(pb);
948
949         if (!util_clip_region(0, 0, pw, ph,
950                               clip_x, clip_y, clip_w, clip_h,
951                               &rx, &ry, &rw, &rh)) return;
952
953         util_clip_triangle(x1, y1, x2, y2, x3, y3,
954                            &tx, &ty, &tw, &th);
955
956         if (!util_clip_region(rx, ry, rw, rh,
957                               tx, ty, tw, th,
958                               &fx1, &fy1, &fw, &fh)) return;
959         fx2 = fx1 + fw;
960         fy2 = fy1 + fh;
961
962         has_alpha = gdk_pixbuf_get_has_alpha(pb);
963         prs = gdk_pixbuf_get_rowstride(pb);
964         p_pix = gdk_pixbuf_get_pixels(pb);
965
966         p_step = (has_alpha) ? 4 : 3;
967
968         if (y1 > y2)
969                 {
970                 t = x1; x1 = x2; x2 = t;
971                 t = y1; y1 = y2; y2 = t;
972                 }
973         if (y2 > y3)
974                 {
975                 t = x2; x2 = x3; x3 = t;
976                 t = y2; y2 = y3; y3 = t;
977                 }
978         if (y1 > y2)
979                 {
980                 t = x1; x1 = x2; x2 = t;
981                 t = y1; y1 = y2; y2 = t;
982                 }
983
984         slope1 = (gdouble)(y2 - y1);
985         if (slope1) slope1 = (gdouble)(x2 - x1) / slope1;
986         slope1_x = x1;
987         slope1_y = y1;
988         slope2 = (gdouble)(y3 - y1);
989         if (slope2) slope2 = (gdouble)(x3 - x1) / slope2;
990
991         for (y = fy1; y < fy2; y++)
992                 {
993                 gint xa, xb;
994
995                 if (!middle && y > y2)
996                         {
997                         slope1 = (gdouble)(y3 - y2);
998                         if (slope1) slope1 = (gdouble)(x3 - x2) / slope1;
999                         slope1_x = x2;
1000                         slope1_y = y2;
1001
1002                         middle = TRUE;
1003                         }
1004
1005                 xa = slope1_x + ((gdouble)slope1 * (y - slope1_y) + 0.5);
1006                 xb = x1 + ((gdouble)slope2 * (y - y1) + 0.5);
1007
1008                 if (xa > xb)
1009                         {
1010                         t = xa; xa = xb; xb = t;
1011                         }
1012
1013                 xa = CLAMP(xa, fx1, fx2);
1014                 xb = CLAMP(xb, fx1, fx2);
1015
1016                 pp = p_pix + y * prs + xa * p_step;
1017
1018                 while (xa < xb)
1019                         {
1020                         *pp = (r * a + *pp * (256-a)) >> 8;
1021                         pp++;
1022                         *pp = (g * a + *pp * (256-a)) >> 8;
1023                         pp++;
1024                         *pp = (b * a + *pp * (256-a)) >> 8;
1025                         pp++;
1026                         if (has_alpha) pp++;
1027
1028                         xa++;
1029                         }
1030                 }
1031 }
1032
1033 /*
1034  *-----------------------------------------------------------------------------
1035  * pixbuf drawing (line)
1036  *-----------------------------------------------------------------------------
1037  */
1038
1039 static gboolean util_clip_line(gdouble clip_x, gdouble clip_y, gdouble clip_w, gdouble clip_h,
1040                                gdouble x1, gdouble y1, gdouble x2, gdouble y2,
1041                                gdouble *rx1, gdouble *ry1, gdouble *rx2, gdouble *ry2)
1042 {
1043         gboolean flip = FALSE;
1044         gdouble d;
1045
1046         if (x1 > x2)
1047                 {
1048                 gdouble t;
1049
1050                 t = x1; x1 = x2; x2 = t;
1051                 t = y1; y1 = y2; y2 = t;
1052                 flip = TRUE;
1053                 }
1054
1055         if (x2 < clip_x || x1 > clip_x + clip_w) return FALSE;
1056
1057         if (y1 < y2)
1058                 {
1059                 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
1060                 }
1061         else
1062                 {
1063                 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
1064                 }
1065
1066         d = x2 - x1;
1067         if (d > 0.0)
1068                 {
1069                 gdouble slope;
1070
1071                 slope = (y2 - y1) / d;
1072                 if (x1 < clip_x)
1073                         {
1074                         y1 = y1 + slope * (clip_x - x1);
1075                         x1 = clip_x;
1076                         }
1077                 if (x2 > clip_x + clip_w)
1078                         {
1079                         y2 = y2 + slope * (clip_x + clip_w - x2);
1080                         x2 = clip_x + clip_w;
1081                         }
1082                 }
1083
1084         if (y1 < y2)
1085                 {
1086                 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
1087                 }
1088         else
1089                 {
1090                 gdouble t;
1091
1092                 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
1093
1094                 t = x1; x1 = x2; x2 = t;
1095                 t = y1; y1 = y2; y2 = t;
1096                 flip = !flip;
1097                 }
1098
1099         d = y2 - y1;
1100         if (d > 0.0)
1101                 {
1102                 gdouble slope;
1103
1104                 slope = (x2 - x1) / d;
1105                 if (y1 < clip_y)
1106                         {
1107                         x1 = x1 + slope * (clip_y - y1);
1108                         y1 = clip_y;
1109                         }
1110                 if (y2 > clip_y + clip_h)
1111                         {
1112                         x2 = x2 + slope * (clip_y + clip_h - y2);
1113                         y2 = clip_y + clip_h;
1114                         }
1115                 }
1116
1117         if (flip)
1118                 {
1119                 *rx1 = x2;
1120                 *ry1 = y2;
1121                 *rx2 = x1;
1122                 *ry2 = y1;
1123                 }
1124         else
1125                 {
1126                 *rx1 = x1;
1127                 *ry1 = y1;
1128                 *rx2 = x2;
1129                 *ry2 = y2;
1130                 }
1131
1132         return TRUE;
1133 }
1134
1135 void pixbuf_draw_line(GdkPixbuf *pb,
1136                       gint clip_x, gint clip_y, gint clip_w, gint clip_h,
1137                       gint x1, gint y1, gint x2, gint y2,
1138                       guint8 r, guint8 g, guint8 b, guint8 a)
1139 {
1140         gboolean has_alpha;
1141         gint pw, ph, prs;
1142         gint rx, ry, rw, rh;
1143         gdouble rx1, ry1, rx2, ry2;
1144         guchar *p_pix;
1145         guchar *pp;
1146         gint p_step;
1147         gdouble slope;
1148         gdouble x, y;
1149         gint px, py;
1150         gint cx1, cy1, cx2, cy2;
1151
1152         if (!pb) return;
1153
1154         pw = gdk_pixbuf_get_width(pb);
1155         ph = gdk_pixbuf_get_height(pb);
1156
1157         if (!util_clip_region(0, 0, pw, ph,
1158                               clip_x, clip_y, clip_w, clip_h,
1159                               &rx, &ry, &rw, &rh)) return;
1160         if (!util_clip_line((gdouble)rx, (gdouble)ry, (gdouble)rw, (gdouble)rh,
1161                             (gdouble)x1, (gdouble)y1, (gdouble)x2, (gdouble)y2,
1162                             &rx1, &ry1, &rx2, &ry2)) return;
1163
1164         cx1 = rx;
1165         cy1 = ry;
1166         cx2 = rx + rw;
1167         cy2 = ry + rh;
1168
1169         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1170         prs = gdk_pixbuf_get_rowstride(pb);
1171         p_pix = gdk_pixbuf_get_pixels(pb);
1172
1173         p_step = (has_alpha) ? 4 : 3;
1174
1175         if (fabs(rx2 - rx1) > fabs(ry2 - ry1))
1176                 {
1177                 if (rx1 > rx2)
1178                         {
1179                         gdouble t;
1180                         t = rx1; rx1 = rx2; rx2 = t;
1181                         t = ry1; ry1 = ry2; ry2 = t;
1182                         }
1183
1184                 slope = rx2 - rx1;
1185                 if (slope != 0.0) slope = (ry2 - ry1) / slope;
1186                 for (x = rx1; x < rx2; x += 1.0)
1187                         {
1188                         px = (gint)(x + 0.5);
1189                         py = (gint)(ry1 + (x - rx1) * slope + 0.5);
1190
1191                         if (px >=  cx1 && px < cx2 && py >= cy1 && py < cy2)
1192                                 {
1193                                 pp = p_pix + py * prs + px * p_step;
1194                                 *pp = (r * a + *pp * (256-a)) >> 8;
1195                                 pp++;
1196                                 *pp = (g * a + *pp * (256-a)) >> 8;
1197                                 pp++;
1198                                 *pp = (b * a + *pp * (256-a)) >> 8;
1199                                 }
1200                         }
1201                 }
1202         else
1203                 {
1204                 if (ry1 > ry2)
1205                         {
1206                         gdouble t;
1207                         t = rx1; rx1 = rx2; rx2 = t;
1208                         t = ry1; ry1 = ry2; ry2 = t;
1209                         }
1210
1211                 slope = ry2 - ry1;
1212                 if (slope != 0.0) slope = (rx2 - rx1) / slope;
1213                 for (y = ry1; y < ry2; y += 1.0)
1214                         {
1215                         px = (gint)(rx1 + (y - ry1) * slope + 0.5);
1216                         py = (gint)(y + 0.5);
1217
1218                         if (px >=  cx1 && px < cx2 && py >= cy1 && py < cy2)
1219                                 {
1220                                 pp = p_pix + py * prs + px * p_step;
1221                                 *pp = (r * a + *pp * (256-a)) >> 8;
1222                                 pp++;
1223                                 *pp = (g * a + *pp * (256-a)) >> 8;
1224                                 pp++;
1225                                 *pp = (b * a + *pp * (256-a)) >> 8;
1226                                 }
1227                         }
1228                 }
1229 }
1230
1231 /*
1232  *-----------------------------------------------------------------------------
1233  * pixbuf drawing (fades and shadows)
1234  *-----------------------------------------------------------------------------
1235  */
1236
1237 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gboolean has_alpha,
1238                                     gint s, gboolean vertical, gint border,
1239                                     gint x1, gint y1, gint x2, gint y2,
1240                                     guint8 r, guint8 g, guint8 b, guint8 a)
1241 {
1242         guchar *pp;
1243         gint p_step;
1244         guint8 n = a;
1245         gint i, j;
1246
1247         p_step = (has_alpha) ? 4 : 3;
1248         for (j = y1; j < y2; j++)
1249                 {
1250                 pp = p_pix + j * prs + x1 * p_step;
1251                 if (!vertical) n = a - a * abs(j - s) / border;
1252                 for (i = x1; i < x2; i++)
1253                         {
1254                         if (vertical) n = a - a * abs(i - s) / border;
1255                         *pp = (r * n + *pp * (256-n)) >> 8;
1256                         pp++;
1257                         *pp = (g * n + *pp * (256-n)) >> 8;
1258                         pp++;
1259                         *pp = (b * n + *pp * (256-n)) >> 8;
1260                         pp++;
1261                         if (has_alpha) pp++;
1262                         }
1263                 }
1264 }
1265
1266 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gboolean has_alpha,
1267                                     gint sx, gint sy, gint border,
1268                                     gint x1, gint y1, gint x2, gint y2,
1269                                     guint8 r, guint8 g, guint8 b, guint8 a)
1270 {
1271         guchar *pp;
1272         gint p_step;
1273         gint i, j;
1274
1275         p_step = (has_alpha) ? 4 : 3;
1276         for (j = y1; j < y2; j++)
1277                 {
1278                 pp = p_pix + j * prs + x1 * p_step;
1279                 for (i = x1; i < x2; i++)
1280                         {
1281                         guint8 n;
1282                         gint r;
1283
1284                         r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
1285                         n = a - a * r / border;
1286                         *pp = (r * n + *pp * (256-n)) >> 8;
1287                         pp++;
1288                         *pp = (g * n + *pp * (256-n)) >> 8;
1289                         pp++;
1290                         *pp = (b * n + *pp * (256-n)) >> 8;
1291                         pp++;
1292                         if (has_alpha) pp++;
1293                         }
1294                 }
1295 }
1296
1297 void pixbuf_draw_shadow(GdkPixbuf *pb,
1298                         gint clip_x, gint clip_y, gint clip_w, gint clip_h,
1299                         gint x, gint y, gint w, gint h, gint border,
1300                         guint8 r, guint8 g, guint8 b, guint8 a)
1301 {
1302         gint has_alpha;
1303         gint pw, ph, prs;
1304         gint rx, ry, rw, rh;
1305         gint fx, fy, fw, fh;
1306         guchar *p_pix;
1307
1308         if (!pb) return;
1309
1310         pw = gdk_pixbuf_get_width(pb);
1311         ph = gdk_pixbuf_get_height(pb);
1312
1313         if (!util_clip_region(0, 0, pw, ph,
1314                               clip_x, clip_y, clip_w, clip_h,
1315                               &rx, &ry, &rw, &rh)) return;
1316
1317         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1318         prs = gdk_pixbuf_get_rowstride(pb);
1319         p_pix = gdk_pixbuf_get_pixels(pb);
1320
1321         if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
1322                              rx, ry, rw, rh,
1323                              &fx, &fy, &fw, &fh))
1324                 {
1325                 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
1326                 }
1327
1328         if (border < 1) return;
1329
1330         if (util_clip_region(x, y + border, border, h - border * 2,
1331                              rx, ry, rw, rh,
1332                              &fx, &fy, &fw, &fh))
1333                 {
1334                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1335                                         x + border, TRUE, border,
1336                                         fx, fy, fx + fw, fy + fh,
1337                                         r, g, b, a);
1338                 }
1339         if (util_clip_region(x + w - border, 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 + w - border, TRUE, border,
1345                                         fx, fy, fx + fw, fy + fh,
1346                                         r, g, b, a);
1347                 }
1348         if (util_clip_region(x + border, y, w - border * 2, border,
1349                              rx, ry, rw, rh,
1350                              &fx, &fy, &fw, &fh))
1351                 {
1352                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1353                                         y + border, FALSE, border,
1354                                         fx, fy, fx + fw, fy + fh,
1355                                         r, g, b, a);
1356                 }
1357         if (util_clip_region(x + border, y + h - border, 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 + h - border, FALSE, border,
1363                                         fx, fy, fx + fw, fy + fh,
1364                                         r, g, b, a);
1365                 }
1366         if (util_clip_region(x, y, border, border,
1367                              rx, ry, rw, rh,
1368                              &fx, &fy, &fw, &fh))
1369                 {
1370                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1371                                         x + border, y + border, border,
1372                                         fx, fy, fx + fw, fy + fh,
1373                                         r, g, b, a);
1374                 }
1375         if (util_clip_region(x + w - border, 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 + w - border, y + border, border,
1381                                         fx, fy, fx + fw, fy + fh,
1382                                         r, g, b, a);
1383                 }
1384         if (util_clip_region(x, y + h - border, 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 + border, y + h - border, border,
1390                                         fx, fy, fx + fw, fy + fh,
1391                                         r, g, b, a);
1392                 }
1393         if (util_clip_region(x + w - border, 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 + w - border, y + h - border, border,
1399                                         fx, fy, fx + fw, fy + fh,
1400                                         r, g, b, a);
1401                 }
1402 }
1403
1404
1405 /*
1406  *-----------------------------------------------------------------------------
1407  * pixbuf color alterations
1408  *-----------------------------------------------------------------------------
1409  */
1410
1411 void pixbuf_desaturate_rect(GdkPixbuf *pb,
1412                             gint x, gint y, gint w, gint h)
1413 {
1414         gboolean has_alpha;
1415         gint pw, ph, prs;
1416         guchar *p_pix;
1417         guchar *pp;
1418         gint i, j;
1419
1420         if (!pb) return;
1421
1422         pw = gdk_pixbuf_get_width(pb);
1423         ph = gdk_pixbuf_get_height(pb);
1424
1425         if (x < 0 || x + w > pw) return;
1426         if (y < 0 || y + h > ph) return;
1427
1428         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1429         prs = gdk_pixbuf_get_rowstride(pb);
1430         p_pix = gdk_pixbuf_get_pixels(pb);
1431
1432         for (i = 0; i < h; i++)
1433                 {
1434                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
1435                 for (j = 0; j < w; j++)
1436                         {
1437                         guint8 grey;
1438
1439                         grey = (pp[0] + pp[1] + pp[2]) / 3;
1440                         *pp = grey;
1441                         pp++;
1442                         *pp = grey;
1443                         pp++;
1444                         *pp = grey;
1445                         pp++;
1446                         if (has_alpha) pp++;
1447                         }
1448                 }
1449 }
1450 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */