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