magro 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0b0bad63e73de3bde86fdd749536f8949fd84e8c
4
- data.tar.gz: a6c86139fe5cc7c401a9090a34400bf2387f5cc6
3
+ metadata.gz: 32deda4c751be24b856a25e98b44e15d66838fb4
4
+ data.tar.gz: 9d7c6693670cb4c1e7959ff8b11f936d043419d5
5
5
  SHA512:
6
- metadata.gz: 872b26cc6825cd1bb7954e5104d6b196944798126d2d3ae66f08380d7017e747a8eb4748b6fbe2d688feff6ed79fbdb0d082ec29ed188b6cab38601125d7543e
7
- data.tar.gz: 53de5a2b240ce63404a8831f61b6285bf3009d3647c487ac6fb7fa32cada5372dced92d839297af911ee4a6b4c6e062bef88a692aac8d4ccdfa7ca1c7f5f45ea
6
+ metadata.gz: e61aee7251656d90966217242280f76699ded1c81f291b7aacf24ff70c55e5bc897ee551df112fb3f1a992dd0571200b0bd85984adf89aadb43e13b49c5236bf
7
+ data.tar.gz: b6560a03645922b6a0cc2cef2c476773f9f7b1bc0ccd41b45ed3e29179066f037329f4075d9465e7c4826f81dd8cfdd60bb3f30f01571e2fb543582ce2cebdda
data/.gitignore CHANGED
@@ -7,6 +7,8 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ /spec/files/tmp.jpeg
11
+ /spec/files/tmp.png
10
12
 
11
13
  # rspec failure tracking
12
14
  .rspec_status
data/CHANGELOG.md CHANGED
@@ -1,2 +1,8 @@
1
+ # 0.1.1
2
+ - Refactor extension codes.
3
+ - Fix to raise IOError when occured file reading / writing error.
4
+ - Fix to raise NoMemoryError when occured memory allocation error.
5
+ - Several documentation improvements.
6
+
1
7
  # 0.1.0
2
8
  - First release.
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # Magro
2
2
 
