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