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