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