magro 0.1.0 → 0.1.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.
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