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