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