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