magro 0.4.2 → 0.6.0

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
  SHA256:
3
- metadata.gz: 1017c6415c59c2229b1acc8578eb7ce8bb3eb6e114f8b97089beac2c8d9805ab
4
- data.tar.gz: 55ce42e8bb8fa6c98f586d3d54315e76a3d7a95ffc232c08b6bc7717ff4131a9
3
+ metadata.gz: c1c544837f96597012ff6cf938a4148b7fcc640237daf460ae26c64f91588767
4
+ data.tar.gz: '08915ee5d2d4ed4b535963684b160e2ab4707d7823b56578f5574099ca33d7ad'
5
5
  SHA512:
6
- metadata.gz: 6a59aeddd2b9f6f8a469fbae9c89313a163370b04c83dfd82a31d6cc770b4b313e7c9dfa88bcc620972dec7f8e899907c874623dc5dcaf6b31d9cd3573f89503
7
- data.tar.gz: 05c4091e37f4cba235f4f05e069ea886be7f9e687a2609cfe6ebbbd60d2ab1d4533eaee3461611aaf9113b6baa7884924be5de9e37868490c38591c9b9c9de6b
6
+ metadata.gz: 4d5cd134c942319d0ecb83fb39b89cf8a42c3cacc9c4336eaf592b0170a4257e597ce5c62e4fdb35b4641f2446e206c79da4b012c009516547ff7ae464745975
7
+ data.tar.gz: cd6cdb5059adf3ed20ea6b9625d314f0519c338b0f01b8abb1ea73ecb8c5cb183b0288e1cdbdb22482d8fb71945b26f4761911c606bb81ce62008cb3ee984742
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ # 0.6.0
2
+ - Reorganize native exntesion codes.
3
+ - Remove dependent gem's type declaration file from installation files.
4
+ - Update documentations.
5
+ - Introduce conventional commits.
6
+
7
+ # 0.5.1
8
+ - Fix version specifier of runtime dependencies.
9
+
10
+ # 0.5.0
11
+ - Add type declaration files.
12
+ - Refactor some codes.
13
+
14
+ # 0.4.3
15
+ - Fix imread method to read image from URL.
16
+ - Fix some configulation files.
17
+
1
18
  # 0.4.2
2
19
  - Add GC guard to reading and writing image methods.
3
20
 
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2019-2021 Atsushi Tatsuma
1
+ Copyright (c) 2019-2022 Atsushi Tatsuma
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without
data/README.md CHANGED
@@ -61,6 +61,6 @@ Or install it yourself as:
61
61
  Bug reports and pull requests are welcome on GitHub at https://github.com/yoshoku/magro.
62
62
  This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
63
63
 
64
- ## Code of Conduct
64
+ ## License
65
65
 
66
- Everyone interacting in the Magro project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/yoshoku/magro/blob/main/CODE_OF_CONDUCT.md).
66
+ The gem is available as open source under the terms of the [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause).
data/ext/magro/extconf.rb CHANGED
@@ -8,10 +8,7 @@ $LOAD_PATH.each do |lp|
8
8
  end
9
9
  end
10
10
 
11
- unless have_header('numo/narray.h')
12
- puts 'numo/narray.h not found.'
13
- exit(1)
14
- end
11
+ abort 'numo/narray.h not found.' unless have_header('numo/narray.h')
15
12
 
16
13
  if RUBY_PLATFORM =~ /mswin|cygwin|mingw/
17
14
  $LOAD_PATH.each do |lp|
@@ -20,35 +17,14 @@ if RUBY_PLATFORM =~ /mswin|cygwin|mingw/
20
17
  break
21
18
  end
22
19
  end
23
- unless have_library('narray', 'nary_new')
24
- puts 'libnarray.a not found.'
25
- exit(1)
26
- end
27
- end
28
-
29
- unless have_header('setjmp.h')
30
- puts 'setjmp.h not found.'
31
- exit(1)
32
- end
33
-
34
- unless have_header('png.h')
35
- puts 'png.h not found.'
36
- exit(1)
20
+ abort 'libnarray.a not found.' unless have_library('narray', 'nary_new')
37
21
  end
