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