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