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