async-ruby-vips 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ #ifndef ASYNC_VIPS_CALLBACK_H
2
+ #define ASYNC_VIPS_CALLBACK_H
3
+
4
+ #include "transform_data.h"
5
+
6
+ void av_add_to_event_queue(transform_data_t* tdata);
7
+ void init_async_vips_event_thread(void);
8
+
9
+ #endif
@@ -0,0 +1,54 @@
1
+ #include "details.h"
2
+
3
+ int image_scale_none = 0x00;
4
+ int image_scale_stretch = 0x01;
5
+ int image_scale_fit = 0x02;
6
+ int image_scale_no_scale_up = 0x04;
7
+ int image_scale_detect_proportions = 0x08; // detect if the picture width & height are need to be swapped
8
+ int image_scale_fit_no_scale_up = 0x06; // = (fit | no scale_up)
9
+
10
+ /* Get the factors for scale transformation */
11
+ void av_get_scale_transform2(int original_width, int original_height, int desired_width, int desired_height, int scale_mode, double* factor_x, double* factor_y)
12
+ {
13
+ if(scale_mode & image_scale_none) // NONE
14
+ {
15
+ *factor_x = *factor_y = 1.0;
16
+ }
17
+ else if(scale_mode & image_scale_stretch) // STRETCH
18
+ {
19
+ *factor_x = (double) (original_width) / (double) (desired_width);
20
+ *factor_y = (double) (original_height) / (double) (desired_height);
21
+ }
22
+ else if(scale_mode & image_scale_fit) // FIT
23
+ {
24
+ if(scale_mode & image_scale_detect_proportions)
25
+ {
26
+ double p1, p2;
27
+
28
+ p1 = (double) (desired_width) / (double) (desired_height);
29
+ p2 = (double) (original_width) / (double) (original_height);
30
+
31
+ if((p1 > 1.0 && p2 < 1.0) || (p1 < 1.0 && p2 > 1.0))
32
+ {
33
+ int temp = desired_width;
34
+ desired_width = desired_height;
35
+ desired_height = temp;
36
+ }
37
+ }
38
+
39
+ double k1, k2, k;
40
+
41
+ k1 = (double) (original_width) / (double) (desired_width);
42
+ k2 = (double) (original_height) / (double) (desired_height);
43
+
44
+ k = (k1 > k2 ? k1 : k2);
45
+
46
+ if(scale_mode & image_scale_no_scale_up)
47
+ {
48
+ if(k < 1.0)
49
+ k = 1.0; // do not scale up small images at all.
50
+ }
51
+
52
+ *factor_x = *factor_y = k;
53
+ }
54
+ }
@@ -0,0 +1,15 @@
1
+ #ifndef ASYNC_VIPS_IMAGE_H
2
+ #define ASYNC_VIPS_IMAGE_H
3
+
4
+ extern int image_scale_none;
5
+ extern int image_scale_stretch;
6
+ extern int image_scale_fit;
7
+ extern int image_scale_no_scale_up;
8
+ extern int image_scale_detect_proportions;
9
+ extern int image_scale_fit_no_scale_up;
10
+
11
+
12
+ void av_get_scale_transform2(int original_width, int original_height, int desired_width, int desired_height, int scale_mode, double* factor_x, double* factor_y);
13
+
14
+
15
+ #endif
@@ -0,0 +1,18 @@
1
+ ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
2
+
3
+ require "mkmf"
4
+
5
+ File::unlink("Makefile") if (File::exist?("Makefile"))
6
+
7
+ if not pkg_config("vips")
8
+ VIPS_VERSIONS = %w[7.29 7.28 7.27 7.26 7.24]
9
+
10
+ if not VIPS_VERSIONS.detect {|x| pkg_config("vips-#{x}") }
11
+ raise("no pkg_config for any of following libvips versions: #{VIPS_VERSIONS.join(', ')}")
12
+ end
13
+ end
14
+
15
+ have_header('vips/vips.h')
16
+ have_header('libexif/exif-data.h')
17
+
18
+ create_makefile('async_vips_ext')
@@ -0,0 +1,66 @@
1
+ #include "image.h"
2
+ #include <sys/types.h>
3
+ #include <sys/stat.h>
4
+
5
+
6
+ VALUE cImage;
7
+
8
+ /* Get the source parameter */
9
+ static VALUE av_image_src(VALUE self)
10
+ {
11
+ return rb_iv_get(self, "@src");
12
+ }
13
+
14
+ /* Get the destination parameter */
15
+ static VALUE av_image_dst(VALUE self)
16
+ {
17
+ return rb_iv_get(self, "@dst");
18
+ }
19
+
20
+ /* Get the error parameter */
21
+ static VALUE av_image_error(VALUE self)
22
+ {
23
+ return rb_iv_get(self, "@error");
24
+ }
25
+
26
+ /* Get the image width */
27
+ static VALUE av_image_width(VALUE self)
28
+ {
29
+ return rb_iv_get(self, "@width");
30
+ }
31
+
32
+ /* Get the image height */
33
+ static VALUE av_image_height(VALUE self)
34
+ {
35
+ return rb_iv_get(self, "@height");
36
+ }
37
+
38
+ /* Get the size of the dst-image file */
39
+ static VALUE av_image_size(VALUE self)
40
+ {
41
+ return rb_iv_get(self, "@size");
42
+ }
43
+
44
+ /* Initialize the object, called before the callback is invoked */
45
+ void av_image_init(VALUE self, const transform_data_t* tdata)
46
+ {
47
+ rb_iv_set(self, "@src", (tdata->src_path ? rb_str_new2(tdata->src_path) : Qnil));
48
+ rb_iv_set(self, "@dst", (tdata->dst_path ? rb_str_new2(tdata->dst_path) : Qnil));
49
+ rb_iv_set(self, "@error", (tdata->err_str ? rb_str_new2(tdata->err_str) : Qnil));
50
+ rb_iv_set(self, "@width", INT2FIX(tdata->final_width));
51
+ rb_iv_set(self, "@height", INT2FIX(tdata->final_height));
52
+ rb_iv_set(self, "@size", INT2FIX(tdata->final_size));
53
+ }
54
+
55
+
56
+ void init_async_vips_image()
57
+ {
58
+ cImage = rb_define_class_under(mAsyncVips, "Image", rb_cObject);
59
+
60
+ rb_define_method(cImage, "src", av_image_src, 0);
61
+ rb_define_method(cImage, "dst", av_image_dst, 0);
62
+ rb_define_method(cImage, "error", av_image_error, 0);
63
+ rb_define_method(cImage, "width", av_image_width, 0);
64
+ rb_define_method(cImage, "height", av_image_height, 0);
65
+ rb_define_method(cImage, "size", av_image_size, 0);
66
+ }
@@ -0,0 +1,13 @@
1
+ #ifndef ASYNC_VIPS_IMAGE_H
2
+ #define ASYNC_VIPS_IMAGE_H
3
+
4
+ #include "async_vips.h"
5
+ #include "transform_data.h"
6
+
7
+ extern VALUE cImage;
8
+
9
+ void av_image_init(VALUE self, const transform_data_t* tdata);
10
+
11
+ void init_async_vips_image();
12
+
13
+ #endif
@@ -0,0 +1,42 @@
1
+ #include "info.h"
2
+ #include "transform.h"
3
+ #include "writer.h"
4
+ #include <sys/types.h>
5
+ #include <sys/stat.h>
6
+
7
+ ID av_i_id_load;
8
+
9
+ /* Returns image information: width, height, file size */
10
+ static VALUE av_info(int argc, VALUE *argv, VALUE self)
11
+ {
12
+ rb_need_block();
13
+ VALUE proc = rb_block_proc();
14
+
15
+ VALUE rest;
16
+ rb_scan_args(argc, argv, "*", &rest);
17
+
18
+ VALUE params = rb_ary_pop(rest);
19
+ VALUE tmp = rb_check_hash_type(params);
20
+ if (NIL_P(tmp))
21
+ rb_raise(rb_eArgError, "No info parameters specified");
22
+
23
+ // Parse options
24
+ VALUE load = rb_hash_aref(params, ID2SYM(av_i_id_load));
25
+ if(NIL_P(load))
26
+ rb_raise(rb_eArgError, "No image source specified: info(:load => 'image.jpg')");
27
+
28
+ transform_data_t* tdata = av_make_transform_data_src(StringValuePtr(load));
29
+ tdata->proc = proc;
30
+
31
+ rb_gc_register_address(&tdata->proc);
32
+
33
+ av_enqueue_task(av_build_image_thread_func, tdata);
34
+
35
+ return self;
36
+ }
37
+
38
+ void init_async_vips_info()
39
+ {
40
+ rb_define_singleton_method(mAsyncVips, "info", av_info, -1);
41
+ av_i_id_load = rb_intern("load");
42
+ }
@@ -0,0 +1,8 @@
1
+ #ifndef ASYNC_VIPS_INFO_H
2
+ #define ASYNC_VIPS_INFO_H
3
+
4
+ #include "async_vips.h"
5
+
6
+ void init_async_vips_info();
7
+
8
+ #endif
@@ -0,0 +1,396 @@
1
+ #include "transform.h"
2
+ #include "callback.h"
3
+ #include "details.h"
4
+ #include <pthread.h>
5
+ #include <libexif/exif-data.h>
6
+ #include <sys/stat.h>
7
+
8
+
9
+ ID av_t_id_load;
10
+ ID av_t_id_save;
11
+ ID av_t_id_scale_x;
12
+ ID av_t_id_scale_y;
13
+ ID av_t_id_natural_orientation;
14
+
15
+
16
+ /* Get the size of a file */
17
+ static void av_get_image_file_size(const char* file_path, long long* file_size)
18
+ {
19
+ long long sz = 0;
20
+ if(file_path)
21
+ {
22
+ int err;
23
+ struct stat st;
24
+ memset(&st, 0, sizeof(st));
25
+ err = stat(file_path, &st);
26
+ if(!err)
27
+ {
28
+ sz = st.st_size;
29
+ }
30
+ }
31
+
32
+ *file_size = sz;
33
+ }
34
+
35
+ /*
36
+ Get image orientation from EXIF
37
+
38
+ 0th Row 0th Column
39
+ 1 top left side
40
+ 2 top right side
41
+ 3 bottom right side
42
+ 4 bottom left side
43
+ 5 left side top
44
+ 6 right side top
45
+ 7 right side bottom
46
+ 8 left side bottom
47
+ */
48
+ static int av_get_orientation(VipsImage* image)
49
+ {
50
+ int orientation = 0;
51
+
52
+ GType exif_type = vips_image_get_typeof(image, VIPS_META_EXIF_NAME);
53
+ if(exif_type != 0)
54
+ {
55
+ unsigned char *data;
56
+ size_t data_length;
57
+ if(!vips_image_get_blob(image, VIPS_META_EXIF_NAME, (void *)&data, &data_length))
58
+ {
59
+ ExifData *ed;
60
+ ed = exif_data_new_from_data(data, data_length);
61
+ if(ed != NULL)
62
+ {
63
+ ExifByteOrder bo = exif_data_get_byte_order(ed);
64
+ ExifEntry *ee = exif_data_get_entry(ed, EXIF_TAG_ORIENTATION);
65
+ if(ee)
66
+ {
67
+ orientation = exif_get_short(ee->data, bo);
68
+ }
69
+
70
+ exif_data_free(ed);
71
+ }
72
+ }
73
+ }
74
+
75
+ return orientation;
76
+ }
77
+
78
+ /* Sets the image orientation */
79
+ static void av_set_orientation(VipsImage* image, int orientation)
80
+ {
81
+ GType exif_type = vips_image_get_typeof(image, VIPS_META_EXIF_NAME);
82
+ if(exif_type != 0)
83
+ {
84
+ unsigned char *data;
85
+ size_t data_length;
86
+ if(!vips_image_get_blob(image, VIPS_META_EXIF_NAME, (void *)&data, &data_length))
87
+ {
88
+ ExifData *ed;
89
+ ed = exif_data_new_from_data(data, data_length);
90
+ if(ed != NULL)
91
+ {
92
+ ExifByteOrder bo = exif_data_get_byte_order(ed);
93
+ ExifEntry *ee = exif_data_get_entry(ed, EXIF_TAG_ORIENTATION);
94
+ if(ee)
95
+ {
96
+ exif_set_short(ee->data, bo, (short)orientation);
97
+ vips_image_set_blob(image, VIPS_META_EXIF_NAME, NULL, (void *)ed, data_length);
98
+ }
99
+
100
+ exif_data_free(ed);
101
+ }
102
+ }
103
+ }
104
+
105
+ }
106
+
107
+ /* Shrinks image using the provided width and height ratios */
108
+ static VipsImage* av_internal_shrink_image(VipsImage* image, double width_ratio, double height_ratio, char** err_str)
109
+ {
110
+ VipsImage* t = im_open("temp-image", "p");
111
+ if(!t)
112
+ {
113
+ *err_str = copy_vips_error();
114
+ return NULL;
115
+ }
116
+
117
+ if(im_shrink(image, t, width_ratio, height_ratio))
118
+ {
119
+ *err_str = copy_vips_error();
120
+ im_close(t);
121
+ return NULL;
122
+ }
123
+
124
+ return t;
125
+ }
126
+
127
+ /* Rotates the image */
128
+ static VipsImage* av_internal_rotate(VipsImage* in, VipsAngle angle, char** err_str)
129
+ {
130
+ VipsImage* t = im_open("temp-image", "p");
131
+ if(!t)
132
+ {
133
+ *err_str = copy_vips_error();
134
+ return NULL;
135
+ }
136
+
137
+ if(vips_rot(in, &t, angle, NULL))
138
+ {
139
+ *err_str = copy_vips_error();
140
+ return NULL;
141
+ }
142
+
143
+ return t;
144
+ }
145
+
146
+ /* Flips the image */
147
+ static VipsImage* av_internal_flip(VipsImage* in, VipsDirection direction, char** err_str)
148
+ {
149
+ VipsImage* t = im_open("temp-image", "p");
150
+ if(!t)
151
+ {
152
+ *err_str = copy_vips_error();
153
+ return NULL;
154
+ }
155
+
156
+ if(vips_flip(in, &t, direction, NULL))
157
+ {
158
+ *err_str = copy_vips_error();
159
+ return NULL;
160
+ }
161
+
162
+ return t;
163
+ }
164
+
165
+ /* Rotates & Flips the image */
166
+ static VipsImage* av_internal_rotate_flip(VipsImage* in, VipsAngle angle, VipsDirection direction, char** err_str)
167
+ {
168
+ VipsImage* t;
169
+ t = av_internal_rotate(in, angle, err_str);
170
+ if(t)
171
+ {
172
+ VipsImage* f;
173
+ f = av_internal_flip(t, direction, err_str);
174
+ if(f)
175
+ {
176
+ im_close(t);
177
+ t = f;
178
+ }
179
+ else
180
+ {
181
+ im_close(t);
182
+ t = NULL;
183
+ }
184
+ }
185
+
186
+ return t;
187
+ }
188
+
189
+ /*
190
+ Transforms image orientation
191
+ 1) = NONE
192
+ 2) = FLIP_H
193
+ 3) = ROT_180
194
+ 4) = FLIP_V
195
+ 5) = TRANSPOSE
196
+ 6) = ROT_90
197
+ 7) = TRANSVERSE
198
+ 8) = ROT_270
199
+ */
200
+ static VipsImage* av_internal_transform_image_orientation(VipsImage* image, int orientation, char** err_str)
201
+ {
202
+ VipsImage* res = NULL;
203
+
204
+ switch(orientation)
205
+ {
206
+ case 1:
207
+ // no-op
208
+ break;
209
+
210
+ case 2:
211
+ res = av_internal_flip(image, VIPS_DIRECTION_HORIZONTAL, err_str);
212
+ break;
213
+
214
+ case 3:
215
+ res = av_internal_rotate(image, VIPS_ANGLE_180, err_str);
216
+ break;
217
+
218
+ case 4:
219
+ res = av_internal_flip(image, VIPS_DIRECTION_VERTICAL, err_str);
220
+ break;
221
+
222
+ case 5:
223
+ res = av_internal_rotate_flip(image, VIPS_ANGLE_90, VIPS_DIRECTION_HORIZONTAL, err_str);
224
+ break;
225
+
226
+ case 6:
227
+ res = av_internal_rotate(image, VIPS_ANGLE_90, err_str);
228
+ break;
229
+
230
+ case 7:
231
+ res = av_internal_rotate_flip(image, VIPS_ANGLE_270, VIPS_DIRECTION_HORIZONTAL, err_str);
232
+ break;
233
+
234
+ case 8:
235
+ res = av_internal_rotate(image, VIPS_ANGLE_270, err_str);
236
+ break;
237
+
238
+ default:
239
+ // no-op
240
+ break;
241
+ }
242
+
243
+ if(res)
244
+ {
245
+ av_set_orientation(res, 1);
246
+ }
247
+
248
+ return res;
249
+ }
250
+
251
+
252
+ /* Thread function for image processing */
253
+ void* av_build_image_thread_func(void* data)
254
+ {
255
+ transform_data_t* tdata = (transform_data_t*)data;
256
+ if(!tdata)
257
+ return NULL;
258
+
259
+ VipsImage* image;
260
+ image = vips_image_new_mode(tdata->src_path, "r");
261
+ if(image)
262
+ {
263
+ // ORIENTATION
264
+ int is_transform_orientation = tdata->natural_orientation;
265
+ int orientation = av_get_orientation(image);
266
+ //fprintf(stderr, "orientation: %d\n", orientation);
267
+
268
+ // SHRINK
269
+ if(tdata->dst_path && tdata->target_width && tdata->target_height)
270
+ {
271
+ double width_ratio = 0, height_ratio = 0;
272
+
273
+ av_get_scale_transform2(image->Xsize, image->Ysize,
274
+ tdata->target_width, tdata->target_height,
275
+ image_scale_fit_no_scale_up,
276
+ &width_ratio, &height_ratio);
277
+
278
+ VipsImage* t;
279
+ t = av_internal_shrink_image(image, width_ratio, height_ratio, &tdata->err_str);
280
+ if(t)
281
+ {
282
+ if(is_transform_orientation && orientation > 1)
283
+ {
284
+ // Need to transform an image so that orientation becomes 1
285
+ VipsImage* tran;
286
+ tran = av_internal_transform_image_orientation(t, orientation, &tdata->err_str);
287
+ if(tran)
288
+ {
289
+ im_close(t);
290
+ t = tran;
291
+ }
292
+ }
293
+
294
+ VipsImage* out;
295
+ out = vips_image_new_mode(tdata->dst_path, "w");
296
+ if(out)
297
+ {
298
+ if(im_copy(t, out))
299
+ {
300
+ tdata->err_str = copy_vips_error();
301
+ }
302
+
303
+ tdata->final_width = out->Xsize;
304
+ tdata->final_height = out->Ysize;
305
+
306
+ im_close(out);
307
+
308
+ av_get_image_file_size(tdata->dst_path, &tdata->final_size);
309
+ }
310
+ else
311
+ {
312
+ tdata->err_str = copy_vips_error();
313
+ }
314
+
315
+ im_close(t);
316
+ }
317
+ }
318
+ else
319
+ {
320
+ tdata->final_width = image->Xsize;
321
+ tdata->final_height = image->Ysize;
322
+ av_get_image_file_size(tdata->src_path, &tdata->final_size);
323
+ }
324
+
325
+ im_close(image);
326
+ }
327
+
328
+ av_add_to_event_queue(tdata);
329
+
330
+ return NULL;
331
+ }
332
+
333
+ /* Image transformation */
334
+ static VALUE av_transform(int argc, VALUE *argv, VALUE self)
335
+ {
336
+ rb_need_block();
337
+ VALUE proc = rb_block_proc();
338
+
339
+ VALUE rest;
340
+ rb_scan_args(argc, argv, "*", &rest);
341
+
342
+ VALUE params = rb_ary_pop(rest);
343
+ VALUE tmp = rb_check_hash_type(params);
344
+ if (NIL_P(tmp))
345
+ rb_raise(rb_eArgError, "No transformation parameters specified");
346
+
347
+ // Parse options
348
+ VALUE load = rb_hash_aref(params, ID2SYM(av_t_id_load));
349
+ if(NIL_P(load))
350
+ rb_raise(rb_eArgError, "No image source specified: transform(:load => 'image.jpg')");
351
+
352
+ VALUE save = rb_hash_aref(params, ID2SYM(av_t_id_save));
353
+ if(NIL_P(save))
354
+ rb_raise(rb_eArgError, "No image destination specified: transform(:save => 'output.jpg')");
355
+
356
+ VALUE scale_x = rb_hash_aref(params, ID2SYM(av_t_id_scale_x));
357
+ VALUE scale_y = rb_hash_aref(params, ID2SYM(av_t_id_scale_y));
358
+
359
+ if(NIL_P(scale_x) && NIL_P(scale_y))
360
+ rb_raise(rb_eArgError, "No scale width or height specified of the source image: transform(:scale_x => 800, :scale_y => 600)");
361
+
362
+ if(NIL_P(scale_y))
363
+ scale_y = scale_x;
364
+
365
+ if(NIL_P(scale_x))
366
+ scale_x = scale_y;
367
+
368
+ VALUE natural_orient_flag = rb_hash_aref(params, ID2SYM(av_t_id_natural_orientation));
369
+ if(!NIL_P(natural_orient_flag) &&
370
+ (TYPE(natural_orient_flag) != T_TRUE) && (TYPE(natural_orient_flag) != T_FALSE))
371
+ rb_raise(rb_eArgError, "Invalid natural orientation: transform(:natural_orientation => true|false)");
372
+
373
+ transform_data_t* tdata = av_make_transform_data(StringValuePtr(load), StringValuePtr(save));
374
+ tdata->target_width = NUM2INT(scale_x);
375
+ tdata->target_height = NUM2INT(scale_y);
376
+ tdata->natural_orientation = (TYPE(natural_orient_flag) == T_TRUE ? 1 : 0);
377
+ tdata->proc = proc;
378
+
379
+ rb_gc_register_address(&tdata->proc);
380
+
381
+ av_enqueue_task(av_build_image_thread_func, tdata);
382
+
383
+ return self;
384
+ }
385
+
386
+ /* Initialize transformation pipeline */
387
+ void init_async_vips_transform(void)
388
+ {
389
+ rb_define_singleton_method(mAsyncVips, "transform", av_transform, -1);
390
+
391
+ av_t_id_load = rb_intern("load");
392
+ av_t_id_save = rb_intern("save");
393
+ av_t_id_scale_x = rb_intern("scale_x");
394
+ av_t_id_scale_y = rb_intern("scale_y");
395
+ av_t_id_natural_orientation = rb_intern("natural_orientation");
396
+ }