axon 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,118 @@
|
|
1
|
+
#include "jpeg_common.h"
|
2
|
+
|
3
|
+
static ID id_GRAYSCALE, id_RGB, id_YCbCr, id_CMYK, id_YCCK, id_UNKNOWN;
|
4
|
+
static ID id_APP0, id_APP1, id_APP2, id_APP3, id_APP4, id_APP5, id_APP6,
|
5
|
+
id_APP7, id_APP8, id_APP9, id_APP10, id_APP11, id_APP12, id_APP13,
|
6
|
+
id_APP14, id_APP15, id_COM;
|
7
|
+
|
8
|
+
struct jpeg_error_mgr jerr;
|
9
|
+
|
10
|
+
ID
|
11
|
+
j_color_space_to_id(J_COLOR_SPACE cs)
|
12
|
+
{
|
13
|
+
switch (cs) {
|
14
|
+
case JCS_GRAYSCALE: return id_GRAYSCALE;
|
15
|
+
case JCS_RGB: return id_RGB;
|
16
|
+
case JCS_YCbCr: return id_YCbCr;
|
17
|
+
case JCS_CMYK: return id_CMYK;
|
18
|
+
case JCS_YCCK: return id_YCCK;
|
19
|
+
}
|
20
|
+
|
21
|
+
return id_UNKNOWN;
|
22
|
+
}
|
23
|
+
|
24
|
+
J_COLOR_SPACE
|
25
|
+
id_to_j_color_space(ID rb)
|
26
|
+
{
|
27
|
+
if (rb == id_GRAYSCALE) return JCS_GRAYSCALE;
|
28
|
+
else if (rb == id_RGB) return JCS_RGB;
|
29
|
+
else if (rb == id_YCbCr) return JCS_YCbCr;
|
30
|
+
else if (rb == id_CMYK) return JCS_CMYK;
|
31
|
+
else if (rb == id_YCCK) return JCS_YCCK;
|
32
|
+
else if (rb == id_UNKNOWN) return JCS_UNKNOWN;
|
33
|
+
|
34
|
+
rb_raise(rb_eRuntimeError, "Color Space not recognized.");
|
35
|
+
}
|
36
|
+
|
37
|
+
int sym_to_marker_code(VALUE sym)
|
38
|
+
{
|
39
|
+
ID rb = SYM2ID(sym);
|
40
|
+
|
41
|
+
if (rb == id_APP0) return JPEG_APP0;
|
42
|
+
else if (rb == id_APP1) return JPEG_APP0 + 1;
|
43
|
+
else if (rb == id_APP2) return JPEG_APP0 + 2;
|
44
|
+
else if (rb == id_APP3) return JPEG_APP0 + 3;
|
45
|
+
else if (rb == id_APP4) return JPEG_APP0 + 4;
|
46
|
+
else if (rb == id_APP5) return JPEG_APP0 + 5;
|
47
|
+
else if (rb == id_APP6) return JPEG_APP0 + 6;
|
48
|
+
else if (rb == id_APP7) return JPEG_APP0 + 7;
|
49
|
+
else if (rb == id_APP8) return JPEG_APP0 + 8;
|
50
|
+
else if (rb == id_APP9) return JPEG_APP0 + 9;
|
51
|
+
else if (rb == id_APP10) return JPEG_APP0 + 10;
|
52
|
+
else if (rb == id_APP11) return JPEG_APP0 + 11;
|
53
|
+
else if (rb == id_APP12) return JPEG_APP0 + 12;
|
54
|
+
else if (rb == id_APP13) return JPEG_APP0 + 13;
|
55
|
+
else if (rb == id_APP14) return JPEG_APP0 + 14;
|
56
|
+
else if (rb == id_APP15) return JPEG_APP0 + 15;
|
57
|
+
else if (rb == id_COM) return JPEG_COM;
|
58
|
+
|
59
|
+
rb_raise(rb_eRuntimeError, "Marker code not recognized.");
|
60
|
+
}
|
61
|
+
|
62
|
+
void
|
63
|
+
error_exit(j_common_ptr cinfo)
|
64
|
+
{
|
65
|
+
char buffer[JMSG_LENGTH_MAX];
|
66
|
+
|
67
|
+
(*cinfo->err->format_message) (cinfo, buffer);
|
68
|
+
rb_raise(rb_eRuntimeError, "jpeglib: %s", buffer);
|
69
|
+
}
|
70
|
+
|
71
|
+
void
|
72
|
+
output_message(j_common_ptr cinfo)
|
73
|
+
{
|
74
|
+
/* do nothing */
|
75
|
+
}
|
76
|
+
|
77
|
+
void
|
78
|
+
init_jerror(struct jpeg_error_mgr * err)
|
79
|
+
{
|
80
|
+
jpeg_std_error(err);
|
81
|
+
err->error_exit = error_exit;
|
82
|
+
err->output_message = output_message;
|
83
|
+
}
|
84
|
+
|
85
|
+
void
|
86
|
+
Init_jpeg()
|
87
|
+
{
|
88
|
+
VALUE mAxon = rb_define_module("Axon");
|
89
|
+
rb_const_set(mAxon, rb_intern("JPEG_LIB_VERSION"),
|
90
|
+
INT2FIX(JPEG_LIB_VERSION));
|
91
|
+
|
92
|
+
init_jerror(&jerr);
|
93
|
+
|
94
|
+
id_GRAYSCALE = rb_intern("GRAYSCALE");
|
95
|
+
id_RGB = rb_intern("RGB");
|
96
|
+
id_YCbCr = rb_intern("YCbCr");
|
97
|
+
id_CMYK = rb_intern("CMYK");
|
98
|
+
id_YCCK = rb_intern("YCCK");
|
99
|
+
id_UNKNOWN = rb_intern("UNKNOWN");
|
100
|
+
|
101
|
+
id_APP0 = rb_intern("APP0");
|
102
|
+
id_APP1 = rb_intern("APP1");
|
103
|
+
id_APP2 = rb_intern("APP2");
|
104
|
+
id_APP3 = rb_intern("APP3");
|
105
|
+
id_APP4 = rb_intern("APP4");
|
106
|
+
id_APP5 = rb_intern("APP5");
|
107
|
+
id_APP6 = rb_intern("APP6");
|
108
|
+
id_APP7 = rb_intern("APP7");
|
109
|
+
id_APP8 = rb_intern("APP8");
|
110
|
+
id_APP9 = rb_intern("APP9");
|
111
|
+
id_APP10 = rb_intern("APP10");
|
112
|
+
id_APP11 = rb_intern("APP11");
|
113
|
+
id_APP12 = rb_intern("APP12");
|
114
|
+
id_APP13 = rb_intern("APP13");
|
115
|
+
id_APP14 = rb_intern("APP14");
|
116
|
+
id_APP15 = rb_intern("APP15");
|
117
|
+
id_COM = rb_intern("COM");
|
118
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#ifndef AXON_JPEG_COMMON_H
|
2
|
+
#define AXON_JPEG_COMMON_H
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <jpeglib.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
|
+
/*
|
12
|
+
* Marker size is defined by two bytes, so the maximum is 65,535 bytes.
|
13
|
+
* Two of those bytes are used by the size indicator bytes themselves, leaving
|
14
|
+
* 65,533 bytes.
|
15
|
+
*/
|
16
|
+
|
17
|
+
#define MAX_MARKER_LEN 65533
|
18
|
+
|
19
|
+
/*
|
20
|
+
* The Exif marker eats 4 bytes for "Exif", and 2 bytes for NULL terminators.
|
21
|
+
*/
|
22
|
+
|
23
|
+
#define EXIF_OVERHEAD_LEN 6
|
24
|
+
#define MAX_EXIF_DATA_LEN MAX_MARKER_LEN - EXIF_OVERHEAD_LEN
|
25
|
+
#define EXIF_MARKER (JPEG_APP0 + 1)
|
26
|
+
|
27
|
+
extern struct jpeg_error_mgr jerr;
|
28
|
+
|
29
|
+
ID j_color_space_to_id(J_COLOR_SPACE cs);
|
30
|
+
J_COLOR_SPACE id_to_j_color_space(ID rb);
|
31
|
+
int sym_to_marker_code(ID rb);
|
32
|
+
|
33
|
+
void Init_jpeg();
|
34
|
+
void Init_jpeg_native_writer();
|
35
|
+
void Init_jpeg_native_reader();
|
36
|
+
|
37
|
+
#endif
|
@@ -0,0 +1,248 @@
|
|
1
|
+
#include "jpeg_common.h"
|
2
|
+
#include "iccjpeg.h"
|
3
|
+
|
4
|
+
static ID id_write, id_io, id_each, id_width, id_height, id_color_model,
|
5
|
+
id_components, id_icc_profile, id_exif, id_quality, id_bufsize,
|
6
|
+
id_image;
|
7
|
+
|
8
|
+
struct buf_dest_mgr {
|
9
|
+
struct jpeg_destination_mgr pub;
|
10
|
+
|
11
|
+
VALUE io;
|
12
|
+
|
13
|
+
JOCTET *buffer;
|
14
|
+
size_t alloc;
|
15
|
+
};
|
16
|
+
|
17
|
+
static void
|
18
|
+
reset_buffer(struct buf_dest_mgr *dest)
|
19
|
+
{
|
20
|
+
dest->pub.next_output_byte = dest->buffer;
|
21
|
+
dest->pub.free_in_buffer = dest->alloc;
|
22
|
+
}
|
23
|
+
|
24
|
+
static void
|
25
|
+
init_destination(j_compress_ptr cinfo)
|
26
|
+
{
|
27
|
+
struct buf_dest_mgr *dest = (struct buf_dest_mgr *) cinfo->dest;
|
28
|
+
size_t size = dest->alloc * sizeof(JOCTET);
|
29
|
+
|
30
|
+
/* Allocate the output buffer --- it will be released when done */
|
31
|
+
dest->buffer = (JOCTET *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,
|
32
|
+
JPOOL_IMAGE, size);
|
33
|
+
reset_buffer(dest);
|
34
|
+
}
|
35
|
+
|
36
|
+
static boolean
|
37
|
+
empty_output_buffer(j_compress_ptr cinfo)
|
38
|
+
{
|
39
|
+
VALUE str, write_len;
|
40
|
+
struct buf_dest_mgr *dest = (struct buf_dest_mgr *) cinfo->dest;
|
41
|
+
size_t write_len_i;
|
42
|
+
|
43
|
+
str = rb_str_new(dest->buffer, dest->alloc);
|
44
|
+
|
45
|
+
write_len = rb_funcall(dest->io, id_write, 1, str);
|
46
|
+
write_len_i = (size_t)FIX2INT(write_len);
|
47
|
+
if (write_len_i != dest->alloc)
|
48
|
+
rb_raise(rb_eRuntimeError, "Write Error. Wrote %d instead of %d bytes.",
|
49
|
+
(int)write_len_i, (int)dest->alloc);
|
50
|
+
|
51
|
+
reset_buffer(dest);
|
52
|
+
|
53
|
+
return TRUE;
|
54
|
+
}
|
55
|
+
|
56
|
+
static void
|
57
|
+
term_destination(j_compress_ptr cinfo)
|
58
|
+
{
|
59
|
+
struct buf_dest_mgr *dest = (struct buf_dest_mgr *) cinfo->dest;
|
60
|
+
size_t len = dest->alloc - dest->pub.free_in_buffer;
|
61
|
+
VALUE str;
|
62
|
+
|
63
|
+
if (len > 0) {
|
64
|
+
str = rb_str_new(dest->buffer, len);
|
65
|
+
rb_funcall(dest->io, id_write, 1, str);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
static VALUE
|
70
|
+
write_scanline(VALUE scan_line, VALUE data2)
|
71
|
+
{
|
72
|
+
JSAMPROW row_pointer[1];
|
73
|
+
j_compress_ptr cinfo = (j_compress_ptr)data2;
|
74
|
+
|
75
|
+
if (TYPE(scan_line) != T_STRING)
|
76
|
+
scan_line = rb_obj_as_string(scan_line);
|
77
|
+
|
78
|
+
if (RSTRING_LEN(scan_line) != cinfo->image_width * cinfo->num_components)
|
79
|
+
rb_raise(rb_eRuntimeError, "Scanline has a bad size. Expected %d but got %d.",
|
80
|
+
(int)(cinfo->image_width * cinfo->num_components),
|
81
|
+
(int)RSTRING_LEN(scan_line));
|
82
|
+
|
83
|
+
row_pointer[0] = (JSAMPLE *)RSTRING_PTR(scan_line);
|
84
|
+
jpeg_write_scanlines(cinfo, row_pointer, 1);
|
85
|
+
return Qnil;
|
86
|
+
}
|
87
|
+
|
88
|
+
static int
|
89
|
+
write_exif(j_compress_ptr cinfo, char *str, int len)
|
90
|
+
{
|
91
|
+
if (len > MAX_EXIF_DATA_LEN) {
|
92
|
+
rb_raise(rb_eRuntimeError, "Exif data is too large. Max is %d.",
|
93
|
+
MAX_EXIF_DATA_LEN);
|
94
|
+
}
|
95
|
+
|
96
|
+
jpeg_write_m_header(cinfo, EXIF_MARKER,
|
97
|
+
(unsigned int) (len + EXIF_OVERHEAD_LEN));
|
98
|
+
|
99
|
+
jpeg_write_m_byte(cinfo, 0x45);
|
100
|
+
jpeg_write_m_byte(cinfo, 0x78);
|
101
|
+
jpeg_write_m_byte(cinfo, 0x69);
|
102
|
+
jpeg_write_m_byte(cinfo, 0x66);
|
103
|
+
jpeg_write_m_byte(cinfo, 0x0);
|
104
|
+
jpeg_write_m_byte(cinfo, 0x0);
|
105
|
+
|
106
|
+
/* Add the profile data */
|
107
|
+
while (len--) {
|
108
|
+
jpeg_write_m_byte(cinfo, *str);
|
109
|
+
str++;
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
static void
|
114
|
+
configure(VALUE self, j_compress_ptr cinfo)
|
115
|
+
{
|
116
|
+
VALUE image, width, height, color_model, components, quality;
|
117
|
+
|
118
|
+
quality = rb_funcall(self, id_quality, 0);
|
119
|
+
|
120
|
+
image = rb_funcall(self, id_image, 0);
|
121
|
+
width = rb_funcall(image, id_width, 0);
|
122
|
+
height = rb_funcall(image, id_height, 0);
|
123
|
+
components = rb_funcall(image, id_components, 0);
|
124
|
+
color_model = rb_funcall(image, id_color_model, 0);
|
125
|
+
|
126
|
+
cinfo->image_width = FIX2INT(width);
|
127
|
+
cinfo->image_height = FIX2INT(height);
|
128
|
+
cinfo->input_components = FIX2INT(components);
|
129
|
+
cinfo->in_color_space = id_to_j_color_space(SYM2ID(color_model));
|
130
|
+
|
131
|
+
jpeg_set_defaults(cinfo);
|
132
|
+
|
133
|
+
if(!NIL_P(quality))
|
134
|
+
jpeg_set_quality(cinfo, FIX2INT(quality), TRUE);
|
135
|
+
}
|
136
|
+
|
137
|
+
static void
|
138
|
+
write_header(VALUE self, j_compress_ptr cinfo)
|
139
|
+
{
|
140
|
+
VALUE icc, exif;
|
141
|
+
|
142
|
+
icc = rb_funcall(self, id_icc_profile, 0);
|
143
|
+
if(!NIL_P(icc)) {
|
144
|
+
StringValue(icc);
|
145
|
+
write_icc_profile(cinfo, RSTRING_PTR(icc), RSTRING_LEN(icc));
|
146
|
+
}
|
147
|
+
|
148
|
+
exif = rb_funcall(self, id_exif, 0);
|
149
|
+
if (!NIL_P(exif)) {
|
150
|
+
StringValue(exif);
|
151
|
+
write_exif(cinfo, RSTRING_PTR(exif), RSTRING_LEN(exif));
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
static VALUE
|
156
|
+
write2(VALUE *args)
|
157
|
+
{
|
158
|
+
VALUE image, self;
|
159
|
+
|
160
|
+
self = args[0];
|
161
|
+
image = rb_funcall(self, id_image, 0);
|
162
|
+
j_compress_ptr cinfo = (j_compress_ptr) args[1];
|
163
|
+
|
164
|
+
configure(self, cinfo);
|
165
|
+
|
166
|
+
jpeg_start_compress(cinfo, TRUE);
|
167
|
+
|
168
|
+
write_header(self, cinfo);
|
169
|
+
rb_block_call(image, id_each, 0, 0, write_scanline, (VALUE)cinfo);
|
170
|
+
|
171
|
+
jpeg_finish_compress(cinfo);
|
172
|
+
|
173
|
+
return Qnil;
|
174
|
+
}
|
175
|
+
|
176
|
+
static VALUE
|
177
|
+
write2_ensure(VALUE *args)
|
178
|
+
{
|
179
|
+
jpeg_destroy_compress((j_compress_ptr) args[1]);
|
180
|
+
return Qnil;
|
181
|
+
}
|
182
|
+
|
183
|
+
/*
|
184
|
+
* call-seq:
|
185
|
+
* writer.write -> nil
|
186
|
+
*
|
187
|
+
* Compress image and write the jpeg.
|
188
|
+
*/
|
189
|
+
|
190
|
+
static VALUE
|
191
|
+
jpeg_native_write(VALUE self)
|
192
|
+
{
|
193
|
+
struct jpeg_compress_struct cinfo;
|
194
|
+
struct buf_dest_mgr mgr;
|
195
|
+
VALUE io, rb_bufsize, ensure_args[2];
|
196
|
+
int bufsize;
|
197
|
+
|
198
|
+
io = rb_funcall(self, id_io, 0);
|
199
|
+
rb_bufsize = rb_funcall(self, id_bufsize, 0);
|
200
|
+
bufsize = FIX2INT(rb_bufsize);
|
201
|
+
|
202
|
+
if (bufsize < 1)
|
203
|
+
rb_raise(rb_eRuntimeError, "Buffer size must be greater than zero");
|
204
|
+
|
205
|
+
cinfo.err = &jerr;
|
206
|
+
|
207
|
+
jpeg_create_compress(&cinfo);
|
208
|
+
|
209
|
+
mgr.pub.init_destination = init_destination;
|
210
|
+
mgr.pub.empty_output_buffer = empty_output_buffer;
|
211
|
+
mgr.pub.term_destination = term_destination;
|
212
|
+
mgr.alloc = bufsize;
|
213
|
+
mgr.io = io;
|
214
|
+
cinfo.dest = (struct jpeg_destination_mgr *)&mgr;
|
215
|
+
|
216
|
+
ensure_args[0] = self;
|
217
|
+
ensure_args[1] = (VALUE)&cinfo;
|
218
|
+
|
219
|
+
return rb_ensure(write2, (VALUE)ensure_args, write2_ensure,
|
220
|
+
(VALUE)ensure_args);
|
221
|
+
}
|
222
|
+
|
223
|
+
/* This module provides a single method #jpeg_native_write. It requires that
|
224
|
+
* the including class respond to #io, #image, #quality, #exif, #icc_profile,
|
225
|
+
* and #bufsize.
|
226
|
+
*/
|
227
|
+
|
228
|
+
void
|
229
|
+
Init_jpeg_native_writer()
|
230
|
+
{
|
231
|
+
VALUE mAxon = rb_define_module("Axon");
|
232
|
+
VALUE mNativeWriter = rb_define_module_under(mAxon, "JPEGNativeWriter");
|
233
|
+
|
234
|
+
rb_define_method(mNativeWriter, "jpeg_native_write", jpeg_native_write, 0);
|
235
|
+
|
236
|
+
id_write = rb_intern("write");
|
237
|
+
id_each = rb_intern("each");
|
238
|
+
id_io = rb_intern("io");
|
239
|
+
id_width = rb_intern("width");
|
240
|
+
id_height = rb_intern("height");
|
241
|
+
id_color_model = rb_intern("color_model");
|
242
|
+
id_components = rb_intern("components");
|
243
|
+
id_icc_profile = rb_intern("icc_profile");
|
244
|
+
id_exif = rb_intern("exif");
|
245
|
+
id_quality = rb_intern("quality");
|
246
|
+
id_bufsize = rb_intern("bufsize");
|
247
|
+
id_image = rb_intern("image");
|
248
|
+
}
|