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