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,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