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