Remove structs and some enums from typedefs.h
[geeqie.git] / src / pixbuf-util.cc
1 /*
2  * Copyright (C) 2004 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "pixbuf-util.h"
24
25 #include "exif.h"
26 #include "filedata.h"
27 #include "ui-fileops.h"
28
29 #include <cmath>
30
31
32 /*
33  *-----------------------------------------------------------------------------
34  * png save
35  *-----------------------------------------------------------------------------
36  */
37
38 gboolean pixbuf_to_file_as_png(GdkPixbuf *pixbuf, const gchar *filename)
39 {
40         GError *error = nullptr;
41         gboolean ret;
42
43         if (!pixbuf || !filename) return FALSE;
44
45         ret = gdk_pixbuf_save(pixbuf, filename, "png", &error,
46                               "tEXt::Software", GQ_APPNAME " " VERSION, NULL);
47
48         if (error)
49                 {
50                 log_printf("Error saving png file: %s\n", error->message);
51                 g_error_free(error);
52                 }
53
54         return ret;
55 }
56
57 /*
58  *-----------------------------------------------------------------------------
59  * jpeg save
60  *-----------------------------------------------------------------------------
61  */
62
63 #pragma GCC diagnostic push
64 #pragma GCC diagnostic ignored "-Wunused-function"
65 gboolean pixbuf_to_file_as_jpg_unused(GdkPixbuf *pixbuf, const gchar *filename, gint quality)
66 {
67         GError *error = NULL;
68         gchar *qbuf;
69         gboolean ret;
70
71         if (!pixbuf || !filename) return FALSE;
72
73         if (quality == -1) quality = 75;
74         if (quality < 1 || quality > 100)
75                 {
76                 log_printf("Jpeg not saved, invalid quality %d\n", quality);
77                 return FALSE;
78                 }
79
80         qbuf = g_strdup_printf("%d", quality);
81         ret = gdk_pixbuf_save(pixbuf, filename, "jpeg", &error, "quality", qbuf, NULL);
82         g_free(qbuf);
83
84         if (error)
85                 {
86                 log_printf("Error saving jpeg to %s\n%s\n", filename, error->message);
87                 g_error_free(error);
88                 }
89
90         return ret;
91 }
92 #pragma GCC diagnostic pop
93
94 /*
95  *-----------------------------------------------------------------------------
96  * pixbuf from inline
97  *-----------------------------------------------------------------------------
98  */
99
100 struct PixbufInline
101 {
102         const gchar *key;
103         const gchar *data;
104 };
105
106 static PixbufInline inline_pixbuf_data[] = {
107         { PIXBUF_INLINE_ARCHIVE,        "gq-archive-file.png" },
108         { PIXBUF_INLINE_BROKEN, "gq-sheet-broken.png" },
109         { PIXBUF_INLINE_COLLECTION,     "gq-collection.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_CONFIG,    "gq-icon-config.png" },
114         { PIXBUF_INLINE_ICON_DRAW_RECTANGLE,    "gq-icon-draw-rectangle.png" },
115         { PIXBUF_INLINE_ICON_EXIF,      "gq-icon-exif.png" },
116         { PIXBUF_INLINE_ICON_EXPOSURE,  "gq-icon-exposure.png" },
117         { PIXBUF_INLINE_ICON_FILE_FILTER,       "gq-icon-file-filter.png" },
118         { PIXBUF_INLINE_ICON_FLOAT,     "gq-icon-float.png" },
119         { PIXBUF_INLINE_ICON_GRAYSCALE, "gq-icon-grayscale.png" },
120         { PIXBUF_INLINE_ICON_HEIF,      "gq-icon-heic.png" },
121         { PIXBUF_INLINE_ICON_HIDETOOLS,         "gq-icon-hidetools.png" },
122         { PIXBUF_INLINE_ICON_MAINTENANCE,       "gq-icon-maintenance.png" },
123         { PIXBUF_INLINE_ICON_MARKS,     "gq-icon-marks.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()
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 #pragma GCC diagnostic push
700 #pragma GCC diagnostic ignored "-Wunused-function"
701 void pixbuf_draw_rect_unused(GdkPixbuf *pb,
702                       gint x, gint y, gint w, gint h,
703                       gint r, gint g, gint b, gint a,
704                       gint left, gint right, gint top, gint bottom)
705 {
706         pixbuf_draw_rect_fill(pb, x + left, y, w - left - right, top,
707                               r, g, b ,a);
708         pixbuf_draw_rect_fill(pb, x + w - right, y, right, h,
709                               r, g, b ,a);
710         pixbuf_draw_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
711                               r, g, b ,a);
712         pixbuf_draw_rect_fill(pb, x, y, left, h,
713                               r, g, b ,a);
714 }
715 #pragma GCC diagnostic pop
716
717 void pixbuf_set_rect_fill(GdkPixbuf *pb,
718                           gint x, gint y, gint w, gint h,
719                           gint r, gint g, gint b, gint a)
720 {
721         gboolean has_alpha;
722         gint pw, ph, prs;
723         guchar *p_pix;
724         guchar *pp;
725         gint i, j;
726
727         if (!pb) return;
728
729         pw = gdk_pixbuf_get_width(pb);
730         ph = gdk_pixbuf_get_height(pb);
731
732         if (x < 0 || x + w > pw) return;
733         if (y < 0 || y + h > ph) return;
734
735         has_alpha = gdk_pixbuf_get_has_alpha(pb);
736         prs = gdk_pixbuf_get_rowstride(pb);
737         p_pix = gdk_pixbuf_get_pixels(pb);
738
739         for (i = 0; i < h; i++)
740                 {
741                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
742                 for (j = 0; j < w; j++)
743                         {
744                         *pp = r; pp++;
745                         *pp = g; pp++;
746                         *pp = b; pp++;
747                         if (has_alpha) { *pp = a; pp++; }
748                         }
749                 }
750 }
751
752 void pixbuf_set_rect(GdkPixbuf *pb,
753                      gint x, gint y, gint w, gint h,
754                      gint r, gint g, gint b, gint a,
755                      gint left, gint right, gint top, gint bottom)
756 {
757         pixbuf_set_rect_fill(pb, x + left, y, w - left - right, top,
758                              r, g, b ,a);
759         pixbuf_set_rect_fill(pb, x + w - right, y, right, h,
760                              r, g, b ,a);
761         pixbuf_set_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
762                              r, g, b ,a);
763         pixbuf_set_rect_fill(pb, x, y, left, h,
764                              r, g, b ,a);
765 }
766
767 void pixbuf_pixel_set(GdkPixbuf *pb, gint x, gint y, gint r, gint g, gint b, gint a)
768 {
769         guchar *buf;
770         gboolean has_alpha;
771         gint rowstride;
772         guchar *p;
773
774         if (x < 0 || x >= gdk_pixbuf_get_width(pb) ||
775             y < 0 || y >= gdk_pixbuf_get_height(pb)) return;
776
777         buf = gdk_pixbuf_get_pixels(pb);
778         has_alpha = gdk_pixbuf_get_has_alpha(pb);
779         rowstride = gdk_pixbuf_get_rowstride(pb);
780
781         p = buf + (y * rowstride) + (x * (has_alpha ? 4 : 3));
782         *p = r; p++;
783         *p = g; p++;
784         *p = b; p++;
785         if (has_alpha) *p = a;
786 }
787
788
789 /*
790  *-----------------------------------------------------------------------------
791  * pixbuf text rendering
792  *-----------------------------------------------------------------------------
793  */
794
795 static void pixbuf_copy_font(GdkPixbuf *src, gint sx, gint sy,
796                              GdkPixbuf *dest, gint dx, gint dy,
797                              gint w, gint h,
798                              guint8 r, guint8 g, guint8 b, guint8 a)
799 {
800         gint sw, sh, srs;
801         gboolean s_alpha;
802         gint s_step;
803         guchar *s_pix;
804         gint dw, dh, drs;
805         gboolean d_alpha;
806         gint d_step;
807         guchar *d_pix;
808
809         guchar *sp;
810         guchar *dp;
811         gint i, j;
812
813         if (!src || !dest) return;
814
815         sw = gdk_pixbuf_get_width(src);
816         sh = gdk_pixbuf_get_height(src);
817
818         if (sx < 0 || sx + w > sw) return;
819         if (sy < 0 || sy + h > sh) return;
820
821         dw = gdk_pixbuf_get_width(dest);
822         dh = gdk_pixbuf_get_height(dest);
823
824         if (dx < 0 || dx + w > dw) return;
825         if (dy < 0 || dy + h > dh) return;
826
827         s_alpha = gdk_pixbuf_get_has_alpha(src);
828         d_alpha = gdk_pixbuf_get_has_alpha(dest);
829         srs = gdk_pixbuf_get_rowstride(src);
830         drs = gdk_pixbuf_get_rowstride(dest);
831         s_pix = gdk_pixbuf_get_pixels(src);
832         d_pix = gdk_pixbuf_get_pixels(dest);
833
834         s_step = (s_alpha) ? 4 : 3;
835         d_step = (d_alpha) ? 4 : 3;
836
837         for (i = 0; i < h; i++)
838                 {
839                 sp = s_pix + (sy + i) * srs + sx * s_step;
840                 dp = d_pix + (dy + i) * drs + dx * d_step;
841                 for (j = 0; j < w; j++)
842                         {
843                         if (*sp)
844                                 {
845                                 guint8 asub;
846
847                                 asub = a * sp[0] / 255;
848                                 *dp = (r * asub + *dp * (256-asub)) >> 8;
849                                 dp++;
850                                 asub = a * sp[1] / 255;
851                                 *dp = (g * asub + *dp * (256-asub)) >> 8;
852                                 dp++;
853                                 asub = a * sp[2] / 255;
854                                 *dp = (b * asub + *dp * (256-asub)) >> 8;
855                                 dp++;
856
857                                 if (d_alpha)
858                                         {
859                                         *dp = MAX(*dp, a * ((sp[0] + sp[1] + sp[2]) / 3) / 255);
860                                         dp++;
861                                         }
862                                 }
863                         else
864                                 {
865                                 dp += d_step;
866                                 }
867
868                         sp += s_step;
869                         }
870                 }
871 }
872
873 void pixbuf_draw_layout(GdkPixbuf *pixbuf, PangoLayout *layout, GtkWidget *,
874                         gint x, gint y,
875                         guint8 r, guint8 g, guint8 b, guint8 a)
876 {
877         GdkPixbuf *buffer;
878         gint w, h;
879         gint sx, sy;
880         gint dw, dh;
881         cairo_surface_t *source;
882         cairo_t *cr;
883
884         pango_layout_get_pixel_size(layout, &w, &h);
885         if (w < 1 || h < 1) return;
886
887         source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
888
889         cr = cairo_create (source);
890         cairo_set_source_rgb(cr, 0, 0, 0);
891         cairo_rectangle (cr, 0, 0, w, h);
892         cairo_fill (cr);
893         cairo_set_source_rgb(cr, 1, 1, 1);
894         pango_cairo_show_layout (cr, layout);
895         cairo_destroy (cr);
896
897         buffer = gdk_pixbuf_new_from_data (cairo_image_surface_get_data (source),
898                                            GDK_COLORSPACE_RGB,
899                                            cairo_image_surface_get_format (source) == CAIRO_FORMAT_ARGB32,
900                                            8,
901                                            cairo_image_surface_get_width (source),
902                                            cairo_image_surface_get_height (source),
903                                            cairo_image_surface_get_stride (source),
904                                            nullptr,
905                                            nullptr);
906
907         sx = 0;
908         sy = 0;
909         dw = gdk_pixbuf_get_width(pixbuf);
910         dh = gdk_pixbuf_get_height(pixbuf);
911
912         if (x < 0)
913                 {
914                 w += x;
915                 sx = -x;
916                 x = 0;
917                 }
918
919         if (y < 0)
920                 {
921                 h += y;
922                 sy = -y;
923                 y = 0;
924                 }
925
926         if (x + w > dw) w = dw - x;
927         if (y + h > dh) h = dh - y;
928
929         pixbuf_copy_font(buffer, sx, sy,
930                          pixbuf, x, y, w, h,
931                          r, g, b, a);
932
933         g_object_unref(buffer);
934         cairo_surface_destroy(source);
935 }
936
937 /*
938  *-----------------------------------------------------------------------------
939  * pixbuf drawing (triangle)
940  *-----------------------------------------------------------------------------
941  */
942
943 void util_clip_triangle(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
944                         gint *rx, gint *ry, gint *rw, gint *rh)
945 {
946         gint tx, ty, tw, th;
947
948         tx = MIN(x1, x2);
949         tx = MIN(tx, x3);
950         ty = MIN(y1, y2);
951         ty = MIN(ty, y3);
952         tw = MAX(abs(x1 - x2), abs(x2 - x3));
953         tw = MAX(tw, abs(x3 - x1));
954         th = MAX(abs(y1 - y2), abs(y2 - y3));
955         th = MAX(th, abs(y3 - y1));
956
957         *rx = tx;
958         *ry = ty;
959         *rw = tw;
960         *rh = th;
961 }
962
963 void pixbuf_draw_triangle(GdkPixbuf *pb,
964                           gint clip_x, gint clip_y, gint clip_w, gint clip_h,
965                           gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
966                           guint8 r, guint8 g, guint8 b, guint8 a)
967 {
968         gboolean has_alpha;
969         gint pw, ph, prs;
970         gint rx, ry, rw, rh;
971         gint tx, ty, tw, th;
972         gint fx1, fy1;
973         gint fx2, fy2;
974         gint fw, fh;
975         guchar *p_pix;
976         guchar *pp;
977         gint p_step;
978         gdouble slope1, slope2;
979         gint slope1_x, slope1_y;
980         gint y;
981         gint t;
982         gboolean middle = FALSE;
983
984         if (!pb) return;
985
986         pw = gdk_pixbuf_get_width(pb);
987         ph = gdk_pixbuf_get_height(pb);
988
989         if (!util_clip_region(0, 0, pw, ph,
990                               clip_x, clip_y, clip_w, clip_h,
991                               &rx, &ry, &rw, &rh)) return;
992
993         util_clip_triangle(x1, y1, x2, y2, x3, y3,
994                            &tx, &ty, &tw, &th);
995
996         if (!util_clip_region(rx, ry, rw, rh,
997                               tx, ty, tw, th,
998                               &fx1, &fy1, &fw, &fh)) return;
999         fx2 = fx1 + fw;
1000         fy2 = fy1 + fh;
1001
1002         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1003         prs = gdk_pixbuf_get_rowstride(pb);
1004         p_pix = gdk_pixbuf_get_pixels(pb);
1005
1006         p_step = (has_alpha) ? 4 : 3;
1007
1008         if (y1 > y2)
1009                 {
1010                 t = x1; x1 = x2; x2 = t;
1011                 t = y1; y1 = y2; y2 = t;
1012                 }
1013         if (y2 > y3)
1014                 {
1015                 t = x2; x2 = x3; x3 = t;
1016                 t = y2; y2 = y3; y3 = t;
1017                 }
1018         if (y1 > y2)
1019                 {
1020                 t = x1; x1 = x2; x2 = t;
1021                 t = y1; y1 = y2; y2 = t;
1022                 }
1023
1024         slope1 = static_cast<gdouble>(y2 - y1);
1025         if (slope1) slope1 = static_cast<gdouble>(x2 - x1) / slope1;
1026         slope1_x = x1;
1027         slope1_y = y1;
1028         slope2 = static_cast<gdouble>(y3 - y1);
1029         if (slope2) slope2 = static_cast<gdouble>(x3 - x1) / slope2;
1030
1031         for (y = fy1; y < fy2; y++)
1032                 {
1033                 gint xa, xb;
1034
1035                 if (!middle && y > y2)
1036                         {
1037                         slope1 = static_cast<gdouble>(y3 - y2);
1038                         if (slope1) slope1 = static_cast<gdouble>(x3 - x2) / slope1;
1039                         slope1_x = x2;
1040                         slope1_y = y2;
1041
1042                         middle = TRUE;
1043                         }
1044
1045                 xa = slope1_x + (slope1 * (y - slope1_y) + 0.5);
1046                 xb = x1 + (slope2 * (y - y1) + 0.5);
1047
1048                 if (xa > xb)
1049                         {
1050                         t = xa; xa = xb; xb = t;
1051                         }
1052
1053                 xa = CLAMP(xa, fx1, fx2);
1054                 xb = CLAMP(xb, fx1, fx2);
1055
1056                 pp = p_pix + y * prs + xa * p_step;
1057
1058                 while (xa < xb)
1059                         {
1060                         *pp = (r * a + *pp * (256-a)) >> 8;
1061                         pp++;
1062                         *pp = (g * a + *pp * (256-a)) >> 8;
1063                         pp++;
1064                         *pp = (b * a + *pp * (256-a)) >> 8;
1065                         pp++;
1066                         if (has_alpha) pp++;
1067
1068                         xa++;
1069                         }
1070                 }
1071 }
1072
1073 /*
1074  *-----------------------------------------------------------------------------
1075  * pixbuf drawing (line)
1076  *-----------------------------------------------------------------------------
1077  */
1078
1079 static gboolean util_clip_line(gdouble clip_x, gdouble clip_y, gdouble clip_w, gdouble clip_h,
1080                                gdouble x1, gdouble y1, gdouble x2, gdouble y2,
1081                                gdouble *rx1, gdouble *ry1, gdouble *rx2, gdouble *ry2)
1082 {
1083         gboolean flip = FALSE;
1084         gdouble d;
1085
1086         if (x1 > x2)
1087                 {
1088                 gdouble t;
1089
1090                 t = x1; x1 = x2; x2 = t;
1091                 t = y1; y1 = y2; y2 = t;
1092                 flip = TRUE;
1093                 }
1094
1095         if (x2 < clip_x || x1 > clip_x + clip_w) return FALSE;
1096
1097         if (y1 < y2)
1098                 {
1099                 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
1100                 }
1101         else
1102                 {
1103                 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
1104                 }
1105
1106         d = x2 - x1;
1107         if (d > 0.0)
1108                 {
1109                 gdouble slope;
1110
1111                 slope = (y2 - y1) / d;
1112                 if (x1 < clip_x)
1113                         {
1114                         y1 = y1 + slope * (clip_x - x1);
1115                         x1 = clip_x;
1116                         }
1117                 if (x2 > clip_x + clip_w)
1118                         {
1119                         y2 = y2 + slope * (clip_x + clip_w - x2);
1120                         x2 = clip_x + clip_w;
1121                         }
1122                 }
1123
1124         if (y1 < y2)
1125                 {
1126                 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
1127                 }
1128         else
1129                 {
1130                 gdouble t;
1131
1132                 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
1133
1134                 t = x1; x1 = x2; x2 = t;
1135                 t = y1; y1 = y2; y2 = t;
1136                 flip = !flip;
1137                 }
1138
1139         d = y2 - y1;
1140         if (d > 0.0)
1141                 {
1142                 gdouble slope;
1143
1144                 slope = (x2 - x1) / d;
1145                 if (y1 < clip_y)
1146                         {
1147                         x1 = x1 + slope * (clip_y - y1);
1148                         y1 = clip_y;
1149                         }
1150                 if (y2 > clip_y + clip_h)
1151                         {
1152                         x2 = x2 + slope * (clip_y + clip_h - y2);
1153                         y2 = clip_y + clip_h;
1154                         }
1155                 }
1156
1157         if (flip)
1158                 {
1159                 *rx1 = x2;
1160                 *ry1 = y2;
1161                 *rx2 = x1;
1162                 *ry2 = y1;
1163                 }
1164         else
1165                 {
1166                 *rx1 = x1;
1167                 *ry1 = y1;
1168                 *rx2 = x2;
1169                 *ry2 = y2;
1170                 }
1171
1172         return TRUE;
1173 }
1174
1175 void pixbuf_draw_line(GdkPixbuf *pb,
1176                       gint clip_x, gint clip_y, gint clip_w, gint clip_h,
1177                       gint x1, gint y1, gint x2, gint y2,
1178                       guint8 r, guint8 g, guint8 b, guint8 a)
1179 {
1180         gboolean has_alpha;
1181         gint pw, ph, prs;
1182         gint rx, ry, rw, rh;
1183         gdouble rx1, ry1, rx2, ry2;
1184         guchar *p_pix;
1185         guchar *pp;
1186         gint p_step;
1187         gdouble slope;
1188         gdouble x, y;
1189         gint px, py;
1190         gint cx1, cy1, cx2, cy2;
1191
1192         if (!pb) return;
1193
1194         pw = gdk_pixbuf_get_width(pb);
1195         ph = gdk_pixbuf_get_height(pb);
1196
1197         if (!util_clip_region(0, 0, pw, ph,
1198                               clip_x, clip_y, clip_w, clip_h,
1199                               &rx, &ry, &rw, &rh)) return;
1200         if (!util_clip_line(static_cast<gdouble>(rx), static_cast<gdouble>(ry), static_cast<gdouble>(rw), static_cast<gdouble>(rh),
1201                             static_cast<gdouble>(x1), static_cast<gdouble>(y1), static_cast<gdouble>(x2), static_cast<gdouble>(y2),
1202                             &rx1, &ry1, &rx2, &ry2)) return;
1203
1204         cx1 = rx;
1205         cy1 = ry;
1206         cx2 = rx + rw;
1207         cy2 = ry + rh;
1208
1209         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1210         prs = gdk_pixbuf_get_rowstride(pb);
1211         p_pix = gdk_pixbuf_get_pixels(pb);
1212
1213         p_step = (has_alpha) ? 4 : 3;
1214
1215         if (fabs(rx2 - rx1) > fabs(ry2 - ry1))
1216                 {
1217                 if (rx1 > rx2)
1218                         {
1219                         gdouble t;
1220                         t = rx1; rx1 = rx2; rx2 = t;
1221                         t = ry1; ry1 = ry2; ry2 = t;
1222                         }
1223
1224                 slope = rx2 - rx1;
1225                 if (slope != 0.0) slope = (ry2 - ry1) / slope;
1226                 for (x = rx1; x < rx2; x += 1.0)
1227                         {
1228                         px = static_cast<gint>(x + 0.5);
1229                         py = static_cast<gint>(ry1 + (x - rx1) * slope + 0.5);
1230
1231                         if (px >=  cx1 && px < cx2 && py >= cy1 && py < cy2)
1232                                 {
1233                                 pp = p_pix + py * prs + px * p_step;
1234                                 *pp = (r * a + *pp * (256-a)) >> 8;
1235                                 pp++;
1236                                 *pp = (g * a + *pp * (256-a)) >> 8;
1237                                 pp++;
1238                                 *pp = (b * a + *pp * (256-a)) >> 8;
1239                                 }
1240                         }
1241                 }
1242         else
1243                 {
1244                 if (ry1 > ry2)
1245                         {
1246                         gdouble t;
1247                         t = rx1; rx1 = rx2; rx2 = t;
1248                         t = ry1; ry1 = ry2; ry2 = t;
1249                         }
1250
1251                 slope = ry2 - ry1;
1252                 if (slope != 0.0) slope = (rx2 - rx1) / slope;
1253                 for (y = ry1; y < ry2; y += 1.0)
1254                         {
1255                         px = static_cast<gint>(rx1 + (y - ry1) * slope + 0.5);
1256                         py = static_cast<gint>(y + 0.5);
1257
1258                         if (px >=  cx1 && px < cx2 && py >= cy1 && py < cy2)
1259                                 {
1260                                 pp = p_pix + py * prs + px * p_step;
1261                                 *pp = (r * a + *pp * (256-a)) >> 8;
1262                                 pp++;
1263                                 *pp = (g * a + *pp * (256-a)) >> 8;
1264                                 pp++;
1265                                 *pp = (b * a + *pp * (256-a)) >> 8;
1266                                 }
1267                         }
1268                 }
1269 }
1270
1271 /*
1272  *-----------------------------------------------------------------------------
1273  * pixbuf drawing (fades and shadows)
1274  *-----------------------------------------------------------------------------
1275  */
1276
1277 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gboolean has_alpha,
1278                                     gint s, gboolean vertical, gint border,
1279                                     gint x1, gint y1, gint x2, gint y2,
1280                                     guint8 r, guint8 g, guint8 b, guint8 a)
1281 {
1282         guchar *pp;
1283         gint p_step;
1284         guint8 n = a;
1285         gint i, j;
1286
1287         p_step = (has_alpha) ? 4 : 3;
1288         for (j = y1; j < y2; j++)
1289                 {
1290                 pp = p_pix + j * prs + x1 * p_step;
1291                 if (!vertical) n = a - a * abs(j - s) / border;
1292                 for (i = x1; i < x2; i++)
1293                         {
1294                         if (vertical) n = a - a * abs(i - s) / border;
1295                         *pp = (r * n + *pp * (256-n)) >> 8;
1296                         pp++;
1297                         *pp = (g * n + *pp * (256-n)) >> 8;
1298                         pp++;
1299                         *pp = (b * n + *pp * (256-n)) >> 8;
1300                         pp++;
1301                         if (has_alpha) pp++;
1302                         }
1303                 }
1304 }
1305
1306 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gboolean has_alpha,
1307                                     gint sx, gint sy, gint border,
1308                                     gint x1, gint y1, gint x2, gint y2,
1309                                     guint8, guint8 g, guint8 b, guint8 a)
1310 {
1311         guchar *pp;
1312         gint p_step;
1313         gint i, j;
1314
1315         p_step = (has_alpha) ? 4 : 3;
1316         for (j = y1; j < y2; j++)
1317                 {
1318                 pp = p_pix + j * prs + x1 * p_step;
1319                 for (i = x1; i < x2; i++)
1320                         {
1321                         guint8 n;
1322                         gint r;
1323
1324                         r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
1325                         n = a - a * r / border;
1326                         *pp = (r * n + *pp * (256-n)) >> 8;
1327                         pp++;
1328                         *pp = (g * n + *pp * (256-n)) >> 8;
1329                         pp++;
1330                         *pp = (b * n + *pp * (256-n)) >> 8;
1331                         pp++;
1332                         if (has_alpha) pp++;
1333                         }
1334                 }
1335 }
1336
1337 void pixbuf_draw_shadow(GdkPixbuf *pb,
1338                         gint clip_x, gint clip_y, gint clip_w, gint clip_h,
1339                         gint x, gint y, gint w, gint h, gint border,
1340                         guint8 r, guint8 g, guint8 b, guint8 a)
1341 {
1342         gint has_alpha;
1343         gint pw, ph, prs;
1344         gint rx, ry, rw, rh;
1345         gint fx, fy, fw, fh;
1346         guchar *p_pix;
1347
1348         if (!pb) return;
1349
1350         pw = gdk_pixbuf_get_width(pb);
1351         ph = gdk_pixbuf_get_height(pb);
1352
1353         if (!util_clip_region(0, 0, pw, ph,
1354                               clip_x, clip_y, clip_w, clip_h,
1355                               &rx, &ry, &rw, &rh)) return;
1356
1357         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1358         prs = gdk_pixbuf_get_rowstride(pb);
1359         p_pix = gdk_pixbuf_get_pixels(pb);
1360
1361         if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
1362                              rx, ry, rw, rh,
1363                              &fx, &fy, &fw, &fh))
1364                 {
1365                 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
1366                 }
1367
1368         if (border < 1) return;
1369
1370         if (util_clip_region(x, y + border, border, h - border * 2,
1371                              rx, ry, rw, rh,
1372                              &fx, &fy, &fw, &fh))
1373                 {
1374                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1375                                         x + border, TRUE, border,
1376                                         fx, fy, fx + fw, fy + fh,
1377                                         r, g, b, a);
1378                 }
1379         if (util_clip_region(x + w - border, y + border, border, h - border * 2,
1380                              rx, ry, rw, rh,
1381                              &fx, &fy, &fw, &fh))
1382                 {
1383                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1384                                         x + w - border, TRUE, border,
1385                                         fx, fy, fx + fw, fy + fh,
1386                                         r, g, b, a);
1387                 }
1388         if (util_clip_region(x + border, y, w - border * 2, border,
1389                              rx, ry, rw, rh,
1390                              &fx, &fy, &fw, &fh))
1391                 {
1392                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1393                                         y + border, FALSE, border,
1394                                         fx, fy, fx + fw, fy + fh,
1395                                         r, g, b, a);
1396                 }
1397         if (util_clip_region(x + border, y + h - border, w - border * 2, border,
1398                              rx, ry, rw, rh,
1399                              &fx, &fy, &fw, &fh))
1400                 {
1401                 pixbuf_draw_fade_linear(p_pix, prs, has_alpha,
1402                                         y + h - border, FALSE, border,
1403                                         fx, fy, fx + fw, fy + fh,
1404                                         r, g, b, a);
1405                 }
1406         if (util_clip_region(x, y, border, border,
1407                              rx, ry, rw, rh,
1408                              &fx, &fy, &fw, &fh))
1409                 {
1410                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1411                                         x + border, y + border, border,
1412                                         fx, fy, fx + fw, fy + fh,
1413                                         r, g, b, a);
1414                 }
1415         if (util_clip_region(x + w - border, y, border, border,
1416                              rx, ry, rw, rh,
1417                              &fx, &fy, &fw, &fh))
1418                 {
1419                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1420                                         x + w - border, y + border, border,
1421                                         fx, fy, fx + fw, fy + fh,
1422                                         r, g, b, a);
1423                 }
1424         if (util_clip_region(x, y + h - border, border, border,
1425                              rx, ry, rw, rh,
1426                              &fx, &fy, &fw, &fh))
1427                 {
1428                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1429                                         x + border, y + h - border, border,
1430                                         fx, fy, fx + fw, fy + fh,
1431                                         r, g, b, a);
1432                 }
1433         if (util_clip_region(x + w - border, y + h - border, border, border,
1434                              rx, ry, rw, rh,
1435                              &fx, &fy, &fw, &fh))
1436                 {
1437                 pixbuf_draw_fade_radius(p_pix, prs, has_alpha,
1438                                         x + w - border, y + h - border, border,
1439                                         fx, fy, fx + fw, fy + fh,
1440                                         r, g, b, a);
1441                 }
1442 }
1443
1444
1445 /*
1446  *-----------------------------------------------------------------------------
1447  * pixbuf color alterations
1448  *-----------------------------------------------------------------------------
1449  */
1450
1451 void pixbuf_desaturate_rect(GdkPixbuf *pb,
1452                             gint x, gint y, gint w, gint h)
1453 {
1454         gboolean has_alpha;
1455         gint pw, ph, prs;
1456         guchar *p_pix;
1457         guchar *pp;
1458         gint i, j;
1459
1460         if (!pb) return;
1461
1462         pw = gdk_pixbuf_get_width(pb);
1463         ph = gdk_pixbuf_get_height(pb);
1464
1465         if (x < 0 || x + w > pw) return;
1466         if (y < 0 || y + h > ph) return;
1467
1468         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1469         prs = gdk_pixbuf_get_rowstride(pb);
1470         p_pix = gdk_pixbuf_get_pixels(pb);
1471
1472         for (i = 0; i < h; i++)
1473                 {
1474                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
1475                 for (j = 0; j < w; j++)
1476                         {
1477                         guint8 grey;
1478
1479                         grey = (pp[0] + pp[1] + pp[2]) / 3;
1480                         *pp = grey;
1481                         pp++;
1482                         *pp = grey;
1483                         pp++;
1484                         *pp = grey;
1485                         pp++;
1486                         if (has_alpha) pp++;
1487                         }
1488                 }
1489 }
1490
1491 /*
1492  *-----------------------------------------------------------------------------
1493  * pixbuf highlight under/over exposure *-----------------------------------------------------------------------------
1494  */
1495 void pixbuf_highlight_overunderexposed(GdkPixbuf *pb, gint x, gint y, gint w, gint h)
1496 {
1497         gboolean has_alpha;
1498         gint pw, ph, prs;
1499         guchar *p_pix;
1500         guchar *pp;
1501         gint i, j;
1502
1503         if (!pb) return;
1504
1505         pw = gdk_pixbuf_get_width(pb);
1506         ph = gdk_pixbuf_get_height(pb);
1507
1508         if (x < 0 || x + w > pw) return;
1509         if (y < 0 || y + h > ph) return;
1510
1511         has_alpha = gdk_pixbuf_get_has_alpha(pb);
1512         prs = gdk_pixbuf_get_rowstride(pb);
1513         p_pix = gdk_pixbuf_get_pixels(pb);
1514
1515         for (i = 0; i < h; i++)
1516                 {
1517                 pp = p_pix + (y + i) * prs + (x * (has_alpha ? 4 : 3));
1518                 for (j = 0; j < w; j++)
1519                         {
1520                         if (pp[0] == 255 || pp[1] == 255 || pp[2] == 255 || pp[0] == 0 || pp[1] == 0 || pp[2] == 0)
1521                                 {
1522                                 *pp = 255;
1523                                 pp++;
1524                                 *pp = 0;
1525                                 pp++;
1526                                 *pp = 0;
1527                                 pp++;
1528                                 if (has_alpha) pp++;
1529                                 }
1530                         else
1531                                 {
1532                                 pp = pp + 3;
1533                                 if (has_alpha) pp++;
1534                                 }
1535                         }
1536                 }
1537 }
1538
1539 /*
1540  *-----------------------------------------------------------------------------
1541  * pixbuf ignore alpha
1542  *-----------------------------------------------------------------------------
1543 */
1544 void pixbuf_ignore_alpha_rect(GdkPixbuf *pb,
1545                  gint x, gint y, gint w, gint h)
1546 {
1547    gboolean has_alpha;
1548    gint pw, ph, prs;
1549    guchar *p_pix;
1550    guchar *pp;
1551    gint i, j;
1552
1553    if (!pb) return;
1554
1555    pw = gdk_pixbuf_get_width(pb);
1556    ph = gdk_pixbuf_get_height(pb);
1557
1558    if (x < 0 || x + w > pw) return;
1559    if (y < 0 || y + h > ph) return;
1560
1561    has_alpha = gdk_pixbuf_get_has_alpha(pb);
1562    if (!has_alpha) return;
1563
1564    prs = gdk_pixbuf_get_rowstride(pb);
1565    p_pix = gdk_pixbuf_get_pixels(pb);
1566
1567    for (i = 0; i < h; i++)
1568        {
1569        pp = p_pix + (y + i) * prs + (x * 4 );
1570        for (j = 0; j < w; j++)
1571            {
1572            pp[3] = 0xff;
1573            pp+=4;
1574            }
1575        }
1576 }
1577 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */