axon 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gemtest +0 -0
  2. data/CHANGELOG.rdoc +3 -0
  3. data/README.rdoc +104 -0
  4. data/Rakefile +30 -0
  5. data/TODO.rdoc +12 -0
  6. data/ext/axon/axon.c +20 -0
  7. data/ext/axon/bilinear_interpolation.c +115 -0
  8. data/ext/axon/extconf.rb +21 -0
  9. data/ext/axon/iccjpeg.c +248 -0
  10. data/ext/axon/iccjpeg.h +73 -0
  11. data/ext/axon/interpolation.h +7 -0
  12. data/ext/axon/jpeg_common.c +118 -0
  13. data/ext/axon/jpeg_common.h +37 -0
  14. data/ext/axon/jpeg_native_writer.c +248 -0
  15. data/ext/axon/jpeg_reader.c +774 -0
  16. data/ext/axon/nearest_neighbor_interpolation.c +50 -0
  17. data/ext/axon/png_common.c +21 -0
  18. data/ext/axon/png_common.h +18 -0
  19. data/ext/axon/png_native_writer.c +166 -0
  20. data/ext/axon/png_reader.c +381 -0
  21. data/lib/axon/axon.so +0 -0
  22. data/lib/axon/bilinear_scaler.rb +60 -0
  23. data/lib/axon/cropper.rb +35 -0
  24. data/lib/axon/fit.rb +67 -0
  25. data/lib/axon/jpeg_writer.rb +41 -0
  26. data/lib/axon/nearest_neighbor_scaler.rb +39 -0
  27. data/lib/axon/png_writer.rb +35 -0
  28. data/lib/axon/scaler.rb +41 -0
  29. data/lib/axon/solid.rb +23 -0
  30. data/lib/axon.rb +45 -0
  31. data/test/_test_readme.rb +34 -0
  32. data/test/helper.rb +17 -0
  33. data/test/reader_tests.rb +115 -0
  34. data/test/stress_tests.rb +71 -0
  35. data/test/test_bilinear_scaler.rb +9 -0
  36. data/test/test_cropper.rb +9 -0
  37. data/test/test_exif.rb +39 -0
  38. data/test/test_generator.rb +10 -0
  39. data/test/test_icc.rb +18 -0
  40. data/test/test_jpeg.rb +9 -0
  41. data/test/test_jpeg_reader.rb +109 -0
  42. data/test/test_jpeg_writer.rb +26 -0
  43. data/test/test_nearest_neighbor_scaler.rb +13 -0
  44. data/test/test_png.rb +9 -0
  45. data/test/test_png_reader.rb +15 -0
  46. data/test/test_png_writer.rb +13 -0
  47. data/test/writer_tests.rb +179 -0
  48. 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
+ }