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