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