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