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