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