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