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