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