Sync to GQview 1.5.9 release.
[geeqie.git] / src / pixbuf_util.c
1 /*
2  * GQview
3  * (C) 2004 John Ellis
4  *
5  * Author: John Ellis
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12
13 #include "gqview.h"
14 #include "pixbuf_util.h"
15
16 #include "icons/icons_inline.h"
17
18
19 /*
20  *-----------------------------------------------------------------------------
21  * png save
22  *-----------------------------------------------------------------------------
23  */
24
25 gboolean pixbuf_to_file_as_png (GdkPixbuf *pixbuf, const char *filename)
26 {
27         GError *error = NULL;
28         gint ret;
29
30         if (!pixbuf || !filename) return FALSE;
31
32         ret = gdk_pixbuf_save(pixbuf, filename, "png", &error,
33                               "tEXt::Software", "GQview "VERSION, NULL);
34
35         if (error)
36                 {
37                 printf("Error saving png file: %s\n", error->message);
38                 g_error_free(error);
39                 }
40
41         return ret;
42 }
43
44 /*
45  *-----------------------------------------------------------------------------
46  * jpeg save
47  *-----------------------------------------------------------------------------
48  */
49
50 gboolean pixbuf_to_file_as_jpg(GdkPixbuf *pixbuf, const gchar *filename, gint quality)
51 {
52         GError *error = NULL;
53         gchar *qbuf;
54         gboolean ret;
55
56         if (!pixbuf || !filename) return FALSE;
57
58         if (quality == -1) quality = 75;
59         if (quality < 1 || quality > 100)
60                 {
61                 printf("Jpeg not saved, invalid quality %d\n", quality);
62                 return FALSE;
63                 }
64
65         qbuf = g_strdup_printf("%d", quality);
66         ret = gdk_pixbuf_save(pixbuf, filename, "jpeg", &error, "quality", qbuf, NULL);
67         g_free(qbuf);
68
69         if (error)
70                 {
71                 printf("Error saving jpeg to %s\n%s\n", filename, error->message);
72                 g_error_free(error);
73                 }
74
75         return ret;
76 }
77
78 /*
79  *-----------------------------------------------------------------------------
80  * pixbuf from inline
81  *-----------------------------------------------------------------------------
82  */
83
84 typedef struct _PixbufInline PixbufInline;
85 struct _PixbufInline
86 {
87         const gchar *key;
88         const guint8 *data;
89 };
90
91 static PixbufInline inline_pixbuf_data[] = {
92         { PIXBUF_INLINE_FOLDER_CLOSED,  folder_closed },
93         { PIXBUF_INLINE_FOLDER_LOCKED,  folder_locked },
94         { PIXBUF_INLINE_FOLDER_OPEN,    folder_open },
95         { PIXBUF_INLINE_FOLDER_UP,      folder_up },
96         { PIXBUF_INLINE_SCROLLER,       icon_scroller },
97         { PIXBUF_INLINE_BROKEN,         icon_broken },
98         { PIXBUF_INLINE_LOGO,           gqview_logo },
99         { NULL, NULL }
100 };
101
102 GdkPixbuf *pixbuf_inline(const gchar *key)
103 {
104         gint i;
105
106         if (!key) return NULL;
107
108         i = 0;
109         while (inline_pixbuf_data[i].key)
110                 {
111                 if (strcmp(inline_pixbuf_data[i].key, key) == 0)
112                         {
113                         return gdk_pixbuf_new_from_inline(-1, inline_pixbuf_data[i].data, FALSE, NULL);
114                         }
115                 i++;
116                 }
117
118         printf("warning: inline pixbuf key \"%s\" not found.\n", key);
119
120         return NULL;
121 }
122
123 /*
124  *-----------------------------------------------------------------------------
125  * pixbuf rotation
126  *-----------------------------------------------------------------------------
127  */
128
129 static void pixbuf_copy_block_rotate(guchar *src, gint src_row_stride, gint x, gint y,
130                                      guchar *dest, gint dest_row_stride, gint w, gint h,
131                                      gint bytes_per_pixel, gint counter_clockwise)
132 {
133         gint i, j;
134         guchar *sp;
135         guchar *dp;
136
137         for (i = 0; i < h; i++)
138                 {
139                 sp = src + ((i + y) * src_row_stride) + (x * bytes_per_pixel);
140                 for (j = 0; j < w; j++)
141                         {
142                         if (counter_clockwise)
143                                 {
144                                 dp = dest + ((w - j - 1) * dest_row_stride) + (i * bytes_per_pixel);
145                                 }
146                         else
147                                 {
148                                 dp = dest + (j * dest_row_stride) + ((h - i - 1) * bytes_per_pixel);
149                                 }
150                         *(dp++) = *(sp++);      /* r */
151                         *(dp++) = *(sp++);      /* g */
152                         *(dp++) = *(sp++);      /* b */
153                         if (bytes_per_pixel == 4) *(dp) = *(sp++);      /* a */
154                         }
155                 }
156         
157 }
158
159 static void pixbuf_copy_block(guchar *src, gint src_row_stride, gint w, gint h,
160                               guchar *dest, gint dest_row_stride, gint x, gint y, gint bytes_per_pixel)
161 {
162         gint i;
163         guchar *sp;
164         guchar *dp;
165
166         for (i = 0; i < h; i++)
167                 {
168                 sp = src + (i * src_row_stride);
169                 dp = dest + ((y + i) * dest_row_stride) + (x * bytes_per_pixel);
170                 memcpy(dp, sp, w * bytes_per_pixel);
171                 }
172 }
173
174 #define ROTATE_BUFFER_WIDTH 48
175 #define ROTATE_BUFFER_HEIGHT 48
176
177 /*
178  * Returns a copy of pixbuf src rotated 90 degrees clockwise or 90 counterclockwise
179  *
180  */
181 GdkPixbuf *pixbuf_copy_rotate_90(GdkPixbuf *src, gint counter_clockwise)
182 {
183         GdkPixbuf *dest;
184         gint has_alpha;
185         gint sw, sh, srs;
186         gint dw, dh, drs;
187         guchar *s_pix;
188         guchar *d_pix;
189 #if 0
190         guchar *sp;
191         guchar *dp;
192 #endif
193         gint i, j;
194         gint a;
195         GdkPixbuf *buffer;
196         guchar *b_pix;
197         gint brs;
198         gint w, h;
199
200         if (!src) return NULL;
201
202         sw = gdk_pixbuf_get_width(src);
203         sh = gdk_pixbuf_get_height(src);
204         has_alpha = gdk_pixbuf_get_has_alpha(src);
205         srs = gdk_pixbuf_get_rowstride(src);
206         s_pix = gdk_pixbuf_get_pixels(src);
207
208         dw = sh;
209         dh = sw;
210         dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, dw, dh);
211         drs = gdk_pixbuf_get_rowstride(dest);
212         d_pix = gdk_pixbuf_get_pixels(dest);
213
214         a = (has_alpha ? 4 : 3);
215
216         buffer = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8,
217                                 ROTATE_BUFFER_WIDTH, ROTATE_BUFFER_HEIGHT);
218         b_pix = gdk_pixbuf_get_pixels(buffer);
219         brs = gdk_pixbuf_get_rowstride(buffer);
220
221         for (i = 0; i < sh; i+= ROTATE_BUFFER_WIDTH)
222                 {
223                 w = MIN(ROTATE_BUFFER_WIDTH, (sh - i));
224                 for (j = 0; j < sw; j += ROTATE_BUFFER_HEIGHT)
225                         {
226                         gint x, y;
227
228                         h = MIN(ROTATE_BUFFER_HEIGHT, (sw - j));
229                         pixbuf_copy_block_rotate(s_pix, srs, j, i,
230                                                  b_pix, brs, h, w,
231                                                  a, counter_clockwise);
232
233                         if (counter_clockwise)
234                                 {
235                                 x = i;
236                                 y = sw - h - j;
237                                 }
238                         else
239                                 {
240                                 x = sh - w - i;
241                                 y = j;
242                                 }
243                         pixbuf_copy_block(b_pix, brs, w, h,
244                                           d_pix, drs, x, y, a);
245                         }
246                 }
247
248         gdk_pixbuf_unref(buffer);
249
250 #if 0
251         /* this is the simple version of rotation (roughly 2-4x slower) */
252
253         for (i = 0; i < sh; i++)
254                 {
255                 sp = s_pix + (i * srs);
256                 for (j = 0; j < sw; j++)
257                         {
258                         if (counter_clockwise)
259                                 {
260                                 dp = d_pix + ((dh - j - 1) * drs) + (i * a);
261                                 }
262                         else
263                                 {
264                                 dp = d_pix + (j * drs) + ((dw - i - 1) * a);
265                                 }
266
267                         *(dp++) = *(sp++);      /* r */
268                         *(dp++) = *(sp++);      /* g */
269                         *(dp++) = *(sp++);      /* b */
270                         if (has_alpha) *(dp) = *(sp++); /* a */
271                         }
272                 }
273 #endif
274
275         return dest;
276 }
277
278 /*
279  * Returns a copy of pixbuf mirrored and or flipped.
280  * TO do a 180 degree rotations set both mirror and flipped TRUE
281  * if mirror and flip are FALSE, result is a simple copy.
282  */
283 GdkPixbuf *pixbuf_copy_mirror(GdkPixbuf *src, gint mirror, gint flip)
284 {
285         GdkPixbuf *dest;
286         gint has_alpha;
287         gint w, h, srs;
288         gint drs;
289         guchar *s_pix;
290         guchar *d_pix;
291         guchar *sp;
292         guchar *dp;
293         gint i, j;
294         gint a;
295
296         if (!src) return NULL;
297
298         w = gdk_pixbuf_get_width(src);
299         h = gdk_pixbuf_get_height(src);
300         has_alpha = gdk_pixbuf_get_has_alpha(src);
301         srs = gdk_pixbuf_get_rowstride(src);
302         s_pix = gdk_pixbuf_get_pixels(src);
303
304         dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, w, h);
305         drs = gdk_pixbuf_get_rowstride(dest);
306         d_pix = gdk_pixbuf_get_pixels(dest);
307
308         a = has_alpha ? 4 : 3;
309
310         for (i = 0; i < h; i++)
311                 {
312                 sp = s_pix + (i * srs);
313                 if (flip)
314                         {
315                         dp = d_pix + ((h - i - 1) * drs);
316                         }
317                 else
318                         {
319                         dp = d_pix + (i * drs);
320                         }
321                 if (mirror)
322                         {
323                         dp += (w - 1) * a;
324                         for (j = 0; j < w; j++)
325                                 {
326                                 *(dp++) = *(sp++);      /* r */
327                                 *(dp++) = *(sp++);      /* g */
328                                 *(dp++) = *(sp++);      /* b */
329                                 if (has_alpha) *(dp) = *(sp++); /* a */
330                                 dp -= (a + 3);
331                                 }
332                         }
333                 else
334                         {
335                         for (j = 0; j < w; j++)
336                                 {
337                                 *(dp++) = *(sp++);      /* r */
338                                 *(dp++) = *(sp++);      /* g */
339                                 *(dp++) = *(sp++);      /* b */
340                                 if (has_alpha) *(dp++) = *(sp++);       /* a */
341                                 }
342                         }
343                 }
344
345         return dest;
346 }
347
348
349 /*
350  *-----------------------------------------------------------------------------
351  * pixbuf drawing
352  *-----------------------------------------------------------------------------
353  */
354
355 /*
356  * Fills region of pixbuf at x,y over w,h
357  * with colors red (r), green (g), blue (b)
358  * applying alpha (a), use a=255 for solid.
359  */
360 void pixbuf_draw_rect_fill(GdkPixbuf *pb,
361                            gint x, gint y, gint w, gint h,
362                            gint r, gint g, gint b, gint a)
363 {
364         gint p_alpha;
365         gint pw, ph, prs;
366         guchar *p_pix;
367         guchar *pp;
368         gint i, j;
369
370         if (!pb) return;
371
372         pw = gdk_pixbuf_get_width(pb);
373         ph = gdk_pixbuf_get_height(pb);
374
375         if (x < 0 || x + w > pw) return;
376         if (y < 0 || y + h > ph) return;
377
378         p_alpha = gdk_pixbuf_get_has_alpha(pb);
379         prs = gdk_pixbuf_get_rowstride(pb);
380         p_pix = gdk_pixbuf_get_pixels(pb);
381
382         for (i = 0; i < h; i++)
383                 {
384                 pp = p_pix + (y + i) * prs + (x * (p_alpha ? 4 : 3));
385                 for (j = 0; j < w; j++)
386                         {
387                         *pp = (r * a + *pp * (256-a)) >> 8;
388                         pp++;
389                         *pp = (g * a + *pp * (256-a)) >> 8;
390                         pp++;
391                         *pp = (b * a + *pp * (256-a)) >> 8;
392                         pp++;
393                         if (p_alpha) pp++;
394                         }
395                 }
396 }
397
398 void pixbuf_draw_rect(GdkPixbuf *pb,
399                       gint x, gint y, gint w, gint h,
400                       gint r, gint g, gint b, gint a,
401                       gint left, gint right, gint top, gint bottom)
402 {
403         pixbuf_draw_rect_fill(pb, x + left, y, w - left - right, top,
404                               r, g, b ,a);
405         pixbuf_draw_rect_fill(pb, x + w - right, y, right, h,
406                               r, g, b ,a);
407         pixbuf_draw_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
408                               r, g, b ,a);
409         pixbuf_draw_rect_fill(pb, x, y, left, h,
410                               r, g, b ,a);
411 }
412
413 void pixbuf_set_rect_fill(GdkPixbuf *pb,
414                           gint x, gint y, gint w, gint h,
415                           gint r, gint g, gint b, gint a)
416 {
417         gint p_alpha;
418         gint pw, ph, prs;
419         guchar *p_pix;
420         guchar *pp;
421         gint i, j;
422
423         if (!pb) return;
424
425         pw = gdk_pixbuf_get_width(pb);
426         ph = gdk_pixbuf_get_height(pb);
427
428         if (x < 0 || x + w > pw) return;
429         if (y < 0 || y + h > ph) return;
430
431         p_alpha = gdk_pixbuf_get_has_alpha(pb);
432         prs = gdk_pixbuf_get_rowstride(pb);
433         p_pix = gdk_pixbuf_get_pixels(pb);
434
435         for (i = 0; i < h; i++)
436                 {
437                 pp = p_pix + (y + i) * prs + (x * (p_alpha ? 4 : 3));
438                 for (j = 0; j < w; j++)
439                         {
440                         *pp = r; pp++;
441                         *pp = g; pp++;
442                         *pp = b; pp++;
443                         if (p_alpha) { *pp = a; pp++; }
444                         }
445                 }
446 }
447
448 void pixbuf_set_rect(GdkPixbuf *pb,
449                      gint x, gint y, gint w, gint h,
450                      gint r, gint g, gint b, gint a,
451                      gint left, gint right, gint top, gint bottom)
452 {
453         pixbuf_set_rect_fill(pb, x + left, y, w - left - right, top,
454                              r, g, b ,a);
455         pixbuf_set_rect_fill(pb, x + w - right, y, right, h,
456                              r, g, b ,a);
457         pixbuf_set_rect_fill(pb, x + left, y + h - bottom, w - left - right, bottom,
458                              r, g, b ,a);
459         pixbuf_set_rect_fill(pb, x, y, left, h,
460                              r, g, b ,a);
461 }
462
463 void pixbuf_pixel_set(GdkPixbuf *pb, gint x, gint y, gint r, gint g, gint b, gint a)
464 {
465         guchar *buf;
466         gint has_alpha;
467         gint rowstride;
468         guchar *p;
469
470         if (x < 0 || x >= gdk_pixbuf_get_width(pb) ||
471             y < 0 || y >= gdk_pixbuf_get_height(pb)) return;
472
473         buf = gdk_pixbuf_get_pixels(pb);
474         has_alpha = gdk_pixbuf_get_has_alpha(pb);
475         rowstride = gdk_pixbuf_get_rowstride(pb);
476
477         p = buf + (y * rowstride) + (x * (has_alpha ? 4 : 3));
478         *p = r; p++;
479         *p = g; p++;
480         *p = b; p++;
481         if (has_alpha) *p = a;
482 }
483
484
485 /*
486  *-----------------------------------------------------------------------------
487  * pixbuf text rendering
488  *-----------------------------------------------------------------------------
489  */
490
491 static void pixbuf_copy_font(GdkPixbuf *src, gint sx, gint sy,
492                              GdkPixbuf *dest, gint dx, gint dy,
493                              gint w, gint h,
494                              guint8 r, guint8 g, guint8 b, guint8 a)
495 {
496         gint sw, sh, srs;
497         gint s_alpha;
498         gint s_step;
499         guchar *s_pix;
500         gint dw, dh, drs;
501         gint d_alpha;
502         gint d_step;
503         guchar *d_pix;
504
505         guchar *sp;
506         guchar *dp;
507         gint i, j;
508
509         if (!src || !dest) return;
510
511         sw = gdk_pixbuf_get_width(src);
512         sh = gdk_pixbuf_get_height(src);
513
514         if (sx < 0 || sx + w > sw) return;
515         if (sy < 0 || sy + h > sh) return;
516
517         dw = gdk_pixbuf_get_width(dest);
518         dh = gdk_pixbuf_get_height(dest);
519
520         if (dx < 0 || dx + w > dw) return;
521         if (dy < 0 || dy + h > dh) return;
522
523         s_alpha = gdk_pixbuf_get_has_alpha(src);
524         d_alpha = gdk_pixbuf_get_has_alpha(dest);
525         srs = gdk_pixbuf_get_rowstride(src);
526         drs = gdk_pixbuf_get_rowstride(dest);
527         s_pix = gdk_pixbuf_get_pixels(src);
528         d_pix = gdk_pixbuf_get_pixels(dest);
529
530         s_step = (s_alpha) ? 4 : 3;
531         d_step = (d_alpha) ? 4 : 3;
532
533         for (i = 0; i < h; i++)
534                 {
535                 sp = s_pix + (sy + i) * srs + sx * s_step;
536                 dp = d_pix + (dy + i) * drs + dx * d_step;
537                 for (j = 0; j < w; j++)
538                         {
539                         if (*sp)
540                                 {
541                                 guint8 asub;
542
543                                 asub = a * sp[0] / 255;
544                                 *dp = (r * asub + *dp * (256-asub)) >> 8;
545                                 dp++;
546                                 asub = a * sp[1] / 255;
547                                 *dp = (g * asub + *dp * (256-asub)) >> 8;
548                                 dp++;
549                                 asub = a * sp[2] / 255;
550                                 *dp = (b * asub + *dp * (256-asub)) >> 8;
551                                 dp++;
552
553                                 if (d_alpha)
554                                         {
555                                         *dp = MAX(*dp, a * ((sp[0] + sp[1] + sp[2]) / 3) / 255);
556                                         dp++;
557                                         }
558                                 }
559                         else
560                                 {
561                                 dp += d_step;
562                                 }
563
564                         sp += s_step;
565                         }
566                 }
567 }
568
569 void pixbuf_draw_layout(GdkPixbuf *pixbuf, PangoLayout *layout, GtkWidget *widget,
570                         gint x, gint y,
571                         guint8 r, guint8 g, guint8 b, guint8 a)
572 {
573         GdkPixmap *pixmap;
574         GdkPixbuf *buffer;
575         gint w, h;
576         GdkGC *gc;
577         gint sx, sy;
578         gint dw, dh;
579
580         if (!widget || !widget->window) return;
581
582         pango_layout_get_pixel_size(layout, &w, &h);
583         pixmap = gdk_pixmap_new(widget->window, w, h, -1);
584
585         gc = gdk_gc_new(widget->window);
586         gdk_gc_copy(gc, widget->style->black_gc);
587         gdk_draw_rectangle(pixmap, gc, TRUE, 0, 0, w, h);
588         gdk_gc_copy(gc, widget->style->white_gc);
589         gdk_draw_layout(pixmap, gc, 0, 0, layout);
590         g_object_unref(gc);
591
592         buffer = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, w, h);
593         gdk_pixbuf_get_from_drawable(buffer, pixmap,
594                                      gdk_drawable_get_colormap(widget->window),
595                                      0, 0, 0, 0, w, h);
596         g_object_unref(pixmap);
597
598         sx = 0;
599         sy = 0;
600         dw = gdk_pixbuf_get_width(pixbuf);
601         dh = gdk_pixbuf_get_height(pixbuf);
602
603         if (x < 0)
604                 {
605                 w += x;
606                 sx = -x;
607                 x = 0;
608                 }
609
610         if (y < 0)
611                 {
612                 h += y;
613                 sy = -y;
614                 y = 0;
615                 }
616
617         if (x + w > dw) w = dw - x;
618         if (y + h > dh) h = dh - y;
619
620         pixbuf_copy_font(buffer, 0, 0,
621                          pixbuf, x, y, w, h,
622                          r, g, b, a);
623
624         g_object_unref(buffer);
625 }
626