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