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