axon 0.0.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.
- data/.gemtest +0 -0
- data/CHANGELOG.rdoc +3 -0
- data/README.rdoc +104 -0
- data/Rakefile +30 -0
- data/TODO.rdoc +12 -0
- data/ext/axon/axon.c +20 -0
- data/ext/axon/bilinear_interpolation.c +115 -0
- data/ext/axon/extconf.rb +21 -0
- data/ext/axon/iccjpeg.c +248 -0
- data/ext/axon/iccjpeg.h +73 -0
- data/ext/axon/interpolation.h +7 -0
- data/ext/axon/jpeg_common.c +118 -0
- data/ext/axon/jpeg_common.h +37 -0
- data/ext/axon/jpeg_native_writer.c +248 -0
- data/ext/axon/jpeg_reader.c +774 -0
- data/ext/axon/nearest_neighbor_interpolation.c +50 -0
- data/ext/axon/png_common.c +21 -0
- data/ext/axon/png_common.h +18 -0
- data/ext/axon/png_native_writer.c +166 -0
- data/ext/axon/png_reader.c +381 -0
- data/lib/axon/axon.so +0 -0
- data/lib/axon/bilinear_scaler.rb +60 -0
- data/lib/axon/cropper.rb +35 -0
- data/lib/axon/fit.rb +67 -0
- data/lib/axon/jpeg_writer.rb +41 -0
- data/lib/axon/nearest_neighbor_scaler.rb +39 -0
- data/lib/axon/png_writer.rb +35 -0
- data/lib/axon/scaler.rb +41 -0
- data/lib/axon/solid.rb +23 -0
- data/lib/axon.rb +45 -0
- data/test/_test_readme.rb +34 -0
- data/test/helper.rb +17 -0
- data/test/reader_tests.rb +115 -0
- data/test/stress_tests.rb +71 -0
- data/test/test_bilinear_scaler.rb +9 -0
- data/test/test_cropper.rb +9 -0
- data/test/test_exif.rb +39 -0
- data/test/test_generator.rb +10 -0
- data/test/test_icc.rb +18 -0
- data/test/test_jpeg.rb +9 -0
- data/test/test_jpeg_reader.rb +109 -0
- data/test/test_jpeg_writer.rb +26 -0
- data/test/test_nearest_neighbor_scaler.rb +13 -0
- data/test/test_png.rb +9 -0
- data/test/test_png_reader.rb +15 -0
- data/test/test_png_writer.rb +13 -0
- data/test/writer_tests.rb +179 -0
- metadata +148 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
|
3
|
+
static ID id_image, id_components, id_width_ratio, id_width;
|
4
|
+
|
5
|
+
/*
|
6
|
+
* call-seq:
|
7
|
+
* image.interpolate_scanline(original_scanlines, q) -> interpolated_scanline
|
8
|
+
*/
|
9
|
+
static VALUE
|
10
|
+
interpolate_scanline(VALUE self, VALUE rb_scanline)
|
11
|
+
{
|
12
|
+
VALUE rb_components, rb_width_ratio, rb_dest_sl, rb_image, rb_width;
|
13
|
+
double width_ratio;
|
14
|
+
unsigned char *scanline, *dest_sl;
|
15
|
+
size_t width, components, i, j;
|
16
|
+
|
17
|
+
rb_width = rb_ivar_get(self, id_width);
|
18
|
+
width = FIX2INT(rb_width);
|
19
|
+
|
20
|
+
rb_image = rb_ivar_get(self, id_image);
|
21
|
+
rb_components = rb_funcall(rb_image, id_components, 0);
|
22
|
+
components = FIX2INT(rb_components);
|
23
|
+
|
24
|
+
rb_width_ratio = rb_ivar_get(self, id_width_ratio);
|
25
|
+
width_ratio = NUM2DBL(rb_width_ratio);
|
26
|
+
|
27
|
+
scanline = RSTRING_PTR(rb_scanline);
|
28
|
+
|
29
|
+
rb_dest_sl = rb_str_new(0, width * components);
|
30
|
+
dest_sl = RSTRING_PTR(rb_dest_sl);
|
31
|
+
|
32
|
+
for (i = 0; i < width; i++)
|
33
|
+
for (j = 0; j < components; j++)
|
34
|
+
dest_sl[i * components + j] = scanline[(int)(i / width_ratio)];
|
35
|
+
|
36
|
+
return rb_dest_sl;
|
37
|
+
}
|
38
|
+
|
39
|
+
void
|
40
|
+
Init_nearest_neighbor_interpolation()
|
41
|
+
{
|
42
|
+
VALUE mAxon = rb_define_module("Axon");
|
43
|
+
VALUE mNearestNeighborScaling = rb_define_module_under(mAxon, "NearestNeighborScaling");
|
44
|
+
rb_define_method(mNearestNeighborScaling, "interpolate_scanline", interpolate_scanline, 1);
|
45
|
+
|
46
|
+
id_width_ratio = rb_intern("@width_ratio");
|
47
|
+
id_image = rb_intern("@image");
|
48
|
+
id_components = rb_intern("components");
|
49
|
+
id_width = rb_intern("@width");
|
50
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#include "png_common.h"
|
2
|
+
|
3
|
+
void
|
4
|
+
png_error_fn(png_structp png_ptr, png_const_charp message)
|
5
|
+
{
|
6
|
+
rb_raise(rb_eRuntimeError, "pnglib: %s", message);
|
7
|
+
}
|
8
|
+
|
9
|
+
void
|
10
|
+
png_warning_fn(png_structp png_ptr, png_const_charp message)
|
11
|
+
{
|
12
|
+
/* do nothing */
|
13
|
+
}
|
14
|
+
|
15
|
+
void
|
16
|
+
Init_png()
|
17
|
+
{
|
18
|
+
VALUE mAxon = rb_define_module("Axon");
|
19
|
+
rb_const_set(mAxon, rb_intern("PNG_LIB_VERSION"),
|
20
|
+
INT2FIX(PNG_LIBPNG_VER));
|
21
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#ifndef AXON_PNG_COMMON_H
|
2
|
+
#define AXON_PNG_COMMON_H
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <png.h>
|
6
|
+
|
7
|
+
#ifndef HAVE_RB_BLOCK_CALL
|
8
|
+
#define rb_block_call(arg1, arg2, arg3, arg4, arg5, arg6) rb_iterate(rb_each, arg1, arg5, arg6)
|
9
|
+
#endif
|
10
|
+
|
11
|
+
void png_error_fn(png_structp, png_const_charp);
|
12
|
+
void png_warning_fn(png_structp, png_const_charp);
|
13
|
+
|
14
|
+
void Init_png();
|
15
|
+
void Init_png_native_writer();
|
16
|
+
void Init_png_reader();
|
17
|
+
|
18
|
+
#endif
|
@@ -0,0 +1,166 @@
|
|
1
|
+
#include "png_common.h"
|
2
|
+
|
3
|
+
static ID id_write, id_GRAYSCALE, id_RGB, id_image, id_each, id_width,
|
4
|
+
id_height, id_color_model, id_components;
|
5
|
+
|
6
|
+
void
|
7
|
+
write_data(png_structp png_ptr, png_bytep data, png_size_t length)
|
8
|
+
{
|
9
|
+
VALUE str, io, write_len;
|
10
|
+
int write_len_i;
|
11
|
+
|
12
|
+
if (png_ptr == NULL)
|
13
|
+
return;
|
14
|
+
|
15
|
+
str = rb_str_new(data, length);
|
16
|
+
io = (VALUE)png_get_io_ptr(png_ptr);
|
17
|
+
write_len = rb_funcall(io, id_write, 1, str);
|
18
|
+
write_len_i = FIX2INT(write_len);
|
19
|
+
|
20
|
+
if ((size_t)write_len_i != length)
|
21
|
+
rb_raise(rb_eRuntimeError, "Write Error. Wrote %d instead of %d bytes.",
|
22
|
+
write_len_i, (int)length);
|
23
|
+
}
|
24
|
+
|
25
|
+
void
|
26
|
+
flush_data(png_structp png_ptr)
|
27
|
+
{
|
28
|
+
/* do nothing */
|
29
|
+
}
|
30
|
+
|
31
|
+
static VALUE
|
32
|
+
write_scanline(VALUE scan_line, VALUE *args)
|
33
|
+
{
|
34
|
+
png_structp png_ptr = (png_structp)args[1];
|
35
|
+
png_infop info_ptr = (png_infop)args[2];
|
36
|
+
png_uint_32 width;
|
37
|
+
png_byte components;
|
38
|
+
|
39
|
+
if (TYPE(scan_line) != T_STRING)
|
40
|
+
scan_line = rb_obj_as_string(scan_line);
|
41
|
+
|
42
|
+
width = png_get_image_width(png_ptr, info_ptr);
|
43
|
+
components = png_get_channels(png_ptr, info_ptr);
|
44
|
+
if ((png_uint_32)RSTRING_LEN(scan_line) != width * components)
|
45
|
+
rb_raise(rb_eRuntimeError, "Scanline has a bad size. Expected %d but got %d.",
|
46
|
+
(int)(width * components), (int)RSTRING_LEN(scan_line));
|
47
|
+
|
48
|
+
png_write_row(png_ptr, (png_bytep)RSTRING_PTR(scan_line));
|
49
|
+
return Qnil;
|
50
|
+
}
|
51
|
+
|
52
|
+
static int
|
53
|
+
id_to_color_type(ID rb, int components)
|
54
|
+
{
|
55
|
+
if (rb == id_GRAYSCALE && components == 1) return PNG_COLOR_TYPE_GRAY;
|
56
|
+
else if (rb == id_GRAYSCALE && components == 2) return PNG_COLOR_TYPE_GRAY_ALPHA;
|
57
|
+
else if (rb == id_RGB && components == 3) return PNG_COLOR_TYPE_RGB;
|
58
|
+
else if (rb == id_RGB && components == 4) return PNG_COLOR_TYPE_RGB_ALPHA;
|
59
|
+
|
60
|
+
rb_raise(rb_eRuntimeError, "Color Space not recognized.");
|
61
|
+
}
|
62
|
+
|
63
|
+
static void
|
64
|
+
size_and_colors(VALUE self, png_structp png_ptr, png_infop info_ptr)
|
65
|
+
{
|
66
|
+
VALUE image, width, height, color_model, components;
|
67
|
+
int color_type;
|
68
|
+
|
69
|
+
image = rb_funcall(self, id_image, 0);
|
70
|
+
|
71
|
+
width = rb_funcall(image, id_width, 0);
|
72
|
+
height = rb_funcall(image, id_height, 0);
|
73
|
+
components = rb_funcall(image, id_components, 0);
|
74
|
+
color_model = rb_funcall(image, id_color_model, 0);
|
75
|
+
|
76
|
+
color_type = id_to_color_type(SYM2ID(color_model), FIX2INT(components));
|
77
|
+
|
78
|
+
png_set_IHDR(png_ptr, info_ptr, FIX2INT(width), FIX2INT(height), 8,
|
79
|
+
color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
80
|
+
PNG_FILTER_TYPE_DEFAULT);
|
81
|
+
}
|
82
|
+
|
83
|
+
static VALUE
|
84
|
+
write2(VALUE *args)
|
85
|
+
{
|
86
|
+
VALUE self = args[0];
|
87
|
+
png_structp png_ptr = (png_structp)args[1];
|
88
|
+
png_infop info_ptr = (png_infop)args[2];
|
89
|
+
|
90
|
+
VALUE image = rb_funcall(self, id_image, 0);
|
91
|
+
|
92
|
+
size_and_colors(self, png_ptr, info_ptr);
|
93
|
+
|
94
|
+
png_write_info(png_ptr, info_ptr);
|
95
|
+
rb_block_call(image, id_each, 0, 0, write_scanline, (VALUE)args);
|
96
|
+
png_write_end(png_ptr, info_ptr);
|
97
|
+
|
98
|
+
return Qnil;
|
99
|
+
}
|
100
|
+
|
101
|
+
static VALUE
|
102
|
+
write2_ensure(VALUE *args)
|
103
|
+
{
|
104
|
+
png_structp png_ptr = (png_structp)args[1];
|
105
|
+
png_infop info_ptr = (png_infop)args[2];
|
106
|
+
|
107
|
+
png_destroy_write_struct(&png_ptr, &info_ptr);
|
108
|
+
return Qnil;
|
109
|
+
}
|
110
|
+
|
111
|
+
/*
|
112
|
+
* call-seq:
|
113
|
+
* writer.write(io) -> nil
|
114
|
+
*
|
115
|
+
* Compress image and write the png to io.
|
116
|
+
*/
|
117
|
+
|
118
|
+
static VALUE
|
119
|
+
pngwrite(VALUE self, VALUE io)
|
120
|
+
{
|
121
|
+
VALUE ensure_args[3];
|
122
|
+
|
123
|
+
png_structp png_ptr;
|
124
|
+
png_infop info_ptr;
|
125
|
+
|
126
|
+
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
|
127
|
+
(png_error_ptr)png_error_fn,
|
128
|
+
(png_error_ptr)png_warning_fn);
|
129
|
+
|
130
|
+
if (png_ptr == NULL)
|
131
|
+
rb_raise(rb_eRuntimeError, "unable to allocate a png object");
|
132
|
+
|
133
|
+
info_ptr = png_create_info_struct(png_ptr);
|
134
|
+
if (info_ptr == NULL) {
|
135
|
+
png_destroy_write_struct(&png_ptr, png_infopp_NULL);
|
136
|
+
rb_raise(rb_eRuntimeError, "unable to allocate a png info object");
|
137
|
+
}
|
138
|
+
|
139
|
+
png_set_write_fn(png_ptr, (void *)io, write_data, flush_data);
|
140
|
+
|
141
|
+
ensure_args[0] = self;
|
142
|
+
ensure_args[1] = (VALUE)png_ptr;
|
143
|
+
ensure_args[2] = (VALUE)info_ptr;
|
144
|
+
|
145
|
+
return rb_ensure(write2, (VALUE)ensure_args, write2_ensure,
|
146
|
+
(VALUE)ensure_args);
|
147
|
+
}
|
148
|
+
|
149
|
+
void
|
150
|
+
Init_png_native_writer()
|
151
|
+
{
|
152
|
+
VALUE mAxon = rb_define_module("Axon");
|
153
|
+
VALUE mNativeWriter = rb_define_module_under(mAxon, "PNGNativeWriter");
|
154
|
+
|
155
|
+
rb_define_method(mNativeWriter, "png_native_write", pngwrite, 1);
|
156
|
+
|
157
|
+
id_GRAYSCALE = rb_intern("GRAYSCALE");
|
158
|
+
id_RGB = rb_intern("RGB");
|
159
|
+
id_write = rb_intern("write");
|
160
|
+
id_image = rb_intern("image");
|
161
|
+
id_each = rb_intern("each");
|
162
|
+
id_width = rb_intern("width");
|
163
|
+
id_height = rb_intern("height");
|
164
|
+
id_color_model = rb_intern("color_model");
|
165
|
+
id_components = rb_intern("components");
|
166
|
+
}
|
@@ -0,0 +1,381 @@
|
|
1
|
+
#include "png_common.h"
|
2
|
+
|
3
|
+
static ID id_read, id_GRAYSCALE, id_RGB;
|
4
|
+
|
5
|
+
struct png_data {
|
6
|
+
png_structp png_ptr;
|
7
|
+
png_infop info_ptr;
|
8
|
+
|
9
|
+
int needs_reset;
|
10
|
+
int locked;
|
11
|
+
int rewind_after_scanlines;
|
12
|
+
|
13
|
+
VALUE io;
|
14
|
+
};
|
15
|
+
|
16
|
+
static void
|
17
|
+
raise_if_locked(struct png_data *reader)
|
18
|
+
{
|
19
|
+
if (reader->locked)
|
20
|
+
rb_raise(rb_eRuntimeError, "Can't modify a locked Reader");
|
21
|
+
}
|
22
|
+
|
23
|
+
static void
|
24
|
+
free_png(struct png_data *reader)
|
25
|
+
{
|
26
|
+
png_structp png_ptr;
|
27
|
+
png_infop info_ptr;
|
28
|
+
|
29
|
+
png_ptr = reader->png_ptr;
|
30
|
+
info_ptr = reader->info_ptr;
|
31
|
+
|
32
|
+
if (png_ptr && info_ptr)
|
33
|
+
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
|
34
|
+
else if (png_ptr)
|
35
|
+
png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
|
36
|
+
}
|
37
|
+
|
38
|
+
static void
|
39
|
+
free_reader(struct png_data *reader)
|
40
|
+
{
|
41
|
+
free_png(reader);
|
42
|
+
free(reader);
|
43
|
+
}
|
44
|
+
|
45
|
+
void
|
46
|
+
read_data_fn(png_structp png_ptr, png_bytep data, png_size_t length)
|
47
|
+
{
|
48
|
+
VALUE io, str;
|
49
|
+
size_t read_len;
|
50
|
+
|
51
|
+
if (png_ptr == NULL)
|
52
|
+
return;
|
53
|
+
|
54
|
+
io = (VALUE)png_get_io_ptr(png_ptr);
|
55
|
+
str = rb_funcall(io, id_read, 1, INT2FIX(length));
|
56
|
+
|
57
|
+
if (NIL_P(str))
|
58
|
+
rb_raise(rb_eRuntimeError, "Read Error. Reader returned nil.");
|
59
|
+
|
60
|
+
StringValue(str);
|
61
|
+
read_len = RSTRING_LEN(str);
|
62
|
+
|
63
|
+
if (read_len != length)
|
64
|
+
rb_raise(rb_eRuntimeError, "Read Error. Read %d instead of %d bytes.",
|
65
|
+
(int)read_len, (int)length);
|
66
|
+
|
67
|
+
png_memcpy(data, RSTRING_PTR(str), read_len);
|
68
|
+
}
|
69
|
+
|
70
|
+
static void
|
71
|
+
mark(struct png_data *reader)
|
72
|
+
{
|
73
|
+
VALUE io = reader->io;
|
74
|
+
|
75
|
+
if (io)
|
76
|
+
rb_gc_mark(io);
|
77
|
+
}
|
78
|
+
|
79
|
+
static void
|
80
|
+
allocate_png(struct png_data *reader)
|
81
|
+
{
|
82
|
+
png_structp png_ptr;
|
83
|
+
png_infop info_ptr;
|
84
|
+
|
85
|
+
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
|
86
|
+
(png_error_ptr)png_error_fn,
|
87
|
+
(png_error_ptr)png_warning_fn);
|
88
|
+
|
89
|
+
if (png_ptr == NULL)
|
90
|
+
rb_raise(rb_eRuntimeError, "unable to allocate a png object");
|
91
|
+
|
92
|
+
info_ptr = png_create_info_struct(png_ptr);
|
93
|
+
if (info_ptr == NULL) {
|
94
|
+
png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
|
95
|
+
rb_raise(rb_eRuntimeError, "unable to allocate a png info object");
|
96
|
+
}
|
97
|
+
|
98
|
+
reader->png_ptr = png_ptr;
|
99
|
+
reader->info_ptr = info_ptr;
|
100
|
+
}
|
101
|
+
|
102
|
+
static VALUE
|
103
|
+
allocate(VALUE klass)
|
104
|
+
{
|
105
|
+
VALUE self;
|
106
|
+
struct png_data *reader;
|
107
|
+
|
108
|
+
self = Data_Make_Struct(klass, struct png_data, mark, free_reader, reader);
|
109
|
+
allocate_png(reader);
|
110
|
+
|
111
|
+
return self;
|
112
|
+
}
|
113
|
+
|
114
|
+
VALUE
|
115
|
+
read_info(struct png_data *reader)
|
116
|
+
{
|
117
|
+
png_structp png_ptr;
|
118
|
+
png_infop info_ptr;
|
119
|
+
|
120
|
+
png_ptr = reader->png_ptr;
|
121
|
+
info_ptr = reader->info_ptr;
|
122
|
+
png_read_info(png_ptr, info_ptr);
|
123
|
+
|
124
|
+
return Qnil;
|
125
|
+
}
|
126
|
+
|
127
|
+
/*
|
128
|
+
* call-seq:
|
129
|
+
* Reader.new(string_or_io[, markers]) -> reader
|
130
|
+
*
|
131
|
+
* Create a new JPEG Reader. string_or_io may be a String or any object that
|
132
|
+
* responds to read and close.
|
133
|
+
*
|
134
|
+
* markers should be an array of valid JPEG header marker symbols. Valid
|
135
|
+
* symbols are :APP0 through :APP15 and :COM.
|
136
|
+
*
|
137
|
+
* If performance is important, you can avoid reading all header markers by
|
138
|
+
* supplying an empty array, [].
|
139
|
+
*
|
140
|
+
* When markers are not specified, we read all known JPEG markers.
|
141
|
+
*/
|
142
|
+
static VALUE
|
143
|
+
initialize(VALUE self, VALUE io)
|
144
|
+
{
|
145
|
+
struct png_data *reader;
|
146
|
+
|
147
|
+
Data_Get_Struct(self, struct png_data, reader);
|
148
|
+
raise_if_locked(reader);
|
149
|
+
png_set_read_fn(reader->png_ptr, (void *)io, read_data_fn);
|
150
|
+
reader->io = io;
|
151
|
+
read_info(reader);
|
152
|
+
|
153
|
+
return self;
|
154
|
+
}
|
155
|
+
|
156
|
+
static int
|
157
|
+
get_components(png_structp png_ptr, png_infop info_ptr)
|
158
|
+
{
|
159
|
+
switch (png_get_color_type(png_ptr, info_ptr)) {
|
160
|
+
case PNG_COLOR_TYPE_GRAY: return 1;
|
161
|
+
case PNG_COLOR_TYPE_GRAY_ALPHA: return 2;
|
162
|
+
case PNG_COLOR_TYPE_RGB: return 3;
|
163
|
+
case PNG_COLOR_TYPE_RGB_ALPHA: return 4;
|
164
|
+
}
|
165
|
+
|
166
|
+
return 0;
|
167
|
+
}
|
168
|
+
|
169
|
+
/*
|
170
|
+
* call-seq:
|
171
|
+
* reader.num_components -> number
|
172
|
+
*
|
173
|
+
* Retrieve the number of components as stored in the JPEG image.
|
174
|
+
*/
|
175
|
+
static VALUE
|
176
|
+
components(VALUE self)
|
177
|
+
{
|
178
|
+
struct png_data *reader;
|
179
|
+
png_structp png_ptr;
|
180
|
+
png_infop info_ptr;
|
181
|
+
int c;
|
182
|
+
|
183
|
+
Data_Get_Struct(self, struct png_data, reader);
|
184
|
+
png_ptr = reader->png_ptr;
|
185
|
+
info_ptr = reader->info_ptr;
|
186
|
+
|
187
|
+
c = get_components(png_ptr, info_ptr);
|
188
|
+
|
189
|
+
if (c == 0)
|
190
|
+
return Qnil;
|
191
|
+
|
192
|
+
return INT2FIX(c);
|
193
|
+
}
|
194
|
+
|
195
|
+
static ID
|
196
|
+
png_color_type_to_id(png_byte color_type)
|
197
|
+
{
|
198
|
+
switch (color_type) {
|
199
|
+
case PNG_COLOR_TYPE_GRAY: return id_GRAYSCALE;
|
200
|
+
case PNG_COLOR_TYPE_GRAY_ALPHA: return id_GRAYSCALE;
|
201
|
+
case PNG_COLOR_TYPE_RGB: return id_RGB;
|
202
|
+
case PNG_COLOR_TYPE_RGB_ALPHA: return id_RGB;
|
203
|
+
}
|
204
|
+
|
205
|
+
rb_raise(rb_eRuntimeError, "PNG Color Type not recognized.");
|
206
|
+
}
|
207
|
+
|
208
|
+
/*
|
209
|
+
* call-seq:
|
210
|
+
* reader.color_model -> symbol
|
211
|
+
*
|
212
|
+
* Returns a symbol representing the color space into which the JPEG will be
|
213
|
+
* transformed as it is read.
|
214
|
+
*
|
215
|
+
* By default this color space is based on Reader#color_space.
|
216
|
+
*
|
217
|
+
* Possible color spaces are: GRAYSCALE, RGB, YCbCr, CMYK, and YCCK. This method
|
218
|
+
* will return nil if the color space is not recognized.
|
219
|
+
*/
|
220
|
+
static VALUE
|
221
|
+
color_model(VALUE self)
|
222
|
+
{
|
223
|
+
struct png_data *reader;
|
224
|
+
png_structp png_ptr;
|
225
|
+
png_infop info_ptr;
|
226
|
+
|
227
|
+
Data_Get_Struct(self, struct png_data, reader);
|
228
|
+
png_ptr = reader->png_ptr;
|
229
|
+
info_ptr = reader->info_ptr;
|
230
|
+
|
231
|
+
return ID2SYM(png_color_type_to_id(png_get_color_type(png_ptr, info_ptr)));
|
232
|
+
}
|
233
|
+
|
234
|
+
static VALUE
|
235
|
+
each2(VALUE arg)
|
236
|
+
{
|
237
|
+
struct png_data *reader;
|
238
|
+
png_structp png_ptr;
|
239
|
+
png_infop info_ptr;
|
240
|
+
int i, width, height, components, sl_width;
|
241
|
+
VALUE sl;
|
242
|
+
|
243
|
+
reader = (struct png_data *)arg;
|
244
|
+
png_ptr = reader->png_ptr;
|
245
|
+
info_ptr = reader->info_ptr;
|
246
|
+
|
247
|
+
width = png_get_image_width(png_ptr, info_ptr);
|
248
|
+
height = png_get_image_height(png_ptr, info_ptr);
|
249
|
+
components = get_components(png_ptr, info_ptr);
|
250
|
+
sl_width = width * components;
|
251
|
+
|
252
|
+
for (i = 0; i < height; i++) {
|
253
|
+
sl = rb_str_new(0, sl_width);
|
254
|
+
png_read_row(png_ptr, (png_bytep)RSTRING_PTR(sl), png_bytep_NULL);
|
255
|
+
|
256
|
+
if (rb_block_given_p())
|
257
|
+
rb_yield(sl);
|
258
|
+
}
|
259
|
+
|
260
|
+
return Qnil;
|
261
|
+
}
|
262
|
+
|
263
|
+
static VALUE
|
264
|
+
each2_ensure(VALUE arg)
|
265
|
+
{
|
266
|
+
struct png_data *reader;
|
267
|
+
|
268
|
+
reader = (struct png_data *)arg;
|
269
|
+
reader->locked = 0;
|
270
|
+
reader->needs_reset = 1;
|
271
|
+
|
272
|
+
return Qnil;
|
273
|
+
}
|
274
|
+
|
275
|
+
static void
|
276
|
+
reset_reader(struct png_data *reader)
|
277
|
+
{
|
278
|
+
VALUE io;
|
279
|
+
|
280
|
+
io = reader->io;
|
281
|
+
free_png(reader);
|
282
|
+
allocate_png(reader);
|
283
|
+
png_set_read_fn(reader->png_ptr, (void *)io, read_data_fn);
|
284
|
+
reader->io = io;
|
285
|
+
read_info(reader);
|
286
|
+
}
|
287
|
+
|
288
|
+
/*
|
289
|
+
* call-seq:
|
290
|
+
* reader.each(&block)
|
291
|
+
*
|
292
|
+
* Iterate over each decoded scanline in the JPEG image.
|
293
|
+
*/
|
294
|
+
static VALUE
|
295
|
+
each(VALUE self)
|
296
|
+
{
|
297
|
+
struct png_data *reader;
|
298
|
+
|
299
|
+
Data_Get_Struct(self, struct png_data, reader);
|
300
|
+
|
301
|
+
if (reader->needs_reset)
|
302
|
+
reset_reader(reader);
|
303
|
+
|
304
|
+
raise_if_locked(reader);
|
305
|
+
reader->locked = 1;
|
306
|
+
rb_ensure(each2, (VALUE)reader, each2_ensure, (VALUE)reader);
|
307
|
+
|
308
|
+
return Qnil;
|
309
|
+
}
|
310
|
+
|
311
|
+
/*
|
312
|
+
* call-seq:
|
313
|
+
* reader.width -> number
|
314
|
+
*
|
315
|
+
* Retrieve the width of the image as it will be written out. This is primarily
|
316
|
+
* affected by scale_num and scale_denom if they are set.
|
317
|
+
*
|
318
|
+
* Note that this value is not automatically calculated unless you call
|
319
|
+
* Reader#calc_output_dimensions or after Reader#each_scanline has been called.
|
320
|
+
*/
|
321
|
+
static VALUE
|
322
|
+
width(VALUE self)
|
323
|
+
{
|
324
|
+
struct png_data *reader;
|
325
|
+
png_structp png_ptr;
|
326
|
+
png_infop info_ptr;
|
327
|
+
|
328
|
+
Data_Get_Struct(self, struct png_data, reader);
|
329
|
+
png_ptr = reader->png_ptr;
|
330
|
+
info_ptr = reader->info_ptr;
|
331
|
+
|
332
|
+
return INT2FIX(png_get_image_width(png_ptr, info_ptr));
|
333
|
+
}
|
334
|
+
|
335
|
+
/*
|
336
|
+
* call-seq:
|
337
|
+
* reader.height -> number
|
338
|
+
*
|
339
|
+
* Retrieve the height of the image as it will be written out. This is primarily
|
340
|
+
* affected by scale_num and scale_denom if they are set.
|
341
|
+
*
|
342
|
+
* Note that this value is not automatically calculated unless you call
|
343
|
+
* Reader#calc_output_dimensions or after Reader#each has been called.
|
344
|
+
*/
|
345
|
+
static VALUE
|
346
|
+
height(VALUE self)
|
347
|
+
{
|
348
|
+
struct png_data *reader;
|
349
|
+
png_structp png_ptr;
|
350
|
+
png_infop info_ptr;
|
351
|
+
|
352
|
+
Data_Get_Struct(self, struct png_data, reader);
|
353
|
+
png_ptr = reader->png_ptr;
|
354
|
+
info_ptr = reader->info_ptr;
|
355
|
+
|
356
|
+
return INT2FIX(png_get_image_height(png_ptr, info_ptr));
|
357
|
+
}
|
358
|
+
|
359
|
+
void
|
360
|
+
Init_png_reader()
|
361
|
+
{
|
362
|
+
VALUE mAxon = rb_define_module("Axon");
|
363
|
+
VALUE cPNGReader = rb_define_class_under(mAxon, "PNGReader", rb_cObject);
|
364
|
+
VALUE mImage = rb_define_module_under(mAxon, "Image");
|
365
|
+
|
366
|
+
rb_include_module(cPNGReader, mImage);
|
367
|
+
rb_include_module(cPNGReader, rb_mEnumerable);
|
368
|
+
|
369
|
+
rb_define_alloc_func(cPNGReader, allocate);
|
370
|
+
|
371
|
+
rb_define_method(cPNGReader, "initialize", initialize, 1);
|
372
|
+
rb_define_method(cPNGReader, "color_model", color_model, 0);
|
373
|
+
rb_define_method(cPNGReader, "components", components, 0);
|
374
|
+
rb_define_method(cPNGReader, "width", width, 0);
|
375
|
+
rb_define_method(cPNGReader, "height", height, 0);
|
376
|
+
rb_define_method(cPNGReader, "each", each, 0);
|
377
|
+
|
378
|
+
id_read = rb_intern("read");
|
379
|
+
id_GRAYSCALE = rb_intern("GRAYSCALE");
|
380
|
+
id_RGB = rb_intern("RGB");
|
381
|
+
}
|
data/lib/axon/axon.so
ADDED
Binary file
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Axon
|
2
|
+
class BilinearScaler < Scaler
|
3
|
+
include BilinearScaling
|
4
|
+
include Image
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def each
|
8
|
+
source_y = 0
|
9
|
+
destination_y = 0
|
10
|
+
sample_y = 0
|
11
|
+
|
12
|
+
each_scanline_with_next do |sl1, sl2|
|
13
|
+
while sample_y.to_i == source_y
|
14
|
+
yield interpolate_scanline(sl1, sl2, sample_y)
|
15
|
+
|
16
|
+
destination_y += 1
|
17
|
+
sample_y = destination_y / @height_ratio
|
18
|
+
end
|
19
|
+
|
20
|
+
source_y += 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Yields each scanline with the following scanline. The final scanline is
|
27
|
+
# yielded with itself.
|
28
|
+
|
29
|
+
def each_scanline_with_next
|
30
|
+
last_sl = nil
|
31
|
+
|
32
|
+
each_scanline_with_padding do |scanline|
|
33
|
+
yield last_sl, scanline if last_sl
|
34
|
+
last_sl = scanline
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Adds a one pixel padding to the right of the image and one pixel of
|
39
|
+
# padding to the bottom by duplicating the last pixels.
|
40
|
+
|
41
|
+
def each_scanline_with_padding
|
42
|
+
last = nil
|
43
|
+
components = @image.components
|
44
|
+
|
45
|
+
@image.each do |scanline|
|
46
|
+
scanline << scanline[-components, components] # bonus pixel
|
47
|
+
yield scanline
|
48
|
+
last = scanline
|
49
|
+
end
|
50
|
+
|
51
|
+
yield last # bonus scanline
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module Image
|
56
|
+
def scale_bilinear(*args)
|
57
|
+
BilinearScaler.new(self, *args)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|