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