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