magro 0.1.0 → 0.4.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 +5 -5
- data/.coveralls.yml +1 -0
- data/.gitignore +4 -0
- data/.travis.yml +8 -3
- data/CHANGELOG.md +21 -0
- data/Gemfile +7 -1
- data/LICENSE.txt +1 -1
- data/README.md +17 -12
- data/ext/magro/imgrw.c +411 -0
- data/ext/magro/imgrw.h +18 -0
- data/ext/magro/magro.c +1 -394
- data/ext/magro/magro.h +2 -9
- data/lib/magro.rb +2 -0
- data/lib/magro/filter.rb +118 -0
- data/lib/magro/io.rb +10 -4
- data/lib/magro/transform.rb +77 -0
- data/lib/magro/version.rb +1 -1
- data/magro.gemspec +12 -7
- metadata +22 -66
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a8bfcf8b7886c7fa5d89556642be97b077f6bf96d7a1725fd2cb8cf1b15c0aef
|
4
|
+
data.tar.gz: b0ab5bff6959f1264ed584dcefc25fb7a64fc866164f73dcf11b1556e7cf6377
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f40b650588ec98b33aa8d88bd5c0fb11d4dc8b14faaa197751275f478c2c316e7348b5bd1ff4cc55d33e451036b6b6411fc4f60d9d374c787c91d30ff661abc4
|
7
|
+
data.tar.gz: 7df4bfc3e747f2e8d8fb2dad307df3a0b6370fae1b6c53df66797252629851cf57ab256fdcc64088950ce9177ee2d6ca3a694e498588dc3b4baeef17c771d928
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
---
|
2
|
-
sudo: true
|
3
2
|
os: linux
|
4
3
|
dist: bionic
|
5
4
|
language: ruby
|
@@ -8,7 +7,13 @@ rvm:
|
|
8
7
|
- '2.4'
|
9
8
|
- '2.5'
|
10
9
|
- '2.6'
|
10
|
+
- '2.7'
|
11
|
+
|
12
|
+
addons:
|
13
|
+
apt:
|
14
|
+
packages:
|
15
|
+
- libpng-dev
|
16
|
+
- libjpeg-dev
|
11
17
|
|
12
18
|
before_install:
|
13
|
-
-
|
14
|
-
- gem install bundler -v 2.0.2
|
19
|
+
- gem install bundler -v 2.1.4
|
data/CHANGELOG.md
CHANGED
@@ -1,2 +1,23 @@
|
|
1
|
+
# 0.4.0
|
2
|
+
- Rename extension file for reading and writing image file.
|
3
|
+
- Update documentations.
|
4
|
+
|
5
|
+
# 0.3.0
|
6
|
+
- Add [filter module](https://yoshoku.github.io/magro/doc/Magro/Filter.html) consists of image filtering methods.
|
7
|
+
- Change to use round instead of ceil in [quantization of resize method](https://github.com/yoshoku/magro/commit/1b3308ddfb98a650889483af3cd2045aaf6b8837) when given integer image.
|
8
|
+
|
9
|
+
# 0.2.0
|
10
|
+
- Add [transform module](https://yoshoku.github.io/magro/doc/Magro/Transform.html) and resize method.
|
11
|
+
- Fix some configulation files.
|
12
|
+
|
13
|
+
# 0.1.2
|
14
|
+
- Fix bug that fails to read and save file with upper case file extension.
|
15
|
+
|
16
|
+
# 0.1.1
|
17
|
+
- Refactor extension codes.
|
18
|
+
- Fix to raise IOError when occured file reading / writing error.
|
19
|
+
- Fix to raise NoMemoryError when occured memory allocation error.
|
20
|
+
- Several documentation improvements.
|
21
|
+
|
1
22
|
# 0.1.0
|
2
23
|
- First release.
|
data/Gemfile
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in magro.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
gem 'coveralls', '~> 0.8'
|
7
|
+
gem 'bundler', '~> 2.0'
|
8
|
+
gem 'rake', '~> 12.0'
|
9
|
+
gem 'rake-compiler', '~> 1.0'
|
10
|
+
gem 'rspec', '~> 3.0'
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,20 +1,27 @@
|
|
1
1
|
# Magro
|
2
2
|
|
3
3
|
[](https://travis-ci.org/yoshoku/magro)
|
4
|
+
[](https://coveralls.io/github/yoshoku/magro?branch=master)
|
5
|
+
[](https://badge.fury.io/rb/magro)
|
4
6
|
[](https://github.com/yoshoku/numo-liblinear/blob/master/LICENSE.txt)
|
7
|
+
[](https://yoshoku.github.io/magro/doc/)
|
5
8
|
|
6
|
-
Magro is
|
7
|
-
Magro uses [Numo::NArray](https://github.com/ruby-numo/numo-narray) arrays as image objects
|
9
|
+
Magro is a minimal image processing library in Ruby.
|
10
|
+
Magro uses [Numo::NArray](https://github.com/ruby-numo/numo-narray) arrays as image objects and
|
11
|
+
provides basic image processing functions.
|
12
|
+
Current supporting features are reading and writing JPEG and PNG images,
|
13
|
+
image resizing with bilinear interpolation method, and image filtering.
|
8
14
|
|
9
15
|
## Installation
|
10
16
|
|
11
|
-
Magro dependents
|
17
|
+
Magro dependents libpng and libjpeg to provides functions reading and writing image file.
|
18
|
+
Moreover, it is recommended that using libpng version 1.6 or later.
|
12
19
|
|
13
20
|
macOS:
|
14
21
|
|
15
22
|
$ brew install libpng libjpeg
|
16
23
|
|
17
|
-
Ubuntu:
|
24
|
+
Ubuntu (bionic):
|
18
25
|
|
19
26
|
$ sudo apt-get install libpng-dev libjpeg-dev
|
20
27
|
|
@@ -32,25 +39,23 @@ Or install it yourself as:
|
|
32
39
|
|
33
40
|
$ gem install magro
|
34
41
|
|
42
|
+
## Documentation
|
43
|
+
|
44
|
+
- [Magro API Documentation](https://yoshoku.github.io/magro/doc/)
|
45
|
+
|
35
46
|
## Usage
|
36
47
|
|
37
48
|
```ruby
|
38
49
|
> require 'magro'
|
39
50
|
=> true
|
40
|
-
> image = Magro::IO.imread('
|
51
|
+
> image = Magro::IO.imread('foo.png')
|
41
52
|
=> Numo::UInt8#shape=[512,512,3]
|
42
53
|
> grayscale = image.median(axis: 2)
|
43
54
|
=> Numo::UInt8#shape=[512,512]
|
44
|
-
> Magro::IO.imsave('
|
55
|
+
> Magro::IO.imsave('foo_gray.png', grayscale)
|
45
56
|
=> true
|
46
57
|
```
|
47
58
|
|
48
|
-
## Development
|
49
|
-
|
50
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
51
|
-
|
52
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
53
|
-
|
54
59
|
## Contributing
|
55
60
|
|
56
61
|
Bug reports and pull requests are welcome on GitHub at https://github.com/yoshoku/magro.
|
data/ext/magro/imgrw.c
ADDED
@@ -0,0 +1,411 @@
|
|
1
|
+
#include "imgrw.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
|
+
}
|