38
22
 
39
- unless have_header('jpeglib.h')
40
- puts 'jpeglib.h not found.'
41
- exit(1)
42
- end
23
+ abort 'setjmp.h not found.' unless have_header('setjmp.h')
24
+ abort 'png.h not found.' unless have_header('png.h')
25
+ abort 'libpng not found.' unless have_library('png')
43
26
 
44
- unless have_library('png')
45
- puts 'libpng not found.'
46
- exit(1)
47
- end
48
-
49
- unless have_library('jpeg')
50
- puts 'libjpeg not found.'
51
- exit(1)
52
- end
27
+ abort 'jpeglib.h not found.' unless have_header('jpeglib.h')
28
+ abort 'libjpeg not found.' unless have_library('jpeg')
53
29
 
54
30
  create_makefile('magro/magro')
data/ext/magro/magro.c CHANGED
@@ -1,10 +1,462 @@
1
+ /**
2
+ * Copyright (c) 2019-2022 Atsushi Tatsuma
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * * Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * * Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * * Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
1
31
  #include "magro.h"
2
32
 
3
33
  VALUE mMagro;
34
+ VALUE mMagroIO;
4
35
 
5
- void Init_magro()
6
- {
7
- mMagro = rb_define_module("Magro");
36
+ static VALUE magro_io_read_png(VALUE self, VALUE filename_) {
37
+ char* filename = StringValuePtr(filename_);
38
+ FILE* file_ptr = fopen(filename, "rb");
39
+ unsigned char header[8];
40
+ png_structp png_ptr;
41
+ png_infop info_ptr;
42
+ png_bytep* row_ptr_ptr;
43
+ png_bytep row_ptr;
44
+ png_uint_32 width, height;
45
+ int color_type;
46
+ int bit_depth;
47
+ png_uint_32 y;
48
+ int n_dims = 0;
49
+ int n_ch;
50
+ size_t shape[3] = {0};
51
+ VALUE nary;
52
+ uint8_t* nary_ptr;
53
+
54
+ if (file_ptr == NULL) {
55
+ rb_raise(rb_eIOError, "Failed to open file '%s'", filename);
56
+ return Qnil;
57
+ }
58
+
59
+ if (fread(header, 1, 8, file_ptr) < 8) {
60
+ fclose(file_ptr);
61
+ rb_raise(rb_eIOError, "Failed to read header info '%s'", filename);
62
+ return Qnil;
63
+ }
64
+
65
+ if (png_sig_cmp(header, 0, 8)) {
66
+ fclose(file_ptr);
67
+ rb_raise(rb_eIOError, "Failed to read header info '%s'", filename);
68
+ return Qnil;
69
+ }
70
+
71
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
72
+ if (png_ptr == NULL) {
73
+ fclose(file_ptr);
74
+ rb_raise(rb_eNoMemError, "Failed to allocate memory.");
75
+ return Qnil;
76
+ }
77
+ info_ptr = png_create_info_struct(png_ptr);
78
+ if (info_ptr == NULL) {
79
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
80
+ fclose(file_ptr);
81
+ rb_raise(rb_eNoMemError, "Failed to allocate memory.");
82
+ return Qnil;
83
+ }
84
+ if (setjmp(png_jmpbuf(png_ptr))) {
85
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
86
+ fclose(file_ptr);
87
+ rb_raise(rb_eIOError, "Error happened while reading file '%s'", filename);
88
+ return Qnil;
89
+ }
90
+
91
+ png_init_io(png_ptr, file_ptr);
92
+ png_set_sig_bytes(png_ptr, 8);
93
+
94
+ png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16, NULL);
95
+ row_ptr_ptr = png_get_rows(png_ptr, info_ptr);
96
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
97
+
98
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
99
+ png_set_palette_to_rgb(png_ptr);
100
+ png_read_update_info(png_ptr, info_ptr);
101
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
102
+ }
103
+
104
+ switch (color_type) {
105
+ case PNG_COLOR_TYPE_GRAY:
106
+ n_ch = 1;
107
+ n_dims = 2;
108
+ shape[0] = height;
109
+ shape[1] = width;
110
+ break;
111
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
112
+ n_ch = 2;
113
+ n_dims = 3;
114
+ shape[0] = height;
115
+ shape[1] = width;
116
+ shape[2] = 2;
117
+ break;
118
+ case PNG_COLOR_TYPE_RGB:
119
+ n_ch = 3;
120
+ n_dims = 3;
121
+ shape[0] = height;
122
+ shape[1] = width;
123
+ shape[2] = 3;
124
+ break;
125
+ case PNG_COLOR_TYPE_RGB_ALPHA:
126
+ n_ch = 4;
127
+ n_dims = 3;
128
+ shape[0] = height;
129
+ shape[1] = width;
130
+ shape[2] = 4;
131
+ break;
132
+ default:
133
+ n_dims = 0;
134
+ break;
135
+ }
136
+
137
+ if (n_dims == 0) {
138
+ fclose(file_ptr);
139
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
140
+ rb_raise(rb_eIOError, "Unsupported color type of input file '%s'", filename);
141
+ return Qnil;
142
+ }
143
+
144
+ nary = rb_narray_new(numo_cUInt8, n_dims, shape);
145
+ nary_ptr = (uint8_t*)na_get_pointer_for_write(nary);
146
+
147
+ for (y = 0; y < height; y++) {
148
+ row_ptr = row_ptr_ptr[y];
149
+ memcpy(nary_ptr + y * width * n_ch, row_ptr, width * n_ch);
150
+ }
151
+
152
+ fclose(file_ptr);
153
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
154
+
155
+ RB_GC_GUARD(filename_);
156
+
157
+ return nary;
158
+ }
159
+
160
+ static VALUE magro_io_save_png(VALUE self, VALUE filename_, VALUE image) {
161
+ char* filename = StringValuePtr(filename_);
162
+ FILE* file_ptr = fopen(filename, "wb");
163
+ png_structp png_ptr;
164
+ png_infop info_ptr;
165
+ png_bytep* row_ptr_ptr;
166
+ png_uint_32 width, height;
167
+ int color_type;
168
+ int bit_depth = 8;
169
+ png_uint_32 y;
170
+ int n_ch;
171
+ int n_dims;
172
+ narray_t* image_nary;
173
+ uint8_t* image_ptr;
174
+
175
+ if (file_ptr == NULL) {
176
+ rb_raise(rb_eIOError, "Failed to open file '%s'", filename);
177
+ return Qfalse;
178
+ }
179
+
180
+ if (CLASS_OF(image) != numo_cUInt8) image = rb_funcall(numo_cUInt8, rb_intern("cast"), 1, image);
181
+ if (!RTEST(nary_check_contiguous(image))) image = nary_dup(image);
182
+
183
+ GetNArray(image, image_nary);
184
+ n_dims = NA_NDIM(image_nary);
185
+ height = (png_uint_32)NA_SHAPE(image_nary)[0];
186
+ width = (png_uint_32)NA_SHAPE(image_nary)[1];
187
+ image_ptr = (uint8_t*)na_get_pointer_for_read(image);
188
+ n_ch = n_dims == 3 ? (int)NA_SHAPE(image_nary)[2] : 1;
189
+
190
+ switch (n_ch) {
191
+ case 4:
192
+ color_type = PNG_COLOR_TYPE_RGBA;
193
+ break;
194
+ case 3:
195
+ color_type = PNG_COLOR_TYPE_RGB;
196
+ break;
197
+ case 2:
198
+ color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
199
+ break;
200
+ default:
201
+ color_type = PNG_COLOR_TYPE_GRAY;
202
+ break;
203
+ }
204
+
205
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
206
+ if (png_ptr == NULL) {
207
+ fclose(file_ptr);
208
+ rb_raise(rb_eNoMemError, "Failed to allocate memory.");
209
+ return Qfalse;
210
+ }
211
+ info_ptr = png_create_info_struct(png_ptr);
212
+ if (info_ptr == NULL) {
213
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
214
+ fclose(file_ptr);
215
+ rb_raise(rb_eNoMemError, "Failed to allocate memory.");
216
+ return Qfalse;
217
+ }
218
+ if (setjmp(png_jmpbuf(png_ptr))) {
219
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
220
+ fclose(file_ptr);
221
+ return Qfalse;
222
+ }
223
+
224
+ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
225
+ PNG_FILTER_TYPE_DEFAULT);
226
+
227
+ row_ptr_ptr = png_malloc(png_ptr, height * sizeof(png_bytep));
228
+ for (y = 0; y < height; y++) {
229
+ row_ptr_ptr[y] = png_malloc(png_ptr, width * n_ch * sizeof(png_byte));
230
+ memcpy(row_ptr_ptr[y], image_ptr + y * width * n_ch, width * n_ch);
231
+ }
232
+
233
+ png_init_io(png_ptr, file_ptr);
234
+ png_set_rows(png_ptr, info_ptr, row_ptr_ptr);
235
+ png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
236
+
237
+ fclose(file_ptr);
238
+ for (y = 0; y < height; y++) png_free(png_ptr, row_ptr_ptr[y]);
239
+ png_free(png_ptr, row_ptr_ptr);
240
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
241
+
242
+ RB_GC_GUARD(image);
243
+ RB_GC_GUARD(filename_);
244
+
245
+ return Qtrue;
246
+ }
247
+
248
+ struct my_error_mgr {
249
+ struct jpeg_error_mgr pub;
250
+ jmp_buf setjmp_buffer;
251
+ };
252
+
253
+ static void my_error_exit(j_common_ptr cinfo) {
254
+ struct my_error_mgr* my_err = (struct my_error_mgr*)cinfo->err;
255
+ (*cinfo->err->output_message)(cinfo);
256
+ longjmp(my_err->setjmp_buffer, 1);
257
+ }
8
258
 
9
- init_io_module();
259
+ static VALUE magro_io_read_jpg(VALUE self, VALUE filename_) {
260
+ char* filename = StringValuePtr(filename_);
261
+ FILE* file_ptr = fopen(filename, "rb");
262
+ struct jpeg_decompress_struct jpeg;
263
+ struct my_error_mgr err;
264
+ unsigned int width, height;
265
+ int n_colors;
266
+ size_t shape[3] = {0};
267
+ int n_dims;
268
+ unsigned int y;
269
+ VALUE nary;
270
+ uint8_t* nary_ptr;
271
+ JSAMPLE* tmp;
272
+
273
+ if (file_ptr == NULL) {
274
+ rb_raise(rb_eIOError, "Failed to open file '%s'", filename);
275
+ return Qnil;
276
+ }
277
+
278
+ jpeg.err = jpeg_std_error(&err.pub);
279
+ err.pub.error_exit = my_error_exit;
280
+ if (setjmp(err.setjmp_buffer)) {
281
+ rb_raise(rb_eIOError, "Error happened while reading file '%s'", filename);
282
+ return Qnil;
283
+ }
284
+
285
+ jpeg_create_decompress(&jpeg);
286
+ jpeg_stdio_src(&jpeg, file_ptr);
287
+ jpeg_read_header(&jpeg, TRUE);
288
+ jpeg_start_decompress(&jpeg);
289
+
290
+ width = jpeg.output_width;
291
+ height = jpeg.output_height;
292
+ n_colors = jpeg.out_color_components;
293
+
294
+ n_dims = n_colors == 1 ? 2 : 3;
295
+ shape[0] = height;
296
+ shape[1] = width;
297
+ shape[2] = n_colors;
298
+ nary = rb_narray_new(numo_cUInt8, n_dims, shape);
299
+ nary_ptr = (uint8_t*)na_get_pointer_for_write(nary);
300
+
301
+ for (y = 0; y < height; y++) {
302
+ tmp = nary_ptr + y * width * n_colors;
303
+ jpeg_read_scanlines(&jpeg, &tmp, 1);
304
+ }
305
+
306
+ fclose(file_ptr);
307
+ jpeg_finish_decompress(&jpeg);
308
+ jpeg_destroy_decompress(&jpeg);
309
+
310
+ RB_GC_GUARD(filename_);
311
+
312
+ return nary;
313
+ }
314
+
315
+ static VALUE magro_io_save_jpg(int argc, VALUE* argv, VALUE self) {
316
+ VALUE filename_;
317
+ VALUE image;
318
+ VALUE quality_;
319
+ char* filename;
320
+ FILE* file_ptr;
321
+ struct jpeg_compress_struct jpeg;
322
+ struct my_error_mgr err;
323
+ narray_t* image_nary;
324
+ int quality;
325
+ int n_dims, n_ch;
326
+ unsigned int width, height, y;
327
+ uint8_t* image_ptr;
328
+ JSAMPLE* tmp;
329
+
330
+ rb_scan_args(argc, argv, "21", &filename_, &image, &quality_);
331
+
332
+ quality = NIL_P(quality_) ? 95 : NUM2INT(quality_);
333
+
334
+ filename = StringValuePtr(filename_);
335
+
336
+ if (CLASS_OF(image) != numo_cUInt8) image = rb_funcall(numo_cUInt8, rb_intern("cast"), 1, image);
337
+ if (!RTEST(nary_check_contiguous(image))) image = nary_dup(image);
338
+
339
+ jpeg.err = jpeg_std_error(&err.pub);
340
+ err.pub.error_exit = my_error_exit;
341
+ if (setjmp(err.setjmp_buffer)) return Qfalse;
342
+
343
+ jpeg_create_compress(&jpeg);
344
+
345
+ file_ptr = fopen(filename, "wb");
346
+ if (file_ptr == NULL) {
347
+ rb_raise(rb_eIOError, "Failed to open file '%s'", filename);
348
+ jpeg_destroy_compress(&jpeg);
349
+ return Qfalse;
350
+ }
351
+
352
+ GetNArray(image, image_nary);
353
+ n_dims = NA_NDIM(image_nary);
354
+ height = (unsigned int)NA_SHAPE(image_nary)[0];
355
+ width = (unsigned int)NA_SHAPE(image_nary)[1];
356
+ image_ptr = (uint8_t*)na_get_pointer_for_read(image);
357
+ n_ch = n_dims == 3 ? (int)NA_SHAPE(image_nary)[2] : 1;
358
+
359
+ jpeg_stdio_dest(&jpeg, file_ptr);
360
+
361
+ jpeg.image_height = height;
362
+ jpeg.image_width = width;
363
+ jpeg.input_components = n_ch;
364
+
365
+ switch (n_ch) {
366
+ case 3:
367
+ jpeg.in_color_space = JCS_RGB;
368
+ break;
369
+ case 1:
370
+ jpeg.in_color_space = JCS_GRAYSCALE;
371
+ break;
372
+ default:
373
+ jpeg.in_color_space = JCS_UNKNOWN;
374
+ break;
375
+ }
376
+
377
+ jpeg_set_defaults(&jpeg);
378
+
379
+ jpeg_set_quality(&jpeg, quality, TRUE);
380
+
381
+ jpeg_start_compress(&jpeg, TRUE);
382
+
383
+ for (y = 0; y < height; y++) {
384
+ tmp = image_ptr + y * width * n_ch;
385
+ jpeg_write_scanlines(&jpeg, &tmp, 1);
386
+ }
387
+
388
+ jpeg_finish_compress(&jpeg);
389
+ jpeg_destroy_compress(&jpeg);
390
+
391
+ fclose(file_ptr);
392
+
393
+ RB_GC_GUARD(image);
394
+ RB_GC_GUARD(filename_);
395
+
396
+ return Qtrue;
397
+ }
398
+
399
+ void Init_magro() {
400
+ /**
401
+ * Document-module: Magro
402
+ * Magro is an image processing library in Ruby.
403
+ */
404
+ mMagro = rb_define_module("Magro");
405
+ /**
406
+ * Document-module: Magro::IO
407
+ * IO module provides functions for input and output of image file.
408
+ */
409
+ mMagroIO = rb_define_module_under(mMagro, "IO");
410
+ /**
411
+ * @!visibility private
412
+ *
413
+ * Loads PNG image file.
414
+ *
415
+ * @overload read_png(filename) -> Numo::UInt8
416
+ * @param filename [String] File path of PNG image file.
417
+ *
418
+ * @raise [IOError] This error is raised when failed to read image file.
419
+ * @raise [NoMemoryError] If memory allocation of image data fails, this error is raised.
420
+ * @return [Numo::UInt8] (shape: [height, width, n_channels]) Loaded image.
421
+ */
422
+ rb_define_module_function(mMagroIO, "read_png", RUBY_METHOD_FUNC(magro_io_read_png), 1);
423
+ /**
424
+ * @!visibility private
425
+ *
426
+ * Saves PNG image file.
427
+ *
428
+ * @overload save_png(filename, image) -> Boolean
429
+ * @param filename [String] Path to image file to be saved.
430
+ * @param image [Numo::UInt8] (shape: [height, width, n_channels]) Image data to be saved.
431
+ *
432
+ * @raise [IOError] This error is raised when failed to write image file.
433
+ * @raise [NoMemoryError] If memory allocation of image data fails, this error is raised.
434
+ * @return [Boolean] true if file save is successful.
435
+ */
436
+ rb_define_module_function(mMagroIO, "save_png", RUBY_METHOD_FUNC(magro_io_save_png), 2);
437
+ /**
438
+ * @!visibility private
439
+ *
440
+ * Loads JPEG image file.
441
+ *
442
+ * @overload read_jpg(filename) -> Numo::UInt8
443
+ * @param filename [String] File path of JPEG image file.
444
+ *
445
+ * @raise [IOError] This error is raised when failed to read image file.
446
+ * @return [Numo::UInt8] (shape: [height, width, n_channels]) Loaded image.
447
+ */
448
+ rb_define_module_function(mMagroIO, "read_jpg", RUBY_METHOD_FUNC(magro_io_read_jpg), 1);
449
+ /**
450
+ * @!visibility private
451
+ *
452
+ * Saves JPEG image file.
453
+ *
454
+ * @overload save_jpg(filename, image, quality) -> Boolean
455
+ * @param filename [String] Path to image file to be saved.
456
+ * @param image [Numo::UInt8] (shape: [height, width, n_channels]) Image data to be saved.
457
+ * @param quality [Integer/Nil] Quality parameter of jpeg image that takes a value between 0 to 100.
458
+ *
459
+ * @return [Boolean] true if file save is successful.
460
+ */
461
+ rb_define_module_function(mMagroIO, "save_jpg", RUBY_METHOD_FUNC(magro_io_save_jpg), -1);
10
462
  }