3
3
  [![Build Status](https://travis-ci.org/yoshoku/magro.svg?branch=master)](https://travis-ci.org/yoshoku/magro)
4
+ [![Gem Version](https://badge.fury.io/rb/magro.svg)](https://badge.fury.io/rb/magro)
4
5
  [![BSD 3-Clause License](https://img.shields.io/badge/License-BSD%203--Clause-orange.svg)](https://github.com/yoshoku/numo-liblinear/blob/master/LICENSE.txt)
6
+ [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](https://www.rubydoc.info/gems/magro/0.1.1)
5
7
 
6
8
  Magro is an image processing library in Ruby.
7
9
  Magro uses [Numo::NArray](https://github.com/ruby-numo/numo-narray) arrays as image objects.
data/ext/magro/io.c ADDED
@@ -0,0 +1,411 @@
1
+ #include "io.h"
2
+
3
+ RUBY_EXTERN VALUE mMagro;
4
+
5
+ /**
6
+ * @!visibility private
7
+ */
8
+ static
9
+ VALUE magro_io_read_png(VALUE self, VALUE filename_)
10
+ {
11
+ char* filename = StringValuePtr(filename_);
12
+ FILE* file_ptr = fopen(filename, "rb");
13
+ unsigned char header[8];
14
+ png_structp png_ptr;
15
+ png_infop info_ptr;
16
+ png_bytep* row_ptr_ptr;
17
+ png_bytep row_ptr;
18
+ png_uint_32 width, height;
19
+ int color_type;
20
+ int bit_depth;
21
+ png_uint_32 y;
22
+ int n_dims = 0;
23
+ int n_ch;
24
+ size_t shape[3] = { 0 };
25
+ VALUE nary;
26
+ uint8_t* nary_ptr;
27
+
28
+ if (file_ptr == NULL) {
29
+ rb_raise(rb_eIOError, "Failed to open file '%s'", filename);
30
+ return Qnil;
31
+ }
32
+
33
+ if (fread(header, 1, 8, file_ptr) < 8) {
34
+ fclose(file_ptr);
35
+ rb_raise(rb_eIOError, "Failed to read header info '%s'", filename);
36
+ return Qnil;
37
+ }
38
+
39
+ if (png_sig_cmp(header, 0, 8)) {
40
+ fclose(file_ptr);
41
+ rb_raise(rb_eIOError, "Failed to read header info '%s'", filename);
42
+ return Qnil;
43
+ }
44
+
45
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
46
+ if (png_ptr == NULL) {
47
+ fclose(file_ptr);
48
+ rb_raise(rb_eNoMemError, "Failed to allocate memory.");
49
+ return Qnil;
50
+ }
51
+ info_ptr = png_create_info_struct(png_ptr);
52
+ if (info_ptr == NULL) {
53
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
54
+ fclose(file_ptr);
55
+ rb_raise(rb_eNoMemError, "Failed to allocate memory.");
56
+ return Qnil;
57
+ }
58
+ if (setjmp(png_jmpbuf(png_ptr))) {
59
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
60
+ fclose(file_ptr);
61
+ rb_raise(rb_eIOError, "Error happened while reading file '%s'", filename);
62
+ return Qnil;
63
+ }
64
+
65
+ png_init_io(png_ptr, file_ptr);
66
+ png_set_sig_bytes(png_ptr, 8);
67
+
68
+ png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16, NULL);
69
+ row_ptr_ptr = png_get_rows(png_ptr, info_ptr);
70
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
71
+
72
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
73
+ png_set_palette_to_rgb(png_ptr);
74
+ png_read_update_info(png_ptr, info_ptr);
75
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
76
+ }
77
+
78
+ switch (color_type) {
79
+ case PNG_COLOR_TYPE_GRAY:
80
+ n_ch = 1;
81
+ n_dims = 2;
82
+ shape[0] = height;
83
+ shape[1] = width;
84
+ break;
85
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
86
+ n_ch = 2;
87
+ n_dims = 3;
88
+ shape[0] = height;
89
+ shape[1] = width;
90
+ shape[2] = 2;
91
+ break;
92
+ case PNG_COLOR_TYPE_RGB:
93
+ n_ch = 3;
94
+ n_dims = 3;
95
+ shape[0] = height;
96
+ shape[1] = width;
97
+ shape[2] = 3;
98
+ break;
99
+ case PNG_COLOR_TYPE_RGB_ALPHA:
100
+ n_ch = 4;
101
+ n_dims = 3;
102
+ shape[0] = height;
103
+ shape[1] = width;
104
+ shape[2] = 4;
105
+ break;
106
+ default:
107
+ n_dims = 0;
108
+ break;
109
+ }
110
+
111
+ if (n_dims == 0) {
112
+ fclose(file_ptr);
113
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
114
+ rb_raise(rb_eIOError, "Unsupported color type of input file '%s'", filename);
115
+ return Qnil;
116
+ }
117
+
118
+ nary = rb_narray_new(numo_cUInt8, n_dims, shape);
119
+ nary_ptr = (uint8_t*)na_get_pointer_for_write(nary);
120
+
121
+ for (y = 0; y < height; y++) {
122
+ row_ptr = row_ptr_ptr[y];
123
+ memcpy(nary_ptr + y * width * n_ch, row_ptr, width * n_ch);
124
+ }
125
+
126
+ fclose(file_ptr);
127
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
128
+
129
+ return nary;
130
+ }
131
+
132
+ /**
133
+ * @!visibility private
134
+ */
135
+ static
136
+ VALUE magro_io_save_png(VALUE self, VALUE filename_, VALUE image)
137
+ {
138
+ char* filename = StringValuePtr(filename_);
139
+ FILE* file_ptr = fopen(filename, "wb");
140
+ png_structp png_ptr;
141
+ png_infop info_ptr;
142
+ png_bytep* row_ptr_ptr;
143
+ png_uint_32 width, height;
144
+ int color_type;
145
+ int bit_depth = 8;
146
+ png_uint_32 y;
147
+ int n_ch;
148
+ int n_dims;
149
+ narray_t* image_nary;
150
+ uint8_t* image_ptr;
151
+
152
+ if (file_ptr == NULL) {
153
+ rb_raise(rb_eIOError, "Failed to open file '%s'", filename);
154
+ return Qfalse;
155
+ }
156
+
157
+ if (CLASS_OF(image) != numo_cUInt8) {
158
+ image = rb_funcall(numo_cUInt8, rb_intern("cast"), 1, image);
159
+ }
160
+ if (!RTEST(nary_check_contiguous(image))) {
161
+ image = nary_dup(image);
162
+ }
163
+
164
+ GetNArray(image, image_nary);
165
+ n_dims = NA_NDIM(image_nary);
166
+ height = (png_uint_32)NA_SHAPE(image_nary)[0];
167
+ width = (png_uint_32)NA_SHAPE(image_nary)[1];
168
+ image_ptr = (uint8_t*)na_get_pointer_for_read(image);
169
+
170
+ n_ch = 1;
171
+ if (n_dims == 3) {
172
+ n_ch = (int)NA_SHAPE(image_nary)[2];
173
+ }
174
+
175
+ switch (n_ch) {
176
+ case 4:
177
+ color_type = PNG_COLOR_TYPE_RGBA;
178
+ break;
179
+ case 3:
180
+ color_type = PNG_COLOR_TYPE_RGB;
181
+ break;
182
+ case 2:
183
+ color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
184
+ break;
185
+ default:
186
+ color_type = PNG_COLOR_TYPE_GRAY;
187
+ break;
188
+ }
189
+
190
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
191
+ if (png_ptr == NULL) {
192
+ fclose(file_ptr);
193
+ rb_raise(rb_eNoMemError, "Failed to allocate memory.");
194
+ return Qfalse;
195
+ }
196
+ info_ptr = png_create_info_struct(png_ptr);
197
+ if (info_ptr == NULL) {
198
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
199
+ fclose(file_ptr);
200
+ rb_raise(rb_eNoMemError, "Failed to allocate memory.");
201
+ return Qfalse;
202
+ }
203
+ if (setjmp(png_jmpbuf(png_ptr))) {
204
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
205
+ fclose(file_ptr);
206
+ return Qfalse;
207
+ }
208
+
209
+ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type,
210
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
211
+
212
+ row_ptr_ptr = png_malloc(png_ptr, height * sizeof(png_bytep));
213
+ for (y = 0; y < height; y++) {
214
+ row_ptr_ptr[y] = png_malloc(png_ptr, width * n_ch * sizeof(png_byte));
215
+ memcpy(row_ptr_ptr[y], image_ptr + y * width * n_ch, width * n_ch);
216
+ }
217
+
218
+ png_init_io(png_ptr, file_ptr);
219
+ png_set_rows(png_ptr, info_ptr, row_ptr_ptr);
220
+ png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
221
+
222
+ fclose(file_ptr);
223
+ for (y = 0; y < height; y++) {
224
+ png_free(png_ptr, row_ptr_ptr[y]);
225
+ }
226
+ png_free(png_ptr, row_ptr_ptr);
227
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
228
+
229
+ return Qtrue;
230
+ }
231
+
232
+ struct my_error_mgr {
233
+ struct jpeg_error_mgr pub;
234
+ jmp_buf setjmp_buffer;
235
+ };
236
+
237
+ static void
238
+ my_error_exit(j_common_ptr cinfo)
239
+ {
240
+ struct my_error_mgr* my_err = (struct my_error_mgr*)cinfo->err;
241
+ (*cinfo->err->output_message)(cinfo);
242
+ longjmp(my_err->setjmp_buffer, 1);
243
+ }
244
+
245
+ /**
246
+ * @!visibility private
247
+ */
248
+ static
249
+ VALUE magro_io_read_jpg(VALUE self, VALUE filename_)
250
+ {
251
+ char* filename = StringValuePtr(filename_);
252
+ FILE* file_ptr = fopen(filename, "rb");
253
+ struct jpeg_decompress_struct jpeg;
254
+ struct my_error_mgr err;
255
+ unsigned int width, height;
256
+ int n_colors;
257
+ size_t shape[3] = { 0 };
258
+ int n_dims;
259
+ unsigned int y;
260
+ VALUE nary;
261
+ uint8_t* nary_ptr;
262
+ JSAMPLE* tmp;
263
+
264
+ if (file_ptr == NULL) {
265
+ rb_raise(rb_eIOError, "Failed to open file '%s'", filename);
266
+ return Qnil;
267
+ }
268
+
269
+ jpeg.err = jpeg_std_error(&err.pub);
270
+ err.pub.error_exit = my_error_exit;
271
+ if (setjmp(err.setjmp_buffer)) {
272
+ rb_raise(rb_eIOError, "Error happened while reading file '%s'", filename);
273
+ return Qnil;
274
+ }
275
+
276
+ jpeg_create_decompress(&jpeg);
277
+ jpeg_stdio_src(&jpeg, file_ptr);
278
+ jpeg_read_header(&jpeg, TRUE);
279
+ jpeg_start_decompress(&jpeg);
280
+
281
+ width = jpeg.output_width;
282
+ height = jpeg.output_height;
283
+ n_colors = jpeg.out_color_components;
284
+
285
+ n_dims = n_colors == 1 ? 2 : 3;
286
+ shape[0] = height;
287
+ shape[1] = width;
288
+ shape[2] = n_colors;
289
+ nary = rb_narray_new(numo_cUInt8, n_dims, shape);
290
+ nary_ptr = (uint8_t*)na_get_pointer_for_write(nary);
291
+
292
+ for (y = 0; y < height; y++) {
293
+ tmp = nary_ptr + y * width * n_colors;
294
+ jpeg_read_scanlines(&jpeg, &tmp, 1);
295
+ }
296
+
297
+ fclose(file_ptr);
298
+ jpeg_finish_decompress(&jpeg);
299
+ jpeg_destroy_decompress(&jpeg);
300
+
301
+ return nary;
302
+ }
303
+
304
+ /**
305
+ * @!visibility private
306
+ */
307
+ static
308
+ VALUE magro_io_save_jpg(int argc, VALUE* argv, VALUE self)
309
+ {
310
+ VALUE filename_;
311
+ VALUE image;
312
+ VALUE quality_;
313
+ char* filename;
314
+ FILE* file_ptr;
315
+ struct jpeg_compress_struct jpeg;
316
+ struct my_error_mgr err;
317
+ narray_t* image_nary;
318
+ int quality;
319
+ int n_dims, n_ch;
320
+ unsigned int width, height, y;
321
+ uint8_t* image_ptr;
322
+ JSAMPLE* tmp;
323
+
324
+ rb_scan_args(argc, argv, "21", &filename_, &image, &quality_);
325
+
326
+ if (NIL_P(quality_)) {
327
+ quality = 95;
328
+ } else {
329
+ quality = NUM2INT(quality_);
330
+ }
331
+
332
+ filename = StringValuePtr(filename_);
333
+
334
+ if (CLASS_OF(image) != numo_cUInt8) {
335
+ image = rb_funcall(numo_cUInt8, rb_intern("cast"), 1, image);
336
+ }
337
+ if (!RTEST(nary_check_contiguous(image))) {
338
+ image = nary_dup(image);
339
+ }
340
+
341
+ jpeg.err = jpeg_std_error(&err.pub);
342
+ err.pub.error_exit = my_error_exit;
343
+ if (setjmp(err.setjmp_buffer)) {
344
+ return Qfalse;
345
+ }
346
+
347
+ jpeg_create_compress(&jpeg);
348
+
349
+ file_ptr = fopen(filename, "wb");
350
+ if (file_ptr == NULL) {
351
+ rb_raise(rb_eIOError, "Failed to open file '%s'", filename);
352
+ jpeg_destroy_compress(&jpeg);
353
+ return Qfalse;
354
+ }
355
+
356
+ GetNArray(image, image_nary);
357
+ n_dims = NA_NDIM(image_nary);
358
+ height = (unsigned int)NA_SHAPE(image_nary)[0];
359
+ width = (unsigned int)NA_SHAPE(image_nary)[1];
360
+ image_ptr = (uint8_t*)na_get_pointer_for_read(image);
361
+
362
+ n_ch = 1;
363
+ if (n_dims == 3) {
364
+ n_ch = (int)NA_SHAPE(image_nary)[2];
365
+ }
366
+
367
+ jpeg_stdio_dest(&jpeg, file_ptr);
368
+
369
+ jpeg.image_height = height;
370
+ jpeg.image_width = width;
371
+ jpeg.input_components = n_ch;
372
+
373
+ switch (n_ch) {
374
+ case 3:
375
+ jpeg.in_color_space = JCS_RGB;
376
+ break;
377
+ case 1:
378
+ jpeg.in_color_space = JCS_GRAYSCALE;
379
+ break;
380
+ default:
381
+ jpeg.in_color_space = JCS_UNKNOWN;
382
+ break;
383
+ }
384
+
385
+ jpeg_set_defaults(&jpeg);
386
+
387
+ jpeg_set_quality(&jpeg, quality, TRUE);
388
+
389
+ jpeg_start_compress(&jpeg, TRUE);
390
+
391
+ for (y = 0; y < height; y++) {
392
+ tmp = image_ptr + y * width * n_ch;
393
+ jpeg_write_scanlines(&jpeg, &tmp, 1);
394
+ }
395
+
396
+ jpeg_finish_compress(&jpeg);
397
+ jpeg_destroy_compress(&jpeg);
398
+
399
+ fclose(file_ptr);
400
+
401
+ return Qtrue;
402
+ }
403
+
404
+ void init_io_module()
405
+ {
406
+ VALUE mIO = rb_define_module_under(mMagro, "IO");
407
+ rb_define_module_function(mIO, "read_png", magro_io_read_png, 1);
408
+ rb_define_module_function(mIO, "save_png", magro_io_save_png, 2);
409
+ rb_define_module_function(mIO, "read_jpg", magro_io_read_jpg, 1);
410
+ rb_define_module_function(mIO, "save_jpg", magro_io_save_jpg, -1);
411
+ }
data/ext/magro/io.h ADDED
@@ -0,0 +1,18 @@
1
+ #ifndef MAGRO_IO_H
2
+ #define MAGRO_IO_H 1
3
+
4
+ #include <stdio.h>
5
+ #include <stdlib.h>
6
+ #include <string.h>
7
+
8
+ #include <setjmp.h>
9
+ #include <png.h>
10
+ #include <jpeglib.h>
11
+
12
+ #include <ruby.h>
13
+ #include <numo/narray.h>
14
+ #include <numo/template.h>
15
+
16
+ void init_io_module();
17
+
18
+ #endif /* MAGRO_IO_H */
data/ext/magro/magro.c CHANGED
@@ -1,403 +1,10 @@
1
1
  #include "magro.h"
2
2
 
3
3
  VALUE mMagro;
4
- VALUE mIO;
5
-
6
- /**
7
- * @!visibility private
8
- */
9
- static
10
- VALUE magro_io_read_png(VALUE self, VALUE filename_)
11
- {
12
- char* filename = StringValuePtr(filename_);
13
- FILE* file_ptr = fopen(filename, "rb");
14
- unsigned char header[8];
15
- png_structp png_ptr;
16
- png_infop info_ptr;
17
- png_bytep* row_ptr_ptr;
18
- png_bytep row_ptr;
19
- png_uint_32 width, height;
20
- int color_type;
21
- int bit_depth;
22
- png_uint_32 y;
23
- int n_dims = 0;
24
- int n_ch;
25
- size_t shape[3] = { 0 };
26
- VALUE nary;
27
- uint8_t* nary_ptr;
28
-
29
- if (file_ptr == NULL) {
30
- return Qnil;
31
- }
32
-
33
- if (fread(header, 1, 8, file_ptr) < 8) {
34
- fclose(file_ptr);
35
- return Qnil;
36
- }
37
-
38
- if (png_sig_cmp(header, 0, 8)) {
39
- fclose(file_ptr);
40
- return Qnil;
41
- }
42
-
43
- png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
44
- if (png_ptr == NULL) {
45
- fclose(file_ptr);
46
- return Qnil;
47
- }
48
- info_ptr = png_create_info_struct(png_ptr);
49
- if (info_ptr == NULL) {
50
- png_destroy_read_struct(&png_ptr, NULL, NULL);
51
- fclose(file_ptr);
52
- return Qnil;
53
- }
54
- if (setjmp(png_jmpbuf(png_ptr))) {
55
- png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
56
- fclose(file_ptr);
57
- return Qnil;
58
- }
59
-
60
- png_init_io(png_ptr, file_ptr);
61
- png_set_sig_bytes(png_ptr, 8);
62
-
63
- png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16, NULL);
64
- row_ptr_ptr = png_get_rows(png_ptr, info_ptr);
65
- png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
66
-
67
- if (color_type == PNG_COLOR_TYPE_PALETTE) {
68
- png_set_palette_to_rgb(png_ptr);
69
- png_read_update_info(png_ptr, info_ptr);
70
- png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
71
- }
72
-
73
- switch (color_type) {
74
- case PNG_COLOR_TYPE_GRAY:
75
- n_ch = 1;
76
- n_dims = 2;
77
- shape[0] = height;
78
- shape[1] = width;
79
- break;
80
- case PNG_COLOR_TYPE_GRAY_ALPHA:
81
- n_ch = 2;
82
- n_dims = 3;
83
- shape[0] = height;
84
- shape[1] = width;
85
- shape[2] = 2;
86
- break;
87
- case PNG_COLOR_TYPE_RGB:
88
- n_ch = 3;
89
- n_dims = 3;
90
- shape[0] = height;
91
- shape[1] = width;
92
- shape[2] = 3;
93
- break;
94
- case PNG_COLOR_TYPE_RGB_ALPHA:
95
- n_ch = 4;
96
- n_dims = 3;
97
- shape[0] = height;
98
- shape[1] = width;
99
- shape[2] = 4;
100
- break;
101
- default:
102
- n_dims = 0;
103
- break;
104
- }
105
-
106
- if (n_dims == 0) {
107
- fclose(file_ptr);
108
- png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
109
- return Qnil;
110
- }
111
-
112
- nary = rb_narray_new(numo_cUInt8, n_dims, shape);
113
- nary_ptr = (uint8_t*)na_get_pointer_for_write(nary);
114
-
115
- for (y = 0; y < height; y++) {
116
- row_ptr = row_ptr_ptr[y];
117
- memcpy(nary_ptr + y * width * n_ch, row_ptr, width * n_ch);
118
- }
119
-
120
- fclose(file_ptr);
121
- png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
122
-
123
- return nary;
124
- }
125
-
126
- /**
127
- * @!visibility private
128
- */
129
- static
130
- VALUE magro_io_save_png(VALUE self, VALUE filename_, VALUE image)
131
- {
132
- char* filename = StringValuePtr(filename_);
133
- FILE* file_ptr = fopen(filename, "wb");
134
- png_structp png_ptr;
135
- png_infop info_ptr;
136
- png_bytep* row_ptr_ptr;
137
- png_uint_32 width, height;
138
- int color_type;
139
- int bit_depth = 8;
140
- png_uint_32 y;
141
- int n_ch;
142
- int n_dims;
143
- narray_t* image_nary;
144
- uint8_t* image_ptr;
145
-
146
- if (CLASS_OF(image) != numo_cUInt8) {
147
- image = rb_funcall(numo_cUInt8, rb_intern("cast"), 1, image);
148
- }
149
- if (!RTEST(nary_check_contiguous(image))) {
150
- image = nary_dup(image);
151
- }
152
-
153
- GetNArray(image, image_nary);
154
- n_dims = NA_NDIM(image_nary);
155
- height = (png_uint_32)NA_SHAPE(image_nary)[0];
156
- width = (png_uint_32)NA_SHAPE(image_nary)[1];
157
- image_ptr = (uint8_t*)na_get_pointer_for_read(image);
158
-
159
- n_ch = 1;
160
- if (n_dims == 3) {
161
- n_ch = (int)NA_SHAPE(image_nary)[2];
162
- }
163
-
164
- switch (n_ch) {
165
- case 4:
166
- color_type = PNG_COLOR_TYPE_RGBA;
167
- break;
168
- case 3:
169
- color_type = PNG_COLOR_TYPE_RGB;
170
- break;
171
- case 2:
172
- color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
173
- break;
174
- default:
175
- color_type = PNG_COLOR_TYPE_GRAY;
176
- break;
177
- }
178
-
179
- if (file_ptr == NULL) {
180
- return Qfalse;
181
- }
182
- png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
183
- if (png_ptr == NULL) {
184
- fclose(file_ptr);
185
- return Qfalse;
186
- }
187
- info_ptr = png_create_info_struct(png_ptr);
188
- if (info_ptr == NULL) {
189
- png_destroy_read_struct(&png_ptr, NULL, NULL);
190
- fclose(file_ptr);
191
- return Qfalse;
192
- }
193
- if (setjmp(png_jmpbuf(png_ptr))) {
194
- png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
195
- fclose(file_ptr);
196
- return Qfalse;
197
- }
198
-
199
- png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type,
200
- PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
201
-
202
- row_ptr_ptr = png_malloc(png_ptr, height * sizeof(png_bytep));
203
- for (y = 0; y < height; y++) {
204
- row_ptr_ptr[y] = png_malloc(png_ptr, width * n_ch * sizeof(png_byte));
205
- memcpy(row_ptr_ptr[y], image_ptr + y * width * n_ch, width * n_ch);
206
- }
207
-
208
- png_init_io(png_ptr, file_ptr);
209
- png_set_rows(png_ptr, info_ptr, row_ptr_ptr);
210
- png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
211
-
212
- fclose(file_ptr);
213
- for (y = 0; y < height; y++) {
214
- png_free(png_ptr, row_ptr_ptr[y]);
215
- }
216
- png_free(png_ptr, row_ptr_ptr);
217
- png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
218
-
219
- return Qtrue;
220
- }
221
-
222
- struct my_error_mgr {
223
- struct jpeg_error_mgr pub;
224
- jmp_buf setjmp_buffer;
225
- };
226
-
227
- static void
228
- my_error_exit(j_common_ptr cinfo)
229
- {
230
- struct my_error_mgr* my_err = (struct my_error_mgr*)cinfo->err;
231
- (*cinfo->err->output_message)(cinfo);
232
- longjmp(my_err->setjmp_buffer, 1);
233
- }
234
-
235
- /**
236
- * @!visibility private
237
- */
238
- static
239
- VALUE magro_io_read_jpg(VALUE self, VALUE filename_)
240
- {
241
- char* filename = StringValuePtr(filename_);
242
- FILE* file_ptr = fopen(filename, "rb");
243
- struct jpeg_decompress_struct jpeg;
244
- struct my_error_mgr err;
245
- unsigned int width, height;
246
- int n_colors;
247
- size_t shape[3] = { 0 };
248
- int n_dims;
249
- unsigned int y;
250
- VALUE nary;
251
- uint8_t* nary_ptr;
252
- JSAMPLE* tmp;
253
-
254
-
255
- if (file_ptr == NULL) {
256
- return Qnil;
257
- }
258
-
259
- jpeg.err = jpeg_std_error(&err.pub);
260
- err.pub.error_exit = my_error_exit;
261
- if (setjmp(err.setjmp_buffer)) {
262
- return Qnil;
263
- }
264
-
265
- jpeg_create_decompress(&jpeg);
266
- jpeg_stdio_src(&jpeg, file_ptr);
267
- jpeg_read_header(&jpeg, TRUE);
268
- jpeg_start_decompress(&jpeg);
269
-
270
- width = jpeg.output_width;
271
- height = jpeg.output_height;
272
- n_colors = jpeg.out_color_components;
273
-
274
- n_dims = n_colors == 1 ? 2 : 3;
275
- shape[0] = height;
276
- shape[1] = width;
277
- shape[2] = n_colors;
278
- nary = rb_narray_new(numo_cUInt8, n_dims, shape);
279
- nary_ptr = (uint8_t*)na_get_pointer_for_write(nary);
280
-
281
- for (y = 0; y < height; y++) {
282
- tmp = nary_ptr + y * width * n_colors;
283
- jpeg_read_scanlines(&jpeg, &tmp, 1);
284
- }
285
-
286
- fclose(file_ptr);
287
- jpeg_finish_decompress(&jpeg);
288
- jpeg_destroy_decompress(&jpeg);
289
-
290
- return nary;
291
- }
292
-
293
- /**
294
- * @!visibility private
295
- */
296
- static
297
- VALUE magro_io_save_jpg(int argc, VALUE* argv, VALUE self)
298
- {
299
- VALUE filename_;
300
- VALUE image;
301
- VALUE quality_;
302
- char* filename;
303
- FILE* file_ptr;
304
- struct jpeg_compress_struct jpeg;
305
- struct my_error_mgr err;
306
- narray_t* image_nary;
307
- int quality;
308
- int n_dims, n_ch;
309
- unsigned int width, height, y;
310
- uint8_t* image_ptr;
311
- JSAMPLE* tmp;
312
-
313
- rb_scan_args(argc, argv, "21", &filename_, &image, &quality_);
314
-
315
- if (NIL_P(quality_)) {
316
- quality = 95;
317
- } else {
318
- quality = NUM2INT(quality_);
319
- }
320
-
321
- filename = StringValuePtr(filename_);
322
-
323
- if (CLASS_OF(image) != numo_cUInt8) {
324
- image = rb_funcall(numo_cUInt8, rb_intern("cast"), 1, image);
325
- }
326
- if (!RTEST(nary_check_contiguous(image))) {
327
- image = nary_dup(image);
328
- }
329
-
330
- jpeg.err = jpeg_std_error(&err.pub);
331
- err.pub.error_exit = my_error_exit;
332
- if (setjmp(err.setjmp_buffer)) {
333
- return Qfalse;
334
- }
335
-
336
- jpeg_create_compress(&jpeg);
337
-
338
- file_ptr = fopen(filename, "wb");
339
- if (file_ptr == NULL) {
340
- jpeg_destroy_compress(&jpeg);
341
- return Qfalse;
342
- }
343
-
344
- GetNArray(image, image_nary);
345
- n_dims = NA_NDIM(image_nary);
346
- height = (unsigned int)NA_SHAPE(image_nary)[0];
347
- width = (unsigned int)NA_SHAPE(image_nary)[1];
348
- image_ptr = (uint8_t*)na_get_pointer_for_read(image);
349
-
350
- n_ch = 1;
351
- if (n_dims == 3) {
352
- n_ch = (int)NA_SHAPE(image_nary)[2];
353
- }
354
-
355
- jpeg_stdio_dest(&jpeg, file_ptr);
356
-
357
- jpeg.image_height = height;
358
- jpeg.image_width = width;
359
- jpeg.input_components = n_ch;
360
-
361
- switch (n_ch) {
362
- case 3:
363
- jpeg.in_color_space = JCS_RGB;
364
- break;
365
- case 1:
366
- jpeg.in_color_space = JCS_GRAYSCALE;
367
- break;
368
- default:
369
- jpeg.in_color_space = JCS_UNKNOWN;
370
- break;
371
- }
372
-
373
- jpeg_set_defaults(&jpeg);
374
-
375
- jpeg_set_quality(&jpeg, quality, TRUE);
376
-
377
- jpeg_start_compress(&jpeg, TRUE);
378
-
379
- for (y = 0; y < height; y++) {
380
- tmp = image_ptr + y * width * n_ch;
381
- jpeg_write_scanlines(&jpeg, &tmp, 1);
382
- }
383
-
384
- jpeg_finish_compress(&jpeg);
385
- jpeg_destroy_compress(&jpeg);
386
-
387
- fclose(file_ptr);
388
-
389
- return Qtrue;
390
- }
391
4
 
392
5
  void Init_magro()
393
6
  {
394
- rb_require("numo/narray");
395
-
396
7
  mMagro = rb_define_module("Magro");
397
8
 
398
- mIO = rb_define_module_under(mMagro, "IO");
399
- rb_define_module_function(mIO, "read_png", magro_io_read_png, 1);
400
- rb_define_module_function(mIO, "save_png", magro_io_save_png, 2);
401
- rb_define_module_function(mIO, "read_jpg", magro_io_read_jpg, 1);
402
- rb_define_module_function(mIO, "save_jpg", magro_io_save_jpg, -1);
9
+ init_io_module();
403
10
  }
data/ext/magro/magro.h CHANGED
@@ -1,15 +1,8 @@
1
1
  #ifndef MAGRO_H
2
2
  #define MAGRO_H 1
3
3
 
4
- #include <math.h>
5
- #include <string.h>
6
-
7
- #include <setjmp.h>
8
- #include <png.h>
9
- #include <jpeglib.h>
10
-
11
4
  #include <ruby.h>
12
- #include <numo/narray.h>
13
- #include <numo/template.h>
5
+
6
+ #include "io.h"
14
7
 
15
8
  #endif /* MAGRO_H */
data/lib/magro/io.rb CHANGED
@@ -9,6 +9,9 @@ module Magro
9
9
  # @param filename [String] Path to image file to be loaded.
10
10
  # Currently, the following file formats are support:
11
11
  # Portbale Network Graphics (*.png) and JPEG files (*.jpeg, *.jpg, *jpe).
12
+ # @raise [ArgumentError] This error is raised when filename is not String.
13
+ # @raise [IOError] This error is raised when failed to read image file.
14
+ # @raise [NoMemoryError] If memory allocation of image data fails, this error is raised.
12
15
  # @return [Numo::UInt8] (shape: [height, width, n_channels]) Loaded image.
13
16
  def imread(filename)
14
17
  raise ArgumentError, 'Expect class of filename to be String.' unless filename.is_a?(String)
@@ -22,6 +25,9 @@ module Magro
22
25
  # Portbale Network Graphics (*.png) and JPEG files (*.jpeg, *.jpg, *jpe).
23
26
  # @param image [Numo::UInt8] (shape: [height, width, n_channels]) Image data to be saved.
24
27
  # @param quality [Integer] Quality parameter of jpeg image that takes a value between 0 to 100.
28
+ # @raise [ArgumentError] If filename is not String or image is not Numo::NArray, this error is raised.
29
+ # @raise [IOError] This error is raised when failed to read image file.
30
+ # @raise [NoMemoryError] If memory allocation of image data fails, this error is raised.
25
31
  # @return [Boolean] true if file save is successful.
26
32
  def imsave(filename, image, quality: nil)
27
33
  raise ArgumentError, 'Expect class of filename to be String.' unless filename.is_a?(String)
data/lib/magro/version.rb CHANGED
@@ -3,5 +3,5 @@
3
3
  # Magro is an image processing library in Ruby.
4
4
  module Magro
5
5
  # The version of Magro you are using.
6
- VERSION = '0.1.0'
6
+ VERSION = '0.1.1'
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: magro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - yoshoku
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-04 00:00:00.000000000 Z
11
+ date: 2019-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: numo-narray
@@ -100,6 +100,8 @@ files:
100
100
  - bin/console
101
101
  - bin/setup
102
102
  - ext/magro/extconf.rb
103
+ - ext/magro/io.c
104
+ - ext/magro/io.h
103
105
  - ext/magro/magro.c
104
106
  - ext/magro/magro.h
105
107
  - lib/magro.rb