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