Include a Other Software section in Help file
[geeqie.git] / src / pixbuf_util.c
1 /*
2  * Copyright (C) 2004 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "pixbuf_util.h"
24 #include "exif.h"
25 #include "ui_fileops.h"
26
27 #include "icons/icons_inline.h"
28
29 #include <math.h>
30
31
32 /*
33  *-----------------------------------------------------------------------------
34  * png save
35  *-----------------------------------------------------------------------------
36  */
37
38 gboolean pixbuf_to_file_as_png(GdkPixbuf *pixbuf, const gchar *filename)
39 {
40         GError *error = NULL;
41         gboolean ret;
42
43         if (!pixbuf || !filename) return FALSE;
44
45         ret = gdk_pixbuf_save(pixbuf, filename, "png", &error,
46                               "tEXt::Software", GQ_APPNAME " " VERSION, NULL);
47
48         if (error)
49                 {
50                 log_printf("Error saving png file: %s\n", error->message);
51                 g_error_free(error);
52                 }
53
54         return ret;
55 }
56
57 /*
58  *-----------------------------------------------------------------------------
59  * jpeg save
60  *-----------------------------------------------------------------------------
61  */
62
63 gboolean pixbuf_to_file_as_jpg(GdkPixbuf *pixbuf, const gchar *filename, gint quality)
64 {
65         GError *error = NULL;
66         gchar *qbuf;
67         gboolean ret;
68
69         if (!pixbuf || !filename) return FALSE;
70
71         if (quality == -1) quality = 75;
72         if (quality < 1 || quality > 100)
73                 {
74                 log_printf("Jpeg not saved, invalid quality %d\n", quality);
75                 return FALSE;
76                 }
77
78         qbuf = g_strdup_printf("%d", quality);
79         ret = gdk_pixbuf_save(pixbuf, filename, "jpeg", &error, "quality", qbuf, NULL);
80         g_free(qbuf);
81
82         if (error)
83                 {
84                 log_printf("Error saving jpeg to %s\n%s\n", filename, error->message);
85                 g_error_free(error);
86                 }
87
88         return ret;
89 }
90
91 /*
92  *-----------------------------------------------------------------------------
93  * pixbuf from inline
94  *-----------------------------------------------------------------------------
95  */
96
97 typedef struct _PixbufInline PixbufInline;
98 struct _PixbufInline
99 {
100         const gchar *key;
101         const guint8 *data;
102 };
103
104 static PixbufInline inline_pixbuf_data[] = {
105         { PIXBUF_INLINE_FOLDER_CLOSED,  folder_closed },
106         { PIXBUF_INLINE_FOLDER_LOCKED,  folder_locked },
107         { PIXBUF_INLINE_FOLDER_OPEN,    folder_open },
108         { PIXBUF_INLINE_FOLDER_UP,      folder_up },
109         { PIXBUF_INLINE_SCROLLER,       icon_scroller },
110         { PIXBUF_INLINE_BROKEN,         icon_broken },
111         { PIXBUF_INLINE_METADATA,       icon_metadata },
112         { PIXBUF_INLINE_UNKNOWN,        icon_unknown },
113         { PIXBUF_INLINE_VIDEO,          icon_video },
114         { PIXBUF_INLINE_COLLECTION,     icon_collection },
115         { PIXBUF_INLINE_ICON,           gqview_icon },
116         { PIXBUF_INLINE_LOGO,           geeqie_logo },
117         { PIXBUF_INLINE_ICON_FLOAT,     icon_float },
118         { PIXBUF_INLINE_ICON_THUMB,     icon_thumb },
119         { PIXBUF_INLINE_ICON_BOOK,      icon_book },
120         { PIXBUF_INLINE_ICON_CONFIG,    icon_config },
121         { PIXBUF_INLINE_ICON_TOOLS,     icon_tools },
122         { PIXBUF_INLINE_ICON_VIEW,      icon_view },
123         { PIXBUF_INLINE_ICON_GUIDELINES,        icon_guidelines },
124         { PIXBUF_INLINE_ICON_PANORAMA,  icon_panorama },
125         { PIXBUF_INLINE_ICON_MAINTENANCE,       icon_maintenance },
126         { PIXBUF_INLINE_ICON_ZOOMFILLHOR,       icon_zoomfillhor },
127         { PIXBUF_INLINE_ICON_ZOOMFILLVERT,      icon_zoomfillvert },
128         { PIXBUF_INLINE_ICON_HIDETOOLS, icon_hidetools },
129         { PIXBUF_INLINE_ICON_EXIF,      icon_exif },
130         { PIXBUF_INLINE_ICON_MARKS,     icon_marks },
131         { PIXBUF_INLINE_ICON_INFO,      icon_info },
132         { PIXBUF_INLINE_ICON_SORT,      icon_sort },
133         { PIXBUF_INLINE_ICON_PDF,       icon_pdf },
134         { PIXBUF_INLINE_ICON_DRAW_RECTANGLE,    icon_draw_rectangle },
135         { PIXBUF_INLINE_ICON_MOVE,      icon_move },
136         { PIXBUF_INLINE_ICON_RENAME,    icon_rename },
137         { PIXBUF_INLINE_ICON_SELECT_ALL,        icon_select_all },
138         { PIXBUF_INLINE_ICON_SELECT_NONE,       icon_select_none },
139         { PIXBUF_INLINE_ICON_SELECT_INVERT,     icon_select_invert },
140         { PIXBUF_INLINE_ICON_SELECT_RECTANGLE,  icon_select_rectangle },
141         { PIXBUF_INLINE_ICON_FILE_FILTER,       icon_file_filter },
142         { PIXBUF_INLINE_ICON_CW,        icon_rotate_clockwise },
143         { PIXBUF_INLINE_ICON_CCW,       icon_rotate_counter_clockwise },
144         { PIXBUF_INLINE_ICON_180,       icon_rotate_180 },
145         { PIXBUF_INLINE_ICON_MIRROR,    icon_mirror },
146         { PIXBUF_INLINE_ICON_FLIP,      icon_flip },
147         { PIXBUF_INLINE_ICON_ORIGINAL,  icon_original },
148         { PIXBUF_INLINE_ICON_TRASH,     icon_trash },
149         { PIXBUF_INLINE_ICON_HEIF,      icon_heic },
150         { 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                 default:
304                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
305                 }
306
307         if (requested_width && requested_height)
308                 {
309                 gint w = gdk_pixbuf_get_width(pixbuf);
310                 gint h = gdk_pixbuf_get_height(pixbuf);
311
312                 if (w > requested_width || h > requested_height)
313                         {
314                         gint nw, nh;
315
316                         if (pixbuf_scale_aspect(requested_width, requested_height,
317                                                           w, h, &nw, &nh))
318                                 {
319                                 GdkPixbuf *tmp;
320
321                                 tmp = pixbuf;
322                                 pixbuf = gdk_pixbuf_scale_simple(tmp, nw, nh, GDK_INTERP_TILES);
323                                 g_object_unref(G_OBJECT(tmp));
324                                 }
325                         }
326                 }
327         return pixbuf;
328 }
329
330
331 /*
332  *-----------------------------------------------------------------------------
333  * misc utils
334  *-----------------------------------------------------------------------------
335  */
336
337 gboolean util_clip_region(gint x, gint y, gint w, gint h,
338                           gint clip_x, gint clip_y, gint clip_w, gint clip_h,
339                           gint *rx, gint *ry, gint *rw, gint *rh)
340 {
341         if (clip_x + clip_w <= x ||
342             clip_x >= x + w ||
343             clip_y + clip_h <= y ||
344             clip_y >= y + h)
345                 {
346                 return FALSE;
347                 }
348
349         *rx = MAX(x, clip_x);
350         *rw = MIN((x + w), (clip_x + clip_w)) - *rx;
351
352         *ry = MAX(y, clip_y);
353         *rh = MIN((y + h), (clip_y + clip_h)) - *ry;
354
355         return TRUE;
356 }
357
358 /*
359  *-----------------------------------------------------------------------------
360  * pixbuf rotation
361  *-----------------------------------------------------------------------------
362  */
363
364 static void pixbuf_copy_block_rotate(guchar *src, gint src_row_stride, gint x, gint y,
365                                      guchar *dest, gint dest_row_stride, gint w, gint h,
366                                      gint bytes_per_pixel, gboolean counter_clockwise)
367 {
368         gint i, j;
369         guchar *sp;
370         guchar *dp;
371
372         for (i = 0; i < h; i++)
373                 {
374                 sp = src + ((i + y) * src_row_stride) + (x * bytes_per_pixel);
375                 for (j = 0; j < w; j++)
376                         {
377                         if (counter_clockwise)
378                                 {
379                                 dp = dest + ((w - j - 1) * dest_row_stride) + (i * bytes_per_pixel);
380                                 }
381                         else
382                                 {
383                                 dp = dest + (j * dest_row_stride) + ((h - i - 1) * bytes_per_pixel);
384                                 }
385                         *(dp++) = *(sp++);      /* r */
386                         *(dp++) = *(sp++);      /* g */
387                         *(dp++) = *(sp++);      /* b */
388                         if (bytes_per_pixel == 4) *(dp) = *(sp++);      /* a */
389                         }
390                 }
391
392 }
393
394 static void pixbuf_copy_block(guchar *src, gint src_row_stride, gint w, gint h,
395                               guchar *dest, gint dest_row_stride, gint x, gint y, gint bytes_per_pixel)
396 {
397         gint i;
398         guchar *sp;
399         guchar *dp;
400
401         for (i = 0; i < h; i++)
402                 {
403                 sp = src + (i * src_row_stride);
404                 dp = dest + ((y + i) * dest_row_stride) + (x * bytes_per_pixel);
405                 memcpy(dp, sp, w * bytes_per_pixel);
406                 }
407 }
408
409 #define ROTATE_BUFFER_WIDTH 48
410 #define ROTATE_BUFFER_HEIGHT 48
411
412 /*
413  * Returns a copy of pixbuf src rotated 90 degrees clockwise or 90 counterclockwise
414  *
415  */
416 GdkPixbuf *pixbuf_copy_rotate_90(GdkPixbuf *src, gboolean counter_clockwise)
417 {
418         GdkPixbuf *dest;
419         gboolean has_alpha;
420         gint sw, sh, srs;
421         gint dw, dh, drs;
422         guchar *s_pix;
423         guchar *d_pix;
424         gint i, j;
425         gint a;
426         GdkPixbuf *buffer;
427         guchar *b_pix;
428         gint brs;
429         gint w, h;
430
431         if (!src) return NULL;
432
433         sw = gdk_pixbuf_get_width(src);
434         sh = gdk_pixbuf_get_height(src);
435         has_alpha = gdk_pixbuf_get_has_alpha(src);
436         srs = gdk_pixbuf_get_rowstride(src);
437         s_pix = gdk_pixbuf_get_pixels(src);
438
439         dw = sh;
440         dh = sw;
441         dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, dw, dh);
442         drs = gdk_pixbuf_get_rowstride(dest);
443         d_pix = gdk_pixbuf_get_pixels(dest);
444
445         a = (has_alpha ? 4 : 3);
446
447         buffer = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8,
448                                 ROTATE_BUFFER_WIDTH, ROTATE_BUFFER_HEIGHT);
449         b_pix = gdk_pixbuf_get_pixels(buffer);
450         brs = gdk_pixbuf_get_rowstride(buffer);
451
452         for (i = 0; i < sh; i+= ROTATE_BUFFER_WIDTH)
453                 {
454                 w = MIN(ROTATE_BUFFER_WIDTH, (sh - i));
455                 for (j = 0; j < sw; j += ROTATE_BUFFER_HEIGHT)
456                         {
457                         gint x, y;
458
459                         h = MIN(ROTATE_BUFFER_HEIGHT, (sw - j));
460                         pixbuf_copy_block_rotate(s_pix, srs, j, i,
461                                                  b_pix, brs, h, w,
462                                                  a, counter_clockwise);
463
464                         if (counter_clockwise)
465                                 {
466                                 x = i;
467                                 y = sw - h - j;
468                                 }
469                         else
470                                 {
471                                 x = sh - w - i;
472                                 y = j;
473                                 }
474                         pixbuf_copy_block(b_pix, brs, w, h,
475                                           d_pix, drs, x, y, a);
476                         }
477                 }
478
479         g_object_unref(buffer);
480
481 #if 0
482         /* this is the simple version of rotation (roughly 2-4x slower) */
483
484         for (i = 0; i < sh; i++)
485                 {
486                 sp = s_pix + (i * srs);
487                 for (j = 0; j < sw; j++)
488                         {
489                         if (counter_clockwise)
490                                 {
491                                 dp = d_pix + ((dh - j - 1) * drs) + (i * a);
492                                 }
493                         else
494                                 {
495                                 dp = d_pix + (j * drs) + ((dw - i - 1) * a);
496                                 }
497
498                         *(dp++) = *(sp++);      /* r */
499                         *(dp++) = *(sp++);      /* g */
500                         *(dp++) = *(sp++);      /* b */
501                         if (has_alpha) *(dp) = *(sp++); /* a */
502                         }
503                 }
504 #endif
505
506         return dest;
507 }
508
509 /*
510  * Returns a copy of pixbuf mirrored and or flipped.
511  * TO do a 180 degree rotations set both mirror and flipped TRUE
512  * if mirror and flip are FALSE, result is a simple copy.
513  */
514 GdkPixbuf *pixbuf_copy_mirror(GdkPixbuf *src, gboolean mirror, gboolean flip)
515 {
516         GdkPixbuf *dest;
517         gboolean has_alpha;
518         gint w, h, srs;
519         gint drs;
520         guchar *s_pix;
521         guchar *d_pix;
522         guchar *sp;
523         guchar *dp;
524         gint i, j;
525         gint a;
526
527         if (!src) return NULL;
528
529         w = gdk_pixbuf_get_width(src);
530         h = gdk_pixbuf_get_height(src);
531         has_alpha = gdk_pixbuf_get_has_alpha(src);
532         srs = gdk_pixbuf_get_rowstride(src);
533         s_pix = gdk_pixbuf_get_pixels(src);
534
535         dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, w, h);
536         drs = gdk_pixbuf_get_rowstride(dest);
537         d_pix = gdk_pixbuf_get_pixels(dest);
538
539         a = has_alpha ? 4 : 3;
540
541         for (i = 0; i < h; i++)
542                 {
543                 sp = s_pix + (i * srs);
544                 if (flip)
545                         {
546                         dp = d_pix + ((h - i - 1) * drs);
547                         }
548                 else
549                         {
550                         dp = d_pix + (i * drs);
551                         }
552                 if (mirror)
553                         {
554                         dp += (w - 1) * a;
555                         for (j = 0; j < w; j++)
556                                 {
557                                 *(dp++) = *(sp++);      /* r */
558                                 *(dp++) = *(sp++);      /* g */
559                                 *(dp++) = *(sp++);      /* b */
560                                 if (has_alpha) *(dp) = *(sp++); /* a */
561                                 dp -= (a + 3);
562                                 }
563                         }
564                 else
565                         {
566                         for (j = 0; j < w; j++)
567                                 {
568                                 *(dp++) = *(sp++);      /* r */
569                                 *(dp++) = *(sp++);      /* g */
570                                 *(dp++) = *(sp++);      /* b */
571                                 if (has_alpha) *(dp++) = *(sp++);       /* a */
572                                 }
573                         }
574                 }
575
576         return dest;
577 }
578
579 GdkPixbuf *pixbuf_apply_orientation(GdkPixbuf *pixbuf, gint orientation)
580 {
581         GdkPixbuf *dest;
582         GdkPixbuf *tmp = NULL;
583
584         switch (orientation)
585                 {
586                 case EXIF_ORIENTATION_TOP_LEFT:
587                         dest = gdk_pixbuf_copy(pixbuf);
588                         break;
589                 case EXIF_ORIENTATION_TOP_RIGHT:
590                         /* mirrored */
591                         dest = pixbuf_copy_mirror(pixbuf, TRUE, FALSE);
592                         break;
593                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
594                         /* upside down */
595                         dest = pixbuf_copy_mirror(pixbuf, TRUE, TRUE);
596                         break;
597                 case EXIF_ORIENTATION_BOTTOM_LEFT:
598                         /* flipped */
599                         dest = pixbuf_copy_mirror(pixbuf, FALSE, TRUE);
600                         break;
601                 case EXIF_ORIENTATION_LEFT_TOP:
602                         tmp = pixbuf_copy_mirror(pixbuf, FALSE, TRUE);
603                         dest = pixbuf_copy_rotate_90(tmp, FALSE);
604                         break;
605                 case EXIF_ORIENTATION_RIGHT_TOP:
606                         /* rotated -90 (270) */
607                         dest = pixbuf_copy_rotate_90(pixbuf, FALSE);
608                         break;
609                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
610                         tmp = pixbuf_copy_mirror(pixbuf, FALSE, TRUE);
611                         dest = pixbuf_copy_rotate_90(tmp, TRUE);
612                         break;
613                 case EXIF_ORIENTATION_LEFT_BOTTOM:
614                         /* rotated 90 */
615                         dest = pixbuf_copy_rotate_90(pixbuf, TRUE);
616                         break;
617                 default:
618                         dest = gdk_pixbuf_copy(pixbuf);
619                         break;
620                 }
621         if (tmp) g_object_unref(tmp);
622         return dest;
623
624 }
625
626
627 /*
628  *-----------------------------------------------------------------------------
629  * pixbuf drawing (rectangles)
630  *-----------------------------------------------------------------------------
631  */
632
633 /*
634  * Fills region of pixbuf at x,y over w,h
635  * with colors red (r), green (g), blue (b)
636  * applying alpha (a), use a=255 for solid.
637  */
638 void pixbuf_draw_rect_fill(GdkPixbuf *pb,
639                            gint x, gint y, gint w, gint h,
640                            gint r, gint g, gint b, gint a)
641 {
642         gboolean has_alpha;
643         gint pw, ph, prs;
644         guchar *p_pix;
645         guchar *pp;
646         gint i, j;
647
648         if (!pb) return;
649
650         pw = gdk_pixbuf_get_width(pb);
651         ph = gdk_pixbuf_get_height(pb);
652
653         if (x < 0 || x + w > pw) return;
654         if (y < 0 || y + h > ph) return;
655
656         has_alpha = gdk_pixbuf_get_has_alpha(pb);
657         prs = gdk_pixbuf_get_rowstride(pb);
658         p_pix = gdk_pixbuf_get_pixels(pb);
659
660         for (i = 0; i < h; i++)
661                 {
662                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
663                 for (j = 0; j < w; j++)
664                         {
665                         *pp = (r * a + *pp * (256-a)) >> 8;
666                         pp++;
667                         *pp = (g * a + *pp * (256-a)) >> 8;
668                         pp++;
669                         *pp = (b * a + *pp * (256-a)) >> 8;
670                         pp++;
671                         if (has_alpha) pp++;
672                         }
673                 }
674 }
675
676 void pixbuf_draw_rect(GdkPixbuf *pb,
677                       gint x, gint y, gint w, gint h,
678                       gint r, gint g, gint b, gint a,
679                       gint left, gint right, gint top, gint bottom)
680 {
681         pixbuf_draw_rect_fill(pb, x + left, y, w - left - right, top,
682                               r, g, b ,a);
683         pixbuf_draw_rect_fill(pb, x + w - right, y, right, h,
684                               r, g, b ,a);
685         pixbuf_draw_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
686                               r, g, b ,a);
687         pixbuf_draw_rect_fill(pb, x, y, left, h,
688                               r, g, b ,a);
689 }
690
691 void pixbuf_set_rect_fill(GdkPixbuf *pb,
692                           gint x, gint y, gint w, gint h,
693                           gint r, gint g, gint b, gint a)
694 {
695         gboolean has_alpha;
696         gint pw, ph, prs;
697         guchar *p_pix;
698         guchar *pp;
699         gint i, j;
700
701         if (!pb) return;
702
703         pw = gdk_pixbuf_get_width(pb);
704         ph = gdk_pixbuf_get_height(pb);
705
706         if (x < 0 || x + w > pw) return;
707         if (y < 0 || y + h > ph) return;
708
709         has_alpha = gdk_pixbuf_get_has_alpha(pb);
710         prs = gdk_pixbuf_get_rowstride(pb);
711         p_pix = gdk_pixbuf_get_pixels(pb);
712
713         for (i = 0; i < h; i++)
714                 {
715                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
716                 for (j = 0; j < w; j++)
717                         {
718                         *pp = r; pp++;
719                         *pp = g; pp++;
720                         *pp = b; pp++;
721                         if (has_alpha) { *pp = a; pp++; }
722                         }
723                 }
724 }
725
726 void pixbuf_set_rect(GdkPixbuf *pb,
727                      gint x, gint y, gint w, gint h,
728                      gint r, gint g, gint b, gint a,
729                      gint left, gint right, gint top, gint bottom)
730 {
731         pixbuf_set_rect_fill(pb, x + left, y, w - left - right, top,
732                              r, g, b ,a);
733         pixbuf_set_rect_fill(pb, x + w - right, y, right, h,
734                              r, g, b ,a);
735         pixbuf_set_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
736                              r, g, b ,a);
737         pixbuf_set_rect_fill(pb, x, y, left, h,
738                              r, g, b ,a);
739 }
740
741 void pixbuf_pixel_set(GdkPixbuf *pb, gint x, gint y, gint r, gint g, gint b, gint a)
742 {
743         guchar *buf;
744         gboolean has_alpha;
745         gint rowstride;
746         guchar *p;
747
748         if (x < 0 || x >= gdk_pixbuf_get_width(pb) ||
749             y < 0 || y >= gdk_pixbuf_get_height(pb)) return;
750
751         buf = gdk_pixbuf_get_pixels(pb);
752         has_alpha = gdk_pixbuf_get_has_alpha(pb);
753         rowstride = gdk_pixbuf_get_rowstride(pb);
754
755         p = buf + (y * rowstride) + (x * (has_alpha ? 4 : 3));
756         *p = r; p++;
757         *p = g; p++;
758         *p = b; p++;
759         if (has_alpha) *p = a;
760 }
761
762
763 /*
764  *-----------------------------------------------------------------------------
765  * pixbuf text rendering
766  *-----------------------------------------------------------------------------
767  */
768
769 static void pixbuf_copy_font(GdkPixbuf *src, gint sx, gint sy,
770                              GdkPixbuf *dest, gint dx, gint dy,
771                              gint w, gint h,
772                              guint8 r, guint8 g, guint8 b, guint8 a)
773 {
774         gint sw, sh, srs;
775         gboolean s_alpha;
776         gint s_step;
777         guchar *s_pix;
778         gint dw, dh, drs;
779         gboolean d_alpha;
780         gint d_step;
781         guchar *d_pix;
782
783         guchar *sp;
784         guchar *dp;
785         gint i, j;
786
787         if (!src || !dest) return;
788
789         sw = gdk_pixbuf_get_width(src);
790         sh = gdk_pixbuf_get_height(src);
791
792         if (sx < 0 || sx + w > sw) return;
793         if (sy < 0 || sy + h > sh) return;
794
795         dw = gdk_pixbuf_get_width(dest);
796         dh = gdk_pixbuf_get_height(dest);
797
798         if (dx < 0 || dx + w > dw) return;
799         if (dy < 0 || dy + h > dh) return;
800
801         s_alpha = gdk_pixbuf_get_has_alpha(src);
802         d_alpha = gdk_pixbuf_get_has_alpha(dest);
803         srs = gdk_pixbuf_get_rowstride(src);
804         drs = gdk_pixbuf_get_rowstride(dest);
805         s_pix = gdk_pixbuf_get_pixels(src);
806         d_pix = gdk_pixbuf_get_pixels(dest);
807
808         s_step = (s_alpha) ? 4 : 3;
809         d_step = (d_alpha) ? 4 : 3;
810
811         for (i = 0; i < h; i++)
812                 {
813                 sp = s_pix + (sy + i) * srs + sx * s_step;
814                 dp = d_pix + (dy + i) * drs + dx * d_step;
815                 for (j = 0; j < w; j++)
816                         {
817                         if (*sp)
818                                 {
819                                 guint8 asub;
820
821                                 asub = a * sp[0] / 255;
822                                 *dp = (r * asub + *dp * (256-asub)) >> 8;
823                                 dp++;
824                                 asub = a * sp[1] / 255;
825                                 *dp = (g * asub + *dp * (256-asub)) >> 8;
826                                 dp++;
827                                 asub = a * sp[2] / 255;
828                                 *dp = (b * asub + *dp * (256-asub)) >> 8;
829                                 dp++;
830
831                                 if (d_alpha)
832                                         {
833                                         *dp = MAX(*dp, a * ((sp[0] + sp[1] + sp[2]) / 3) / 255);
834                                         dp++;
835                                         }
836                                 }
837                         else
838                                 {
839                                 dp += d_step;
840                                 }
841
842                         sp += s_step;
843                         }
844                 }
845 }
846
847 void pixbuf_draw_layout(GdkPixbuf *pixbuf, PangoLayout *layout, GtkWidget *widget,
848                         gint x, gint y,
849                         guint8 r, guint8 g, guint8 b, guint8 a)
850 {
851         GdkPixbuf *buffer;
852         gint w, h;
853         gint sx, sy;
854         gint dw, dh;
855         cairo_surface_t *source;
856         cairo_t *cr;
857
858         pango_layout_get_pixel_size(layout, &w, &h);
859         if (w < 1 || h < 1) return;
860
861         source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
862
863         cr = cairo_create (source);
864         cairo_set_source_rgb(cr, 0, 0, 0);
865         cairo_rectangle (cr, 0, 0, w, h);
866         cairo_fill (cr);
867         cairo_set_source_rgb(cr, 1, 1, 1);
868         pango_cairo_show_layout (cr, layout);
869         cairo_destroy (cr);
870
871         buffer = gdk_pixbuf_new_from_data (cairo_image_surface_get_data (source),
872                                            GDK_COLORSPACE_RGB,
873                                            cairo_image_surface_get_format (source) == CAIRO_FORMAT_ARGB32,
874                                            8,
875                                            cairo_image_surface_get_width (source),
876                                            cairo_image_surface_get_height (source),
877                                            cairo_image_surface_get_stride (source),
878                                            NULL,
879                                            NULL);
880
881         sx = 0;
882         sy = 0;
883         dw = gdk_pixbuf_get_width(pixbuf);
884         dh = gdk_pixbuf_get_height(pixbuf);
885
886         if (x < 0)
887                 {
888                 w += x;
889                 sx = -x;
890                 x = 0;
891                 }
892
893         if (y < 0)
894                 {
895                 h += y;
896                 sy = -y;
897                 y = 0;
898                 }
899
900         if (x + w > dw) w = dw - x;
901         if (y + h > dh) h = dh - y;
902
903         pixbuf_copy_font(buffer, sx, sy,
904                          pixbuf, x, y, w, h,
905                          r, g, b, a);
906
907         g_object_unref(buffer);
908         cairo_surface_destroy(source);
909 }
910
911 /*
912  *-----------------------------------------------------------------------------
913  * pixbuf drawing (triangle)
914  *-----------------------------------------------------------------------------
915  */
916
917 void util_clip_triangle(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
918                         gint *rx, gint *ry, gint *rw, gint *rh)
919 {
920         gint tx, ty, tw, th;
921
922         tx = MIN(x1, x2);
923         tx = MIN(tx, x3);
924         ty = MIN(y1, y2);
925         ty = MIN(ty, y3);
926         tw = MAX(abs(x1 - x2), abs(x2 - x3));
927         tw = MAX(tw, abs(x3 - x1));
928         th = MAX(abs(y1 - y2), abs(y2 - y3));
929         th = MAX(th, abs(y3 - y1));
930
931         *rx = tx;
932         *ry = ty;
933         *rw = tw;
934         *rh = th;
935 }
936
937 void pixbuf_draw_triangle(GdkPixbuf *pb,
938                           gint clip_x, gint clip_y, gint clip_w, gint clip_h,
939                           gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
940                           guint8 r, guint8 g, guint8 b, guint8 a)
941 {
942         gboolean has_alpha;
943         gint pw, ph, prs;
944         gint rx, ry, rw, rh;
945         gint tx, ty, tw, th;
946         gint fx1, fy1;
947         gint fx2, fy2;
948         gint fw, fh;
949         guchar *p_pix;
950         guchar *pp;
951         gint p_step;
952         gdouble slope1, slope2;
953         gint slope1_x, slope1_y;
954         gint y;
955         gint t;
956         gboolean middle = FALSE;
957
958         if (!pb) return;
959
960         pw = gdk_pixbuf_get_width(pb);
961         ph = gdk_pixbuf_get_height(pb);
962
963         if (!util_clip_region(0, 0, pw, ph,
964                               clip_x, clip_y, clip_w, clip_h,
965                               &rx, &ry, &rw, &rh)) return;
966
967         util_clip_triangle(x1, y1, x2, y2, x3, y3,
968                            &tx, &ty, &tw, &th);
969
970         if (!util_clip_region(rx, ry, rw, rh,
971                               tx, ty, tw, th,
972                               &fx1, &fy1, &fw, &fh)) return;
973         fx2 = fx1 + fw;
974         fy2 = fy1 + fh;
975
976         has_alpha = gdk_pixbuf_get_has_alpha(pb);
977         prs = gdk_pixbuf_get_rowstride(pb);
978         p_pix = gdk_pixbuf_get_pixels(pb);
979
980         p_step = (has_alpha) ? 4 : 3;
981
982         if (y1 > y2)
983                 {
984                 t = x1; x1 = x2; x2 = t;
985                 t = y1; y1 = y2; y2 = t;
986                 }
987         if (y2 > y3)
988                 {
989                 t = x2; x2 = x3; x3 = t;
990                 t = y2; y2 = y3; y3 = t;
991                 }
992         if (y1 > y2)
993                 {
994                 t = x1; x1 = x2; x2 = t;
995                 t = y1; y1 = y2; y2 = t;
996                 }
997
998         slope1 = (gdouble)(y2 - y1);
999         if (slope1) slope1 = (gdouble)(x2 - x1) / slope1;
1000         slope1_x = x1;
1001         slope1_y = y1;
1002         slope2 = (gdouble)(y3 - y1);
1003         if (slope2) slope2 = (gdouble)(x3 - x1) / slope2;
1004
1005         for (y = fy1; y < fy2; y++)
1006                 {
1007                 gint xa, xb;
1008
1009                 if (!middle && y > y2)
1010                         {
1011                         slope1 = (gdouble)(y3 - y2);
1012                         if (slope1) slope1 = (gdouble)(x3 - x2) / slope1;
1013                         slope1_x = x2;
1014                         slope1_y = y2;
1015
1016                         middle = TRUE;
1017                         }
1018
1019                 xa = slope1_x + ((gdouble)slope1 * (y - slope1_y) + 0.5);
1020                 xb = x1 + ((gdouble)slope2 * (y - y1) + 0.5);
1021
1022                 if (xa > xb)
1023                         {
1024                         t = xa; xa = xb; xb = t;
1025                         }
1026
1027                 xa = CLAMP(xa, fx1, fx2);
1028                 xb = CLAMP(xb, fx1, fx2);
1029
1030                 pp = p_pix + y * prs + xa * p_step;
1031
1032                 while (xa < xb)
1033                         {
1034                         *pp = (r * a + *pp * (256-a)) >> 8;
1035                         pp++;
1036                         *pp = (g * a + *pp * (256-a)) >> 8;
1037                         pp++;
1038                         *pp = (b * a + *pp * (256-a)) >> 8;
1039                         pp++;
1040                         if (has_alpha) pp++;
1041
1042                         xa++;
1043                         }
1044                 }
1045 }
1046
1047 /*
1048  *-----------------------------------------------------------------------------
1049  * pixbuf drawing (line)
1050  *-----------------------------------------------------------------------------
1051  */
1052
1053 static gboolean util_clip_line(gdouble clip_x, gdouble clip_y, gdouble clip_w, gdouble clip_h,
1054                                gdouble x1, gdouble y1, gdouble x2, gdouble y2,
1055                                gdouble *rx1, gdouble *ry1, gdouble *rx2, gdouble *ry2)
1056 {
1057         gboolean flip = FALSE;
1058         gdouble d;
1059
1060         if (x1 > x2)
1061                 {
1062                 gdouble t;
1063
1064                 t = x1; x1 = x2; x2 = t;
1065                 t = y1; y1 = y2; y2 = t;
1066                 flip = TRUE;
1067                 }
1068
1069         if (x2 < clip_x || x1 > clip_x + clip_w) return FALSE;
1070
1071         if (y1 < y2)
1072                 {
1073                 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
1074                 }
1075         else
1076                 {
1077                 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
1078                 }
1079
1080         d = x2 - x1;
1081         if (d > 0.0)
1082                 {
1083                 gdouble slope;
1084
1085                 slope = (y2 - y1) / d;
1086                 if (x1 < clip_x)
1087                         {
1088                         y1 = y1 + slope * (clip_x - x1);
1089                         x1 = clip_x;
1090                         }
1091                 if (x2 > clip_x + clip_w)
1092                         {
1093                         y2 = y2 + slope * (clip_x + clip_w - x2);
1094                         x2 = clip_x + clip_w;
1095                         }
1096                 }
1097
1098         if (y1 < y2)
1099                 {
1100                 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
1101                 }
1102         else
1103                 {
1104                 gdouble t;
1105
1106                 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
1107
1108                 t = x1; x1 = x2; x2 = t;
1109                 t = y1; y1 = y2; y2 = t;
1110                 flip = !flip;
1111                 }
1112
1113         d = y2 - y1;
1114         if (d > 0.0)
1115                 {
1116                 gdouble slope;
1117
1118                 slope = (x2 - x1) / d;
1119                 if (y1 < clip_y)
1120                         {
1121                         x1 = x1 + slope * (clip_y - y1);
1122                         y1 = clip_y;
1123                         }
1124                 if (y2 > clip_y + clip_h)
1125                         {
1126                         x2 = x2 + slope * (clip_y + clip_h - y2);
1127                         y2 = clip_y + clip_h;
1128                         }
1129                 }
1130
1131         if (flip)
1132                 {
1133                 *rx1 = x2;
1134                 *ry1 = y2;
1135                 *rx2 = x1;
1136                 *ry2 = y1;
1137                 }
1138         else
1139                 {
1140                 *rx1 = x1;
1141                 *ry1 = y1;
1142                 *rx2 = x2;
1143                 *ry2 = y2;
1144                 }
1145
1146         return TRUE;
1147 }
1148
1149 void pixbuf_draw_line(GdkPixbuf *pb,
1150                       gint clip_x, gint clip_y, gint clip_w, gint clip_h,
1151                       gint x1, gint y1, gint x2, gint y2,
1152                       guint8 r, guint8 g, guint8 b, guint8 a)
1153 {
1154         gboolean has_alpha;
1155         gint pw, ph, prs;
1156         gint rx, ry, rw, rh;
1157         gdouble rx1, ry1, rx2, ry2;
1158         guchar *p_pix;
1159         guchar *pp;
1160         gint p_step;
1161         gdouble slope;
1162         gdouble x, y;
1163         gint px, py;
1164         gint cx1, cy1, cx2, cy2;
1165
1166         if (!pb) return;
1167
1168         pw = gdk_pixbuf_get_width(pb);
1169         ph = gdk_pixbuf_get_height(pb);
1170
1171         if (!util_clip_region(0, 0, pw, ph,
1172                               clip_x, clip_y, clip_w, clip_h,
1173                               &rx, &ry, &rw, &rh)) return;
1174         if (!util_clip_line((gdouble)rx, (gdouble)ry, (gdouble)rw, (gdouble)rh,
1175                             (gdouble)x1, (gdouble)y1, (gdouble)x2, (gdouble)y2,
1176                             &rx1, &ry1, &rx2, &ry2)) return;
1177
1178         cx1 = rx;
1179         cy1 = ry;
1180         cx2 = rx + rw;
1181         cy2 = ry + rh;
1182
1183         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1184         prs = gdk_pixbuf_get_rowstride(pb);
1185         p_pix = gdk_pixbuf_get_pixels(pb);
1186
1187         p_step = (has_alpha) ? 4 : 3;
1188
1189         if (fabs(rx2 - rx1) > fabs(ry2 - ry1))
1190                 {
1191                 if (rx1 > rx2)
1192                         {
1193                         gdouble t;
1194                         t = rx1; rx1 = rx2; rx2 = t;
1195                         t = ry1; ry1 = ry2; ry2 = t;
1196                         }
1197
1198                 slope = rx2 - rx1;
1199                 if (slope != 0.0) slope = (ry2 - ry1) / slope;
1200                 for (x = rx1; x < rx2; x += 1.0)
1201                         {
1202                         px = (gint)(x + 0.5);
1203                         py = (gint)(ry1 + (x - rx1) * slope + 0.5);
1204
1205                         if (px >=  cx1 && px < cx2 && py >= cy1 && py < cy2)
1206                                 {
1207                                 pp = p_pix + py * prs + px * p_step;
1208                                 *pp = (r * a + *pp * (256-a)) >> 8;
1209                                 pp++;
1210                                 *pp = (g * a + *pp * (256-a)) >> 8;
1211                                 pp++;
1212                                 *pp = (b * a + *pp * (256-a)) >> 8;
1213                                 }
1214                         }
1215                 }
1216         else
1217                 {
1218                 if (ry1 > ry2)
1219                         {
1220                         gdouble t;
1221                         t = rx1; rx1 = rx2; rx2 = t;
1222                         t = ry1; ry1 = ry2; ry2 = t;
1223                         }
1224
1225                 slope = ry2 - ry1;
1226                 if (slope != 0.0) slope = (rx2 - rx1) / slope;
1227                 for (y = ry1; y < ry2; y += 1.0)
1228                         {
1229                         px = (gint)(rx1 + (y - ry1) * slope + 0.5);
1230                         py = (gint)(y + 0.5);
1231
1232                         if (px >=  cx1 && px < cx2 && py >= cy1 && py < cy2)
1233                                 {
1234                                 pp = p_pix + py * prs + px * p_step;
1235                                 *pp = (r * a + *pp * (256-a)) >> 8;
1236                                 pp++;
1237                                 *pp = (g * a + *pp * (256-a)) >> 8;
1238                                 pp++;
1239                                 *pp = (b * a + *pp * (256-a)) >> 8;
1240                                 }
1241                         }
1242                 }
1243 }
1244
1245 /*
1246  *-----------------------------------------------------------------------------
1247  * pixbuf drawing (fades and shadows)
1248  *-----------------------------------------------------------------------------
1249  */
1250
1251 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gboolean has_alpha,
1252                                     gint s, gboolean vertical, gint border,
1253                                     gint x1, gint y1, gint x2, gint y2,
1254                                     guint8 r, guint8 g, guint8 b, guint8 a)
1255 {
1256         guchar *pp;
1257         gint p_step;
1258         guint8 n = a;
1259         gint i, j;
1260
1261         p_step = (has_alpha) ? 4 : 3;
1262         for (j = y1; j < y2; j++)
1263                 {
1264                 pp = p_pix + j * prs + x1 * p_step;
1265                 if (!vertical) n = a - a * abs(j - s) / border;
1266                 for (i = x1; i < x2; i++)
1267                         {
1268                         if (vertical) n = a - a * abs(i - s) / border;
1269                         *pp = (r * n + *pp * (256-n)) >> 8;
1270                         pp++;
1271                         *pp = (g * n + *pp * (256-n)) >> 8;
1272                         pp++;
1273                         *pp = (b * n + *pp * (256-n)) >> 8;
1274                         pp++;
1275                         if (has_alpha) pp++;
1276                         }
1277                 }
1278 }
1279
1280 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gboolean has_alpha,
1281                                     gint sx, gint sy, gint border,
1282                                     gint x1, gint y1, gint x2, gint y2,
1283                                     guint8 r, guint8 g, guint8 b, guint8 a)
1284 {
1285         guchar *pp;
1286         gint p_step;
1287         gint i, j;
1288
1289         p_step = (has_alpha) ? 4 : 3;
1290         for (j = y1; j < y2; j++)
1291                 {
1292                 pp = p_pix + j * prs + x1 * p_step;
1293                 for (i = x1; i < x2; i++)
1294                         {
1295                         guint8 n;
1296                         gint r;
1297
1298                         r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
1299                         n = a - a * r / border;
1300                         *pp = (r * n + *pp * (256-n)) >> 8;
1301                         pp++;
1302                         *pp = (g * n + *pp * (256-n)) >> 8;
1303                         pp++;
1304                         *pp = (b * n + *pp * (256-n)) >> 8;
1305                         pp++;
1306                         if (has_alpha) pp++;
1307                         }
1308                 }
1309 }
1310
1311 void pixbuf_draw_shadow(GdkPixbuf *pb,
1312                         gint clip_x, gint clip_y, gint clip_w, gint clip_h,
1313                         gint x, gint y, gint w, gint h, gint border,
1314                         guint8 r, guint8 g, guint8 b, guint8 a)
1315 {
1316         gint has_alpha;
1317         gint pw, ph, prs;
1318         gint rx, ry, rw, rh;
1319         gint fx, fy, fw, fh;
1320         guchar *p_pix;
1321
1322         if (!pb) return;
1323
1324         pw = gdk_pixbuf_get_width(pb);
1325         ph = gdk_pixbuf_get_height(pb);
1326
1327         if (!util_clip_region(0, 0, pw, ph,
1328                               clip_x, clip_y, clip_w, clip_h,
1329                               &rx, &ry, &rw, &rh)) return;
1330
1331         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1332         prs = gdk_pixbuf_get_rowstride(pb);
1333         p_pix = gdk_pixbuf_get_pixels(pb);
1334
1335         if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
1336                              rx, ry, rw, rh,
1337                              &fx, &fy, &fw, &fh))
1338                 {
1339                 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
1340                 }
1341
1342         if (border < 1) return;
1343
1344         if (util_clip_region(x, y + border, border, h - border * 2,
1345                              rx, ry, rw, rh,
1346                              &fx, &fy, &fw, &fh))
1347                 {
1348                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1349                                         x + border, TRUE, border,
1350                                         fx, fy, fx + fw, fy + fh,
1351                                         r, g, b, a);
1352                 }
1353         if (util_clip_region(x + w - border, y + border, border, h - border * 2,
1354                              rx, ry, rw, rh,
1355                              &fx, &fy, &fw, &fh))
1356                 {
1357                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1358                                         x + w - border, TRUE, border,
1359                                         fx, fy, fx + fw, fy + fh,
1360                                         r, g, b, a);
1361                 }
1362         if (util_clip_region(x + border, y, w - border * 2, border,
1363                              rx, ry, rw, rh,
1364                              &fx, &fy, &fw, &fh))
1365                 {
1366                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1367                                         y + border, FALSE, border,
1368                                         fx, fy, fx + fw, fy + fh,
1369                                         r, g, b, a);
1370                 }
1371         if (util_clip_region(x + border, y + h - border, w - border * 2, border,
1372                              rx, ry, rw, rh,
1373                              &fx, &fy, &fw, &fh))
1374                 {
1375                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1376                                         y + h - border, FALSE, border,
1377                                         fx, fy, fx + fw, fy + fh,
1378                                         r, g, b, a);
1379                 }
1380         if (util_clip_region(x, y, border, border,
1381                              rx, ry, rw, rh,
1382                              &fx, &fy, &fw, &fh))
1383                 {
1384                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1385                                         x + border, y + border, border,
1386                                         fx, fy, fx + fw, fy + fh,
1387                                         r, g, b, a);
1388                 }
1389         if (util_clip_region(x + w - border, y, border, border,
1390                              rx, ry, rw, rh,
1391                              &fx, &fy, &fw, &fh))
1392                 {
1393                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1394                                         x + w - border, y + border, border,
1395                                         fx, fy, fx + fw, fy + fh,
1396                                         r, g, b, a);
1397                 }
1398         if (util_clip_region(x, y + h - border, border, border,
1399                              rx, ry, rw, rh,
1400                              &fx, &fy, &fw, &fh))
1401                 {
1402                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1403                                         x + border, y + h - border, border,
1404                                         fx, fy, fx + fw, fy + fh,
1405                                         r, g, b, a);
1406                 }
1407         if (util_clip_region(x + w - border, y + h - border, border, border,
1408                              rx, ry, rw, rh,
1409                              &fx, &fy, &fw, &fh))
1410                 {
1411                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1412                                         x + w - border, y + h - border, border,
1413                                         fx, fy, fx + fw, fy + fh,
1414                                         r, g, b, a);
1415                 }
1416 }
1417
1418
1419 /*
1420  *-----------------------------------------------------------------------------
1421  * pixbuf color alterations
1422  *-----------------------------------------------------------------------------
1423  */
1424
1425 void pixbuf_desaturate_rect(GdkPixbuf *pb,
1426                             gint x, gint y, gint w, gint h)
1427 {
1428         gboolean has_alpha;
1429         gint pw, ph, prs;
1430         guchar *p_pix;
1431         guchar *pp;
1432         gint i, j;
1433
1434         if (!pb) return;
1435
1436         pw = gdk_pixbuf_get_width(pb);
1437         ph = gdk_pixbuf_get_height(pb);
1438
1439         if (x < 0 || x + w > pw) return;
1440         if (y < 0 || y + h > ph) return;
1441
1442         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1443         prs = gdk_pixbuf_get_rowstride(pb);
1444         p_pix = gdk_pixbuf_get_pixels(pb);
1445
1446         for (i = 0; i < h; i++)
1447                 {
1448                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
1449                 for (j = 0; j < w; j++)
1450                         {
1451                         guint8 grey;
1452
1453                         grey = (pp[0] + pp[1] + pp[2]) / 3;
1454                         *pp = grey;
1455                         pp++;
1456                         *pp = grey;
1457                         pp++;
1458                         *pp = grey;
1459                         pp++;
1460                         if (has_alpha) pp++;
1461                         }
1462                 }
1463 }
1464
1465 /*
1466  *-----------------------------------------------------------------------------
1467  * pixbuf highlight under/over exposure *-----------------------------------------------------------------------------
1468  */
1469 void pixbuf_highlight_overunderexposed(GdkPixbuf *pb, gint x, gint y, gint w, gint h)
1470 {
1471         gboolean has_alpha;
1472         gint pw, ph, prs;
1473         guchar *p_pix;
1474         guchar *pp;
1475         gint i, j;
1476
1477         if (!pb) return;
1478
1479         pw = gdk_pixbuf_get_width(pb);
1480         ph = gdk_pixbuf_get_height(pb);
1481
1482         if (x < 0 || x + w > pw) return;
1483         if (y < 0 || y + h > ph) return;
1484
1485         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1486         prs = gdk_pixbuf_get_rowstride(pb);
1487         p_pix = gdk_pixbuf_get_pixels(pb);
1488
1489         for (i = 0; i < h; i++)
1490                 {
1491                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
1492                 for (j = 0; j < w; j++)
1493                         {
1494                         if (pp[0] == 255 || pp[1] == 255 || pp[2] == 255 || pp[0] == 0 || pp[1] == 0 || pp[2] == 0)
1495                                 {
1496                                 *pp = 255;
1497                                 pp++;
1498                                 *pp = 0;
1499                                 pp++;
1500                                 *pp = 0;
1501                                 pp++;
1502                                 if (has_alpha) pp++;
1503                                 }
1504                         else
1505                                 {
1506                                 pp = pp + 3;
1507                                 if (has_alpha) pp++;
1508                                 }
1509                         }
1510                 }
1511 }
1512
1513 /*
1514  *-----------------------------------------------------------------------------
1515  * pixbuf ignore alpha
1516  *-----------------------------------------------------------------------------
1517 */
1518 void pixbuf_ignore_alpha_rect(GdkPixbuf *pb,
1519                  gint x, gint y, gint w, gint h)
1520 {
1521    gboolean has_alpha;
1522    gint pw, ph, prs;
1523    guchar *p_pix;
1524    guchar *pp;
1525    gint i, j;
1526
1527    if (!pb) return;
1528
1529    pw = gdk_pixbuf_get_width(pb);
1530    ph = gdk_pixbuf_get_height(pb);
1531
1532    if (x < 0 || x + w > pw) return;
1533    if (y < 0 || y + h > ph) return;
1534
1535    has_alpha = gdk_pixbuf_get_has_alpha(pb);
1536    if (!has_alpha) return;
1537
1538    prs = gdk_pixbuf_get_rowstride(pb);
1539    p_pix = gdk_pixbuf_get_pixels(pb);
1540
1541    for (i = 0; i < h; i++)
1542        {
1543        pp = p_pix + (y + i) * prs + (x * 4 );
1544        for (j = 0; j < w; j++)
1545            {
1546            pp[3] = 0xff;
1547            pp+=4;
1548            }
1549        }
1550 }
1551 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */