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