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