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