Big whitespaces cleanup:
[geeqie.git] / src / pixbuf_util.c
1 /*
2  * Geeqie
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 "main.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", GQ_APPNAME " " 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,           geeqie_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         if (w < 1 || h < 1) return;
620
621         pixmap = gdk_pixmap_new(widget->window, w, h, -1);
622
623         gc = gdk_gc_new(widget->window);
624         gdk_gc_copy(gc, widget->style->black_gc);
625         gdk_draw_rectangle(pixmap, gc, TRUE, 0, 0, w, h);
626         gdk_gc_copy(gc, widget->style->white_gc);
627         gdk_draw_layout(pixmap, gc, 0, 0, layout);
628         g_object_unref(gc);
629
630         buffer = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, w, h);
631         gdk_pixbuf_get_from_drawable(buffer, pixmap,
632                                      gdk_drawable_get_colormap(widget->window),
633                                      0, 0, 0, 0, w, h);
634         g_object_unref(pixmap);
635
636         sx = 0;
637         sy = 0;
638         dw = gdk_pixbuf_get_width(pixbuf);
639         dh = gdk_pixbuf_get_height(pixbuf);
640
641         if (x < 0)
642                 {
643                 w += x;
644                 sx = -x;
645                 x = 0;
646                 }
647
648         if (y < 0)
649                 {
650                 h += y;
651                 sy = -y;
652                 y = 0;
653                 }
654
655         if (x + w > dw) w = dw - x;
656         if (y + h > dh) h = dh - y;
657
658         pixbuf_copy_font(buffer, sx, sy,
659                          pixbuf, x, y, w, h,
660                          r, g, b, a);
661
662         g_object_unref(buffer);
663 }
664
665 /*
666  *-----------------------------------------------------------------------------
667  * pixbuf drawing (triangle)
668  *-----------------------------------------------------------------------------
669  */
670
671 void util_clip_triangle(gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
672                         gint *rx, gint *ry, gint *rw, gint *rh)
673 {
674         gint tx, ty, tw, th;
675
676         tx = MIN(x1, x2);
677         tx = MIN(tx, x3);
678         ty = MIN(y1, y2);
679         ty = MIN(ty, y3);
680         tw = MAX(abs(x1 - x2), abs(x2 - x3));
681         tw = MAX(tw, abs(x3 - x1));
682         th = MAX(abs(y1 - y2), abs(y2 - y3));
683         th = MAX(th, abs(y3 - y1));
684
685         *rx = tx;
686         *ry = ty;
687         *rw = tw;
688         *rh = th;
689 }
690
691 void pixbuf_draw_triangle(GdkPixbuf *pb,
692                           gint clip_x, gint clip_y, gint clip_w, gint clip_h,
693                           gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
694                           guint8 r, guint8 g, guint8 b, guint8 a)
695 {
696         gint p_alpha;
697         gint pw, ph, prs;
698         gint rx, ry, rw, rh;
699         gint tx, ty, tw, th;
700         gint fx1, fy1;
701         gint fx2, fy2;
702         gint fw, fh;
703         guchar *p_pix;
704         guchar *pp;
705         gint p_step;
706         gdouble slope1, slope2;
707         gint slope1_x, slope1_y;
708         gint y;
709         gint t;
710         gint middle = FALSE;
711
712         if (!pb) return;
713
714         pw = gdk_pixbuf_get_width(pb);
715         ph = gdk_pixbuf_get_height(pb);
716
717         if (!util_clip_region(0, 0, pw, ph,
718                               clip_x, clip_y, clip_w, clip_h,
719                               &rx, &ry, &rw, &rh)) return;
720
721         util_clip_triangle(x1, y1, x2, y2, x3, y3,
722                            &tx, &ty, &tw, &th);
723
724         if (!util_clip_region(rx, ry, rw, rh,
725                               tx, ty, tw, th,
726                               &fx1, &fy1, &fw, &fh)) return;
727         fx2 = fx1 + fw;
728         fy2 = fy1 + fh;
729
730         p_alpha = gdk_pixbuf_get_has_alpha(pb);
731         prs = gdk_pixbuf_get_rowstride(pb);
732         p_pix = gdk_pixbuf_get_pixels(pb);
733
734         p_step = (p_alpha) ? 4 : 3;
735
736         if (y1 > y2)
737                 {
738                 t = x1; x1 = x2; x2 = t;
739                 t = y1; y1 = y2; y2 = t;
740                 }
741         if (y2 > y3)
742                 {
743                 t = x2; x2 = x3; x3 = t;
744                 t = y2; y2 = y3; y3 = t;
745                 }
746         if (y1 > y2)
747                 {
748                 t = x1; x1 = x2; x2 = t;
749                 t = y1; y1 = y2; y2 = t;
750                 }
751
752         slope1 = (gdouble)(y2 - y1);
753         if (slope1) slope1 = (gdouble)(x2 - x1) / slope1;
754         slope1_x = x1;
755         slope1_y = y1;
756         slope2 = (gdouble)(y3 - y1);
757         if (slope2) slope2 = (gdouble)(x3 - x1) / slope2;
758
759         for (y = fy1; y < fy2; y++)
760                 {
761                 gint xa, xb;
762
763                 if (!middle && y > y2)
764                         {
765                         slope1 = (gdouble)(y3 - y2);
766                         if (slope1) slope1 = (gdouble)(x3 - x2) / slope1;
767                         slope1_x = x2;
768                         slope1_y = y2;
769
770                         middle = TRUE;
771                         }
772
773                 xa = slope1_x + ((gdouble)slope1 * (y - slope1_y) + 0.5);
774                 xb = x1 + ((gdouble)slope2 * (y - y1) + 0.5);
775
776                 if (xa > xb)
777                         {
778                         t = xa; xa = xb; xb = t;
779                         }
780
781                 xa = CLAMP(xa, fx1, fx2);
782                 xb = CLAMP(xb, fx1, fx2);
783
784                 pp = p_pix + y * prs + xa * p_step;
785
786                 while (xa < xb)
787                         {
788                         *pp = (r * a + *pp * (256-a)) >> 8;
789                         pp++;
790                         *pp = (g * a + *pp * (256-a)) >> 8;
791                         pp++;
792                         *pp = (b * a + *pp * (256-a)) >> 8;
793                         pp++;
794                         if (p_alpha) pp++;
795
796                         xa++;
797                         }
798                 }
799 }
800
801 /*
802  *-----------------------------------------------------------------------------
803  * pixbuf drawing (line)
804  *-----------------------------------------------------------------------------
805  */
806
807 static gint util_clip_line(gdouble clip_x, gdouble clip_y, gdouble clip_w, gdouble clip_h,
808                            gdouble x1, gdouble y1, gdouble x2, gdouble y2,
809                            gdouble *rx1, gdouble *ry1, gdouble *rx2, gdouble *ry2)
810 {
811         gint flip = FALSE;
812         gdouble d;
813
814         if (x1 > x2)
815                 {
816                 gdouble t;
817
818                 t = x1; x1 = x2; x2 = t;
819                 t = y1; y1 = y2; y2 = t;
820                 flip = TRUE;
821                 }
822
823         if (x2 < clip_x || x1 > clip_x + clip_w) return FALSE;
824
825         if (y1 < y2)
826                 {
827                 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
828                 }
829         else
830                 {
831                 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
832                 }
833
834 #if 0
835         if (x1 >= clip_x && x2 <= clip_x + clip_w)
836                 {
837                 if (y1 < y2)
838                         {
839                         if (y1 >= clip_y && y2 <= clip_y + clip_h) return TRUE;
840                         }
841                 else
842                         {
843                         if (y2 >= clip_y && y1 <= clip_y + clip_h) return TRUE;
844                         }
845                 }
846 #endif
847
848         d = x2 - x1;
849         if (d > 0.0)
850                 {
851                 gdouble slope;
852
853                 slope = (y2 - y1) / d;
854                 if (x1 < clip_x)
855                         {
856                         y1 = y1 + slope * (clip_x - x1);
857                         x1 = clip_x;
858                         }
859                 if (x2 > clip_x + clip_w)
860                         {
861                         y2 = y2 + slope * (clip_x + clip_w - x2);
862                         x2 = clip_x + clip_w;
863                         }
864                 }
865
866         if (y1 < y2)
867                 {
868                 if (y2 < clip_y || y1 > clip_y + clip_h) return FALSE;
869                 }
870         else
871                 {
872                 gdouble t;
873
874                 if (y1 < clip_y || y2 > clip_y + clip_h) return FALSE;
875
876                 t = x1; x1 = x2; x2 = t;
877                 t = y1; y1 = y2; y2 = t;
878                 flip = !flip;
879                 }
880
881         d = y2 - y1;
882         if (d > 0.0)
883                 {
884                 gdouble slope;
885
886                 slope = (x2 - x1) / d;
887                 if (y1 < clip_y)
888                         {
889                         x1 = x1 + slope * (clip_y - y1);
890                         y1 = clip_y;
891                         }
892                 if (y2 > clip_y + clip_h)
893                         {
894                         x2 = x2 + slope * (clip_y + clip_h - y2);
895                         y2 = clip_y + clip_h;
896                         }
897                 }
898
899         if (flip)
900                 {
901                 *rx1 = x2;
902                 *ry1 = y2;
903                 *rx2 = x1;
904                 *ry2 = y1;
905                 }
906         else
907                 {
908                 *rx1 = x1;
909                 *ry1 = y1;
910                 *rx2 = x2;
911                 *ry2 = y2;
912                 }
913
914         return TRUE;
915 }
916
917 void pixbuf_draw_line(GdkPixbuf *pb,
918                       gint clip_x, gint clip_y, gint clip_w, gint clip_h,
919                       gint x1, gint y1, gint x2, gint y2,
920                       guint8 r, guint8 g, guint8 b, guint8 a)
921 {
922         gint p_alpha;
923         gint pw, ph, prs;
924         gint rx, ry, rw, rh;
925         gdouble rx1, ry1, rx2, ry2;
926         guchar *p_pix;
927         guchar *pp;
928         gint p_step;
929         gdouble slope;
930         gdouble x, y;
931         gint px, py;
932         gint cx1, cy1, cx2, cy2;
933
934         if (!pb) return;
935
936         pw = gdk_pixbuf_get_width(pb);
937         ph = gdk_pixbuf_get_height(pb);
938
939         if (!util_clip_region(0, 0, pw, ph,
940                               clip_x, clip_y, clip_w, clip_h,
941                               &rx, &ry, &rw, &rh)) return;
942         if (!util_clip_line((gdouble)rx, (gdouble)ry, (gdouble)rw, (gdouble)rh,
943                             (gdouble)x1, (gdouble)y1, (gdouble)x2, (gdouble)y2,
944                             &rx1, &ry1, &rx2, &ry2)) return;
945
946         cx1 = rx;
947         cy1 = ry;
948         cx2 = rx + rw;
949         cy2 = ry + rh;
950
951         p_alpha = gdk_pixbuf_get_has_alpha(pb);
952         prs = gdk_pixbuf_get_rowstride(pb);
953         p_pix = gdk_pixbuf_get_pixels(pb);
954
955         p_step = (p_alpha) ? 4 : 3;
956
957         if (fabs(rx2 - rx1) > fabs(ry2 - ry1))
958                 {
959                 if (rx1 > rx2)
960                         {
961                         gdouble t;
962                         t = rx1; rx1 = rx2; rx2 = t;
963                         t = ry1; ry1 = ry2; ry2 = t;
964                         }
965
966                 slope = rx2 - rx1;
967                 if (slope != 0.0) slope = (ry2 - ry1) / slope;
968                 for (x = rx1; x < rx2; x += 1.0)
969                         {
970                         px = (gint)(x + 0.5);
971                         py = (gint)(ry1 + (x - rx1) * slope + 0.5);
972
973                         if (px >=  cx1 && px < cx2 && py >= cy1 && py < cy2)
974                                 {
975                                 pp = p_pix + py * prs + px * p_step;
976                                 *pp = (r * a + *pp * (256-a)) >> 8;
977                                 pp++;
978                                 *pp = (g * a + *pp * (256-a)) >> 8;
979                                 pp++;
980                                 *pp = (b * a + *pp * (256-a)) >> 8;
981                                 }
982                         }
983                 }
984         else
985                 {
986                 if (ry1 > ry2)
987                         {
988                         gdouble t;
989                         t = rx1; rx1 = rx2; rx2 = t;
990                         t = ry1; ry1 = ry2; ry2 = t;
991                         }
992
993                 slope = ry2 - ry1;
994                 if (slope != 0.0) slope = (rx2 - rx1) / slope;
995                 for (y = ry1; y < ry2; y += 1.0)
996                         {
997                         px = (gint)(rx1 + (y - ry1) * slope + 0.5);
998                         py = (gint)(y + 0.5);
999
1000                         if (px >=  cx1 && px < cx2 && py >= cy1 && py < cy2)
1001                                 {
1002                                 pp = p_pix + py * prs + px * p_step;
1003                                 *pp = (r * a + *pp * (256-a)) >> 8;
1004                                 pp++;
1005                                 *pp = (g * a + *pp * (256-a)) >> 8;
1006                                 pp++;
1007                                 *pp = (b * a + *pp * (256-a)) >> 8;
1008                                 }
1009                         }
1010                 }
1011 }
1012
1013 /*
1014  *-----------------------------------------------------------------------------
1015  * pixbuf drawing (fades and shadows)
1016  *-----------------------------------------------------------------------------
1017  */
1018
1019 static void pixbuf_draw_fade_linear(guchar *p_pix, gint prs, gint p_alpha,
1020                                     gint s, gint vertical, gint border,
1021                                     gint x1, gint y1, gint x2, gint y2,
1022                                     guint8 r, guint8 g, guint8 b, guint8 a)
1023 {
1024         guchar *pp;
1025         gint p_step;
1026         guint8 n = a;
1027         gint i, j;
1028
1029         p_step = (p_alpha) ? 4 : 3;
1030         for (j = y1; j < y2; j++)
1031                 {
1032                 pp = p_pix + j * prs + x1 * p_step;
1033                 if (!vertical) n = a - a * abs(j - s) / border;
1034                 for (i = x1; i < x2; i++)
1035                         {
1036                         if (vertical) n = a - a * abs(i - s) / border;
1037                         *pp = (r * n + *pp * (256-n)) >> 8;
1038                         pp++;
1039                         *pp = (g * n + *pp * (256-n)) >> 8;
1040                         pp++;
1041                         *pp = (b * n + *pp * (256-n)) >> 8;
1042                         pp++;
1043                         if (p_alpha) pp++;
1044                         }
1045                 }
1046 }
1047
1048 static void pixbuf_draw_fade_radius(guchar *p_pix, gint prs, gint p_alpha,
1049                                     gint sx, gint sy, gint border,
1050                                     gint x1, gint y1, gint x2, gint y2,
1051                                     guint8 r, guint8 g, guint8 b, guint8 a)
1052 {
1053         guchar *pp;
1054         gint p_step;
1055         gint i, j;
1056
1057         p_step = (p_alpha) ? 4 : 3;
1058         for (j = y1; j < y2; j++)
1059                 {
1060                 pp = p_pix + j * prs + x1 * p_step;
1061                 for (i = x1; i < x2; i++)
1062                         {
1063                         guint8 n;
1064                         gint r;
1065
1066                         r = MIN(border, (gint)sqrt((i-sx)*(i-sx) + (j-sy)*(j-sy)));
1067                         n = a - a * r / border;
1068                         *pp = (r * n + *pp * (256-n)) >> 8;
1069                         pp++;
1070                         *pp = (g * n + *pp * (256-n)) >> 8;
1071                         pp++;
1072                         *pp = (b * n + *pp * (256-n)) >> 8;
1073                         pp++;
1074                         if (p_alpha) pp++;
1075                         }
1076                 }
1077 }
1078
1079 void pixbuf_draw_shadow(GdkPixbuf *pb,
1080                         gint clip_x, gint clip_y, gint clip_w, gint clip_h,
1081                         gint x, gint y, gint w, gint h, gint border,
1082                         guint8 r, guint8 g, guint8 b, guint8 a)
1083 {
1084         gint p_alpha;
1085         gint pw, ph, prs;
1086         gint rx, ry, rw, rh;
1087         gint fx, fy, fw, fh;
1088         guchar *p_pix;
1089
1090         if (!pb) return;
1091
1092         pw = gdk_pixbuf_get_width(pb);
1093         ph = gdk_pixbuf_get_height(pb);
1094
1095         if (!util_clip_region(0, 0, pw, ph,
1096                               clip_x, clip_y, clip_w, clip_h,
1097                               &rx, &ry, &rw, &rh)) return;
1098
1099         p_alpha = gdk_pixbuf_get_has_alpha(pb);
1100         prs = gdk_pixbuf_get_rowstride(pb);
1101         p_pix = gdk_pixbuf_get_pixels(pb);
1102
1103         if (util_clip_region(x + border, y + border, w - border * 2, h - border * 2,
1104                              rx, ry, rw, rh,
1105                              &fx, &fy, &fw, &fh))
1106                 {
1107                 pixbuf_draw_rect_fill(pb, fx, fy, fw, fh, r, g, b, a);
1108                 }
1109
1110         if (border < 1) return;
1111
1112         if (util_clip_region(x, y + border, border, h - border * 2,
1113                              rx, ry, rw, rh,
1114                              &fx, &fy, &fw, &fh))
1115                 {
1116                 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
1117                                         x + border, TRUE, border,
1118                                         fx, fy, fx + fw, fy + fh,
1119                                         r, g, b, a);
1120                 }
1121         if (util_clip_region(x + w - border, y + border, border, h - border * 2,
1122                              rx, ry, rw, rh,
1123                              &fx, &fy, &fw, &fh))
1124                 {
1125                 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
1126                                         x + w - border, TRUE, border,
1127                                         fx, fy, fx + fw, fy + fh,
1128                                         r, g, b, a);
1129                 }
1130         if (util_clip_region(x + border, y, w - border * 2, border,
1131                              rx, ry, rw, rh,
1132                              &fx, &fy, &fw, &fh))
1133                 {
1134                 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
1135                                         y + border, FALSE, border,
1136                                         fx, fy, fx + fw, fy + fh,
1137                                         r, g, b, a);
1138                 }
1139         if (util_clip_region(x + border, y + h - border, w - border * 2, border,
1140                              rx, ry, rw, rh,
1141                              &fx, &fy, &fw, &fh))
1142                 {
1143                 pixbuf_draw_fade_linear(p_pix, prs, p_alpha,
1144                                         y + h - border, FALSE, border,
1145                                         fx, fy, fx + fw, fy + fh,
1146                                         r, g, b, a);
1147                 }
1148         if (util_clip_region(x, y, border, border,
1149                              rx, ry, rw, rh,
1150                              &fx, &fy, &fw, &fh))
1151                 {
1152                 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
1153                                         x + border, y + border, border,
1154                                         fx, fy, fx + fw, fy + fh,
1155                                         r, g, b, a);
1156                 }
1157         if (util_clip_region(x + w - border, y, border, border,
1158                              rx, ry, rw, rh,
1159                              &fx, &fy, &fw, &fh))
1160                 {
1161                 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
1162                                         x + w - border, y + border, border,
1163                                         fx, fy, fx + fw, fy + fh,
1164                                         r, g, b, a);
1165                 }
1166         if (util_clip_region(x, y + h - border, border, border,
1167                              rx, ry, rw, rh,
1168                              &fx, &fy, &fw, &fh))
1169                 {
1170                 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
1171                                         x + border, y + h - border, border,
1172                                         fx, fy, fx + fw, fy + fh,
1173                                         r, g, b, a);
1174                 }
1175         if (util_clip_region(x + w - border, y + h - border, border, border,
1176                              rx, ry, rw, rh,
1177                              &fx, &fy, &fw, &fh))
1178                 {
1179                 pixbuf_draw_fade_radius(p_pix, prs, p_alpha,
1180                                         x + w - border, y + h - border, border,
1181                                         fx, fy, fx + fw, fy + fh,
1182                                         r, g, b, a);
1183                 }
1184 }
1185
1186
1187 /*
1188  *-----------------------------------------------------------------------------
1189  * pixbuf color alterations
1190  *-----------------------------------------------------------------------------
1191  */
1192
1193 void pixbuf_desaturate_rect(GdkPixbuf *pb,
1194                             gint x, gint y, gint w, gint h)
1195 {
1196         gint p_alpha;
1197         gint pw, ph, prs;
1198         guchar *p_pix;
1199         guchar *pp;
1200         gint i, j;
1201
1202         if (!pb) return;
1203
1204         pw = gdk_pixbuf_get_width(pb);
1205         ph = gdk_pixbuf_get_height(pb);
1206
1207         if (x < 0 || x + w > pw) return;
1208         if (y < 0 || y + h > ph) return;
1209
1210         p_alpha = gdk_pixbuf_get_has_alpha(pb);
1211         prs = gdk_pixbuf_get_rowstride(pb);
1212         p_pix = gdk_pixbuf_get_pixels(pb);
1213
1214         for (i = 0; i < h; i++)
1215                 {
1216                 pp = p_pix + (y + i) * prs + (x * (p_alpha ? 4 : 3));
1217                 for (j = 0; j < w; j++)
1218                         {
1219                         guint8 grey;
1220
1221                         grey = (pp[0] + pp[1] + pp[2]) / 3;
1222                         *pp = grey;
1223                         pp++;
1224                         *pp = grey;
1225                         pp++;
1226                         *pp = grey;
1227                         pp++;
1228                         if (p_alpha) pp++;
1229                         }
1230                 }
1231 }
1232
1233