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