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