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