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