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