data/ext/magro/magro.h CHANGED
@@ -1,8 +1,48 @@
1
+ /**
2
+ * Copyright (c) 2019-2022 Atsushi Tatsuma
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * * Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * * Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * * Neither the name of the copyright holder nor the names of its
16
+ * contributors may be used to endorse or promote products derived from
17
+ * this software without specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
1
31
  #ifndef MAGRO_H
2
32
  #define MAGRO_H 1
3
33
 
34
+ #include <stdio.h>
35
+ #include <stdlib.h>
36
+ #include <string.h>
37
+
38
+ #include <setjmp.h>
39
+
40
+ #include <jpeglib.h>
41
+ #include <png.h>
42
+
4
43
  #include <ruby.h>
5
44
 
6
- #include "imgrw.h"
45
+ #include <numo/narray.h>
46
+ #include <numo/template.h>
7
47
 
8
48
  #endif /* MAGRO_H */
data/lib/magro/filter.rb CHANGED
@@ -32,7 +32,7 @@ module Magro
32
32
  if n_channels.nil?
33
33
  filter1ch(padded, kernel, scale, offset)
34
34
  else
35
- image.class.zeros(*image.shape).tap do |filtered|
35
+ image.class.zeros(image.shape).tap do |filtered|
36
36
  n_channels.times do |c|
37
37
  filtered[true, true, c] = filter1ch(padded[true, true, c], kernel, scale, offset)
38
38
  end
data/lib/magro/io.rb CHANGED
@@ -1,12 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'open-uri'
4
+ require 'tempfile'
5
+
3
6
  module Magro
4
7
  # IO module provides functions for input and output of image file.
5
8
  module IO
6
9
  module_function
7
10
 
8
11
  # Loads an image from file.
9
- # @param filename [String] Path to image file to be loaded.
12
+ # @param filename [String] File path or URL of image file to be loaded.
10
13
  # Currently, the following file formats are support:
11
14
  # Portbale Network Graphics (*.png) and JPEG files (*.jpeg, *.jpg, *jpe).
12
15
  # @raise [ArgumentError] This error is raised when filename is not String.
@@ -15,8 +18,27 @@ module Magro
15
18
  # @return [Numo::UInt8] (shape: [height, width, n_channels]) Loaded image.
16
19
  def imread(filename)
17
20
  raise ArgumentError, 'Expect class of filename to be String.' unless filename.is_a?(String)
18
- return read_jpg(filename) if filename.downcase =~ /\.(jpeg|jpg|jpe)$/
19
- return read_png(filename) if filename.downcase =~ /\.png$/
21
+
22
+ unless url?(filename)
23
+ return case filename.downcase
24
+ when /\.(jpeg|jpg|jpe)$/
25
+ read_jpg(filename)
26
+ when /\.png$/
27
+ read_png(filename)
28
+ end
29
+ end
30
+
31
+ uri = URI.parse(filename)
32
+ ext = File.extname(uri.path).downcase
33
+ raise IOError, 'Failed to detect file extension from given URL.' unless ext =~ /\.(jpeg|jpg|jpe|png)$/
34
+
35
+ uri.open do |file|
36
+ temp = Tempfile.new(['magro_', ext])
37
+ temp.binmode
38
+ temp.write(file.read)
39
+ temp.rewind
40
+ imread(temp.path)
41
+ end
20
42
  end
21
43
 
22
44
  # Saves an image to file.
@@ -26,7 +48,7 @@ module Magro
26
48
  # @param image [Numo::UInt8] (shape: [height, width, n_channels]) Image data to be saved.
27
49
  # @param quality [Integer] Quality parameter of jpeg image that takes a value between 0 to 100.
28
50
  # @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.
51
+ # @raise [IOError] This error is raised when failed to write image file.
30
52
  # @raise [NoMemoryError] If memory allocation of image data fails, this error is raised.
31
53
  # @return [Boolean] true if file save is successful.
32
54
  def imsave(filename, image, quality: nil)
@@ -46,6 +68,11 @@ module Magro
46
68
  false
47
69
  end
48
70
 
49
- private_class_method :read_jpg, :read_png, :save_jpg, :save_png
71
+ def url?(str)
72
+ uri = URI.parse(str)
73
+ uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
74
+ end
75
+
76
+ private_class_method :read_jpg, :read_png, :save_jpg, :save_png, :url?
50
77
  end
51
78
  end