746f9e95cf3ba23433295a3168662abd27418aae
[booh] / ext / rbbooh.c
1 /*
2  *                         *  BOOH  *
3  *
4  * A.k.a `Best web-album Of the world, Or your money back, Humerus'.
5  *
6  * The acronyn sucks, however this is a tribute to Dragon Ball by
7  * Akira Toriyama, where the last enemy beaten by heroes of Dragon
8  * Ball is named "Boo". But there was already a free software project
9  * called Boo, so this one will be it "Booh". Or whatever.
10  *
11  *
12  * Copyright (c) 2005 Guillaume Cottenceau
13  *
14  * This software may be freely redistributed under the terms of the GNU
15  * public license version 2.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22
23 #include <math.h>
24
25 #define GDK_PIXBUF_ENABLE_BACKEND
26 #include <gtk/gtk.h>
27 #include "rbgobject.h"
28
29 #define _SELF(s) GDK_PIXBUF(RVAL2GOBJ(s)) 
30
31 #include <libexif/exif-data.h>
32
33 static VALUE whitebalance(VALUE self, VALUE level) {
34         double red_filter[256], blue_filter[256];
35         int i, x, y;
36         guchar* pixels = gdk_pixbuf_get_pixels(_SELF(self));
37         int rowstride = gdk_pixbuf_get_rowstride(_SELF(self));
38
39         double factor = 1 + fabs(NUM2DBL(level))/100;
40         if (NUM2DBL(level) < 0) {
41                 factor = 1/factor;
42         }
43
44         for (i = 0; i < 256; i++) {
45                 red_filter[i]  = pow(((double)i)/255, 1/factor) * 255;
46                 blue_filter[i] = pow(((double)i)/255, factor) * 255;
47         }
48     
49         for (y = 0; y < gdk_pixbuf_get_height(_SELF(self)); y++) {
50                 guchar* pixline = &(pixels[rowstride*y]);
51                 for (x = 0; x < gdk_pixbuf_get_width(_SELF(self)); x++) {
52                         pixline[x*3]   = red_filter[pixline[x*3]];
53                         pixline[x*3+2] = blue_filter[pixline[x*3+2]];
54                 }
55         }
56
57         return self;
58 }
59
60 static VALUE gammacorrect(VALUE self, VALUE level) {
61         double filter[256];
62         int i, x, y;
63         guchar* pixels = gdk_pixbuf_get_pixels(_SELF(self));
64         int rowstride = gdk_pixbuf_get_rowstride(_SELF(self));
65
66         double factor = 1 + fabs(NUM2DBL(level))/100;
67         if (NUM2DBL(level) > 0) {
68                 factor = 1/factor;
69         }
70
71         for (i = 0; i < 256; i++) {
72                 filter[i] = pow(((double)i)/255, factor) * 255;
73         }
74     
75         for (y = 0; y < gdk_pixbuf_get_height(_SELF(self)); y++) {
76                 guchar* pixline = &(pixels[rowstride*y]);
77                 for (x = 0; x < gdk_pixbuf_get_width(_SELF(self)); x++) {
78                         pixline[x*3]   = filter[pixline[x*3]];
79                         pixline[x*3+1] = filter[pixline[x*3+1]];
80                         pixline[x*3+2] = filter[pixline[x*3+2]];
81                 }
82         }
83
84         return self;
85 }
86
87 static ExifEntry* exif_entry(VALUE filename, ExifTag tag, ExifData** data) {
88         unsigned int i;
89
90         *data = exif_data_new_from_file(StringValuePtr(filename));
91         if (*data == NULL) {
92                 fprintf(stderr, "libexif failed loading file '%s'\n", StringValuePtr(filename));
93                 return NULL;
94         }
95
96         for (i = 0; i < EXIF_IFD_COUNT; i++) {
97                 ExifEntry* entry = exif_content_get_entry((*data)->ifd[i], tag);
98                 if (entry)
99                         return entry;
100         }
101
102         return NULL;
103 }
104
105 static VALUE exif_orientation(VALUE module, VALUE filename) {
106         ExifData* data = NULL;
107         ExifEntry* entry;
108
109         entry = exif_entry(filename, EXIF_TAG_ORIENTATION, &data);
110
111         if (entry != NULL) {
112                 VALUE ret;
113                 ExifByteOrder o;
114                 ExifShort v_short;
115                 if (!entry || !entry->parent || !entry->parent->parent)
116                         return Qnil;
117                 o = exif_data_get_byte_order(entry->parent->parent);
118                 v_short = exif_get_short(entry->data, o);
119                 ret = INT2NUM(v_short);
120                 exif_data_free(data);
121                 return ret;
122         }
123         
124         if (data)
125                 exif_data_free(data);
126         return Qnil;
127 }
128
129 static VALUE exif_datetimeoriginal(VALUE module, VALUE filename) {
130         ExifData* data = NULL;
131         ExifEntry* entry;
132
133         entry = exif_entry(filename, EXIF_TAG_DATE_TIME_ORIGINAL, &data);
134
135         if (entry != NULL) {
136                 VALUE ret = rb_str_new((const char*) entry->data, entry->size);
137                 exif_data_free(data);
138                 return ret;
139         }
140
141         if (data)
142                 exif_data_free(data);
143         return Qnil;
144 }
145
146 // internalize drawing "video" borders, it is too slow in ruby (0.12 secs on my p4 2.8 GHz, whereas it's barely measurable with this implementation)
147 static VALUE draw_borders(VALUE self, VALUE pixbuf, VALUE x1, VALUE x2, VALUE ystart, VALUE yend) {
148         GdkDrawable* drawable = GDK_DRAWABLE(RVAL2GOBJ(self));
149         int y = NUM2INT(ystart);
150         int yend_ = NUM2INT(yend);
151         GdkPixbuf* pb = GDK_PIXBUF(RVAL2GOBJ(pixbuf));
152         int height = gdk_pixbuf_get_height(pb);
153         while (y < yend_) {
154                 int render_height = MIN(height, yend_ - y);
155                 gdk_draw_pixbuf(drawable, NULL, pb, 0, 0, NUM2INT(x1), y, -1, render_height, GDK_RGB_DITHER_NONE, -1, -1);
156                 gdk_draw_pixbuf(drawable, NULL, pb, 0, 0, NUM2INT(x2), y, -1, render_height, GDK_RGB_DITHER_NONE, -1, -1);
157                 y += height;
158         }
159         return self;
160 }
161
162 // internalize memory leak fix for GdkPixbuf.rotate
163 // (bugged as of rg2 0.16.0)
164 static VALUE rotate_noleak(VALUE self, VALUE angle) {
165         VALUE ret;
166         GdkPixbuf* dest = gdk_pixbuf_rotate_simple(_SELF(self), RVAL2GENUM(angle, GDK_TYPE_PIXBUF_ROTATION));
167         if (dest == NULL)
168                 return Qnil;
169         ret = GOBJ2RVAL(dest);
170         g_object_unref(dest);
171         return ret;
172 }
173
174 // internalize allowing to pass Qnil to RVAL2BOXED to have NULL passed to Gtk
175 // (bugged as of rg2 0.16.0)
176 static VALUE modify_bg(VALUE self, VALUE state, VALUE color) {
177         gtk_widget_modify_bg(GTK_WIDGET(RVAL2GOBJ(self)), RVAL2GENUM(state, GTK_TYPE_STATE_TYPE),
178                              NIL_P(color) ? NULL : (GdkColor*) RVAL2BOXED(color, GDK_TYPE_COLOR));
179         return self;
180 }
181
182 void 
183 Init_libadds()
184 {
185     RGObjClassInfo* cinfo = (RGObjClassInfo*)rbgobj_lookup_class_by_gtype(GDK_TYPE_PIXBUF, Qnil);
186     rb_define_method(cinfo->klass, "whitebalance!", whitebalance, 1); 
187     rb_define_method(cinfo->klass, "gammacorrect!", gammacorrect, 1); 
188     rb_define_method(cinfo->klass, "rotate", rotate_noleak, 1); 
189
190     cinfo = (RGObjClassInfo*)rbgobj_lookup_class_by_gtype(GDK_TYPE_DRAWABLE, Qnil);
191     rb_define_method(cinfo->klass, "draw_borders", draw_borders, 5);
192
193     cinfo = (RGObjClassInfo*)rbgobj_lookup_class_by_gtype(GTK_TYPE_WIDGET, Qnil);
194     rb_define_method(cinfo->klass, "modify_bg", modify_bg, 2);
195
196     VALUE exif = rb_define_module("Exif");
197     rb_define_module_function(exif, "orientation", exif_orientation, 1);
198     rb_define_module_function(exif, "datetimeoriginal", exif_datetimeoriginal, 1);
199 }