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