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 +4 -4
- data/.gitignore +2 -0
- data/CHANGELOG.md +6 -0
- data/README.md +2 -0
- data/ext/magro/io.c +411 -0
- data/ext/magro/io.h +18 -0
- data/ext/magro/magro.c +1 -394
- data/ext/magro/magro.h +2 -9
- data/lib/magro/io.rb +6 -0
- data/lib/magro/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32deda4c751be24b856a25e98b44e15d66838fb4
|
4
|
+
data.tar.gz: 9d7c6693670cb4c1e7959ff8b11f936d043419d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e61aee7251656d90966217242280f76699ded1c81f291b7aacf24ff70c55e5bc897ee551df112fb3f1a992dd0571200b0bd85984adf89aadb43e13b49c5236bf
|
7
|
+
data.tar.gz: b6560a03645922b6a0cc2cef2c476773f9f7b1bc0ccd41b45ed3e29179066f037329f4075d9465e7c4826f81dd8cfdd60bb3f30f01571e2fb543582ce2cebdda
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# Magro
|
2
2
|
|
3
3
|
[](https://travis-ci.org/yoshoku/magro)
|
4
|
+
[](https://badge.fury.io/rb/magro)
|
4
5
|
[](https://github.com/yoshoku/numo-liblinear/blob/master/LICENSE.txt)
|
6
|
+
[](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
|
-
|
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
|
-
|
13
|
-
#include
|
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
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.
|
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-
|
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
|