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