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