async-ruby-vips 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }