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.
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
+ }