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