axon 0.0.2 → 0.1.0

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 (56) hide show
  1. data/CHANGELOG.rdoc +9 -3
  2. data/README.rdoc +29 -36
  3. data/Rakefile +26 -21
  4. data/TODO.rdoc +1 -6
  5. data/ext/axon/axon.c +6 -15
  6. data/ext/axon/extconf.rb +19 -9
  7. data/ext/axon/interpolation.c +147 -0
  8. data/ext/axon/jpeg.c +1207 -0
  9. data/ext/axon/png.c +542 -0
  10. data/lib/axon.rb +235 -32
  11. data/lib/axon/cropper.rb +80 -18
  12. data/lib/axon/fit.rb +69 -19
  13. data/lib/axon/generators.rb +109 -0
  14. data/lib/axon/scalers.rb +160 -0
  15. data/test/helper.rb +151 -6
  16. data/test/reader_tests.rb +37 -82
  17. data/test/scaler_tests.rb +102 -0
  18. data/test/stress_helper.rb +58 -0
  19. data/test/stress_tests.rb +8 -5
  20. data/test/test_bilinear_scaler.rb +60 -2
  21. data/test/test_cropper.rb +68 -1
  22. data/test/test_fit.rb +35 -0
  23. data/test/test_generators.rb +21 -0
  24. data/test/test_image.rb +61 -0
  25. data/test/test_jpeg_reader.rb +96 -94
  26. data/test/test_jpeg_writer.rb +95 -8
  27. data/test/test_nearest_neighbor_scaler.rb +28 -4
  28. data/test/test_png_reader.rb +12 -8
  29. data/test/test_png_writer.rb +8 -6
  30. data/test/writer_tests.rb +129 -111
  31. metadata +71 -128
  32. data/.gemtest +0 -0
  33. data/ext/axon/bilinear_interpolation.c +0 -115
  34. data/ext/axon/interpolation.h +0 -7
  35. data/ext/axon/jpeg_common.c +0 -118
  36. data/ext/axon/jpeg_common.h +0 -37
  37. data/ext/axon/jpeg_native_writer.c +0 -248
  38. data/ext/axon/jpeg_reader.c +0 -774
  39. data/ext/axon/nearest_neighbor_interpolation.c +0 -50
  40. data/ext/axon/png_common.c +0 -21
  41. data/ext/axon/png_common.h +0 -18
  42. data/ext/axon/png_native_writer.c +0 -166
  43. data/ext/axon/png_reader.c +0 -381
  44. data/lib/axon/axon.so +0 -0
  45. data/lib/axon/bilinear_scaler.rb +0 -60
  46. data/lib/axon/jpeg_writer.rb +0 -41
  47. data/lib/axon/nearest_neighbor_scaler.rb +0 -39
  48. data/lib/axon/png_writer.rb +0 -35
  49. data/lib/axon/scaler.rb +0 -41
  50. data/lib/axon/solid.rb +0 -23
  51. data/test/_test_readme.rb +0 -34
  52. data/test/test_exif.rb +0 -39
  53. data/test/test_generator.rb +0 -10
  54. data/test/test_icc.rb +0 -18
  55. data/test/test_jpeg.rb +0 -9
  56. data/test/test_png.rb +0 -9
data/ext/axon/png.c ADDED
@@ -0,0 +1,542 @@
1
+ #include <ruby.h>
2
+ #include <png.h>
3
+
4
+ static ID id_write, id_GRAYSCALE, id_RGB, id_gets, id_width, id_height,
5
+ id_color_model, id_components, id_read;
6
+
7
+ struct png_data {
8
+ png_structp png_ptr;
9
+ png_infop info_ptr;
10
+ size_t lineno;
11
+ VALUE io;
12
+ };
13
+
14
+ struct io_write {
15
+ VALUE io;
16
+ size_t total;
17
+ };
18
+
19
+ static int
20
+ id_to_color_type(ID rb, int components)
21
+ {
22
+ if (rb == id_GRAYSCALE && components == 1) return PNG_COLOR_TYPE_GRAY;
23
+ else if (rb == id_GRAYSCALE && components == 2) return PNG_COLOR_TYPE_GRAY_ALPHA;
24
+ else if (rb == id_RGB && components == 3) return PNG_COLOR_TYPE_RGB;
25
+ else if (rb == id_RGB && components == 4) return PNG_COLOR_TYPE_RGB_ALPHA;
26
+
27
+ rb_raise(rb_eRuntimeError, "Color Space not recognized.");
28
+ }
29
+
30
+ static int
31
+ get_components(png_structp png_ptr, png_infop info_ptr)
32
+ {
33
+ switch (png_get_color_type(png_ptr, info_ptr)) {
34
+ case PNG_COLOR_TYPE_GRAY: return 1;
35
+ case PNG_COLOR_TYPE_GRAY_ALPHA: return 2;
36
+ case PNG_COLOR_TYPE_RGB: return 3;
37
+ case PNG_COLOR_TYPE_RGB_ALPHA: return 4;
38
+ }
39
+
40
+ return 0;
41
+ }
42
+
43
+ static void
44
+ png_error_fn(png_structp png_ptr, png_const_charp message)
45
+ {
46
+ rb_raise(rb_eRuntimeError, "pnglib: %s", message);
47
+ }
48
+
49
+ static void
50
+ png_warning_fn(png_structp png_ptr, png_const_charp message)
51
+ {
52
+ /* do nothing */
53
+ }
54
+
55
+ void
56
+ write_data(png_structp png_ptr, png_bytep data, png_size_t length)
57
+ {
58
+ VALUE str, rb_write_len;
59
+ int write_len;
60
+ struct io_write *iw;
61
+
62
+ if (png_ptr == NULL)
63
+ return;
64
+
65
+ str = rb_str_new(data, length);
66
+ iw = (struct io_write *)png_get_io_ptr(png_ptr);
67
+ rb_write_len = rb_funcall(iw->io, id_write, 1, str);
68
+
69
+ /* Ruby 1.8.7 makes up odd numbers when running NUM2INT on a symbol */
70
+ if (TYPE(rb_write_len) == T_SYMBOL)
71
+ rb_raise(rb_eTypeError, "Number Expected, but got Symbol.");
72
+
73
+ write_len = NUM2INT(rb_write_len);
74
+
75
+ if ((size_t)write_len != length)
76
+ rb_raise(rb_eRuntimeError, "Write Error. Wrote %d instead of %d bytes.",
77
+ write_len, (int)length);
78
+ iw->total += length;
79
+ }
80
+
81
+ void
82
+ flush_data(png_structp png_ptr)
83
+ {
84
+ /* do nothing */
85
+ }
86
+
87
+ static VALUE
88
+ write_scanline(VALUE scan_line, png_structp png_ptr, png_infop info_ptr)
89
+ {
90
+ png_uint_32 width;
91
+ png_byte components;
92
+
93
+ if (TYPE(scan_line) != T_STRING)
94
+ scan_line = rb_obj_as_string(scan_line);
95
+
96
+ width = png_get_image_width(png_ptr, info_ptr);
97
+ components = png_get_channels(png_ptr, info_ptr);
98
+ if ((png_uint_32)RSTRING_LEN(scan_line) != width * components)
99
+ rb_raise(rb_eRuntimeError, "Scanline has a bad size. Expected %d * %d but got %d.",
100
+ (int)width, (int)components, (int)RSTRING_LEN(scan_line));
101
+
102
+ png_write_row(png_ptr, (png_bytep)RSTRING_PTR(scan_line));
103
+ return Qnil;
104
+ }
105
+
106
+ static void
107
+ write_configure(VALUE image_in, png_structp png_ptr, png_infop info_ptr)
108
+ {
109
+ VALUE width, height, color_model, components;
110
+ int color_type;
111
+
112
+ width = rb_funcall(image_in, id_width, 0);
113
+ /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
114
+ if (SYMBOL_P(width))
115
+ rb_raise(rb_eTypeError, "source image has a symbol for width.");
116
+
117
+ height = rb_funcall(image_in, id_height, 0);
118
+ /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
119
+ if (SYMBOL_P(height))
120
+ rb_raise(rb_eTypeError, "source image has a symbol for height.");
121
+
122
+ components = rb_funcall(image_in, id_components, 0);
123
+ /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
124
+ if (SYMBOL_P(components))
125
+ rb_raise(rb_eTypeError, "source image has a symbol for components.");
126
+
127
+ color_model = rb_funcall(image_in, id_color_model, 0);
128
+ if (SYMBOL_P(color_model))
129
+ color_type = id_to_color_type(SYM2ID(color_model), NUM2INT(components));
130
+ else
131
+ rb_raise(rb_eTypeError, "source image has a non symbol color space");
132
+
133
+ png_set_IHDR(png_ptr, info_ptr, NUM2INT(width), NUM2INT(height), 8,
134
+ color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
135
+ PNG_FILTER_TYPE_DEFAULT);
136
+ }
137
+
138
+ static VALUE
139
+ write_png2(VALUE *args)
140
+ {
141
+ struct io_write *data;
142
+ png_structp png_ptr = (png_structp)args[0];
143
+ png_infop info_ptr = (png_infop)args[1];
144
+ VALUE scanline, image_in = args[2];
145
+ size_t i;
146
+
147
+ write_configure(image_in, png_ptr, info_ptr);
148
+ png_write_info(png_ptr, info_ptr);
149
+
150
+ for (i = 0; i < info_ptr->height; i++) {
151
+ scanline = rb_funcall(image_in, id_gets, 0);
152
+ write_scanline(scanline, png_ptr, info_ptr);
153
+ }
154
+ png_write_end(png_ptr, info_ptr);
155
+
156
+ data = (struct io_write *)png_get_io_ptr(png_ptr);
157
+
158
+ return INT2FIX(data->total);
159
+ }
160
+
161
+ static VALUE
162
+ write_png2_ensure(VALUE *args)
163
+ {
164
+ png_structp png_ptr = (png_structp)args[0];
165
+ png_infop info_ptr = (png_infop)args[1];
166
+
167
+ png_destroy_write_struct(&png_ptr, &info_ptr);
168
+ return Qnil;
169
+ }
170
+
171
+ /*
172
+ * call-seq:
173
+ * write(image_in, io_out) -> integer
174
+ *
175
+ * Writes the given image +image_in+ to +io_out+ as compressed PNG data.
176
+ * Returns the number of bytes written.
177
+ *
178
+ * image = Axon::Solid.new(200, 300)
179
+ * io = File.open("test.jpg", "w")
180
+ * Axon::PNG.write(image, io) #=> 1234
181
+ */
182
+
183
+ static VALUE
184
+ write_png(VALUE self, VALUE image_in, VALUE io_out)
185
+ {
186
+ VALUE ensure_args[3];
187
+ png_structp png_ptr;
188
+ png_infop info_ptr;
189
+ struct io_write data;
190
+
191
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
192
+ (png_error_ptr)png_error_fn,
193
+ (png_error_ptr)png_warning_fn);
194
+
195
+ if (png_ptr == NULL)
196
+ rb_raise(rb_eRuntimeError, "unable to allocate a png object");
197
+
198
+ info_ptr = png_create_info_struct(png_ptr);
199
+ if (info_ptr == NULL) {
200
+ png_destroy_write_struct(&png_ptr, (png_info **)NULL);
201
+ rb_raise(rb_eRuntimeError, "unable to allocate a png info object");
202
+ }
203
+
204
+ data.io = io_out;
205
+ data.total = 0;
206
+ png_set_write_fn(png_ptr, (void *)&data, write_data, flush_data);
207
+
208
+ ensure_args[0] = (VALUE)png_ptr;
209
+ ensure_args[1] = (VALUE)info_ptr;
210
+ ensure_args[2] = image_in;
211
+
212
+ return rb_ensure(write_png2, (VALUE)ensure_args, write_png2_ensure,
213
+ (VALUE)ensure_args);
214
+ }
215
+
216
+ static void
217
+ raise_if_locked(struct png_data *reader)
218
+ {
219
+ if (reader->lineno)
220
+ rb_raise(rb_eRuntimeError, "Can't modify a locked Reader");
221
+ }
222
+
223
+ static void
224
+ free_png(struct png_data *reader)
225
+ {
226
+ png_structp png_ptr;
227
+ png_infop info_ptr;
228
+
229
+ png_ptr = reader->png_ptr;
230
+ info_ptr = reader->info_ptr;
231
+
232
+ if (png_ptr && info_ptr)
233
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_info **)NULL);
234
+ else if (png_ptr)
235
+ png_destroy_read_struct(&png_ptr, (png_info **)NULL, (png_info **)NULL);
236
+ }
237
+
238
+ static void
239
+ free_reader(struct png_data *reader)
240
+ {
241
+ free_png(reader);
242
+ free(reader);
243
+ }
244
+
245
+ void
246
+ read_data_fn(png_structp png_ptr, png_bytep data, png_size_t length)
247
+ {
248
+ VALUE io, str;
249
+ size_t read_len;
250
+
251
+ if (png_ptr == NULL)
252
+ return;
253
+
254
+ io = (VALUE)png_get_io_ptr(png_ptr);
255
+ str = rb_funcall(io, id_read, 1, INT2FIX(length));
256
+
257
+ if (NIL_P(str))
258
+ rb_raise(rb_eRuntimeError, "Read Error. Reader returned nil.");
259
+
260
+ StringValue(str);
261
+ read_len = RSTRING_LEN(str);
262
+
263
+ if (read_len != length)
264
+ rb_raise(rb_eRuntimeError, "Read Error. Read %d instead of %d bytes.",
265
+ (int)read_len, (int)length);
266
+
267
+ memcpy(data, RSTRING_PTR(str), read_len);
268
+ }
269
+
270
+ static void
271
+ mark(struct png_data *reader)
272
+ {
273
+ VALUE io = reader->io;
274
+
275
+ if (io)
276
+ rb_gc_mark(io);
277
+ }
278
+
279
+ static void
280
+ allocate_png(struct png_data *reader)
281
+ {
282
+ png_structp png_ptr;
283
+ png_infop info_ptr;
284
+
285
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
286
+ (png_error_ptr)png_error_fn,
287
+ (png_error_ptr)png_warning_fn);
288
+
289
+ if (png_ptr == NULL)
290
+ rb_raise(rb_eRuntimeError, "unable to allocate a png object");
291
+
292
+ info_ptr = png_create_info_struct(png_ptr);
293
+ if (info_ptr == NULL) {
294
+ png_destroy_read_struct(&png_ptr, (png_info **)NULL, (png_info **)NULL);
295
+ rb_raise(rb_eRuntimeError, "unable to allocate a png info object");
296
+ }
297
+
298
+ reader->png_ptr = png_ptr;
299
+ reader->info_ptr = info_ptr;
300
+ }
301
+
302
+ static VALUE
303
+ allocate(VALUE klass)
304
+ {
305
+ VALUE self;
306
+ struct png_data *reader;
307
+
308
+ self = Data_Make_Struct(klass, struct png_data, mark, free_reader, reader);
309
+ allocate_png(reader);
310
+
311
+ return self;
312
+ }
313
+
314
+ /*
315
+ * call-seq:
316
+ * Reader.new(io_in) -> reader
317
+ *
318
+ * Creates a new PNG Reader. +io_in+ must be an IO-like object that responds
319
+ * to read(size).
320
+ *
321
+ * io = File.open("image.png", "r")
322
+ * reader = Axon::PNG::Reader.new(io)
323
+ */
324
+
325
+ static VALUE
326
+ initialize(VALUE self, VALUE io)
327
+ {
328
+ struct png_data *reader;
329
+ png_structp png_ptr;
330
+ png_infop info_ptr;
331
+
332
+ Data_Get_Struct(self, struct png_data, reader);
333
+ png_ptr = reader->png_ptr;
334
+ info_ptr = reader->info_ptr;
335
+
336
+ raise_if_locked(reader);
337
+ png_set_read_fn(png_ptr, (void *)io, read_data_fn);
338
+ reader->io = io;
339
+ png_read_info(png_ptr, info_ptr);
340
+
341
+ return self;
342
+ }
343
+
344
+ /*
345
+ * call-seq:
346
+ * reader.components -> number
347
+ *
348
+ * Retrieve the number of components as stored in the PNG image.
349
+ */
350
+
351
+ static VALUE
352
+ components(VALUE self)
353
+ {
354
+ struct png_data *reader;
355
+ png_structp png_ptr;
356
+ png_infop info_ptr;
357
+ int c;
358
+
359
+ Data_Get_Struct(self, struct png_data, reader);
360
+ png_ptr = reader->png_ptr;
361
+ info_ptr = reader->info_ptr;
362
+
363
+ c = get_components(png_ptr, info_ptr);
364
+
365
+ if (c == 0)
366
+ return Qnil;
367
+
368
+ return INT2FIX(c);
369
+ }
370
+
371
+ static ID
372
+ png_color_type_to_id(png_byte color_type)
373
+ {
374
+ switch (color_type) {
375
+ case PNG_COLOR_TYPE_GRAY: return id_GRAYSCALE;
376
+ case PNG_COLOR_TYPE_GRAY_ALPHA: return id_GRAYSCALE;
377
+ case PNG_COLOR_TYPE_RGB: return id_RGB;
378
+ case PNG_COLOR_TYPE_RGB_ALPHA: return id_RGB;
379
+ }
380
+
381
+ rb_raise(rb_eRuntimeError, "PNG Color Type not recognized.");
382
+ }
383
+
384
+ /*
385
+ * call-seq:
386
+ * reader.color_model -> symbol
387
+ *
388
+ * Returns a symbol representing the color model into which the PNG will be
389
+ * read.
390
+ *
391
+ * Possible color models are: :GRAYSCALE and :RGB.
392
+ */
393
+
394
+ static VALUE
395
+ color_model(VALUE self)
396
+ {
397
+ struct png_data *reader;
398
+ png_structp png_ptr;
399
+ png_infop info_ptr;
400
+
401
+ Data_Get_Struct(self, struct png_data, reader);
402
+ png_ptr = reader->png_ptr;
403
+ info_ptr = reader->info_ptr;
404
+
405
+ return ID2SYM(png_color_type_to_id(png_get_color_type(png_ptr, info_ptr)));
406
+ }
407
+
408
+ /*
409
+ * call-seq:
410
+ * gets -> string or nil
411
+ *
412
+ * Reads the next scanline of data from the image.
413
+ *
414
+ * If the end of the image has been reached, this will return nil.
415
+ */
416
+
417
+ static VALUE
418
+ p_gets(VALUE self)
419
+ {
420
+ struct png_data *reader;
421
+ png_structp png_ptr;
422
+ png_infop info_ptr;
423
+ int width, components, sl_width;
424
+ size_t height;
425
+ VALUE sl;
426
+
427
+ Data_Get_Struct(self, struct png_data, reader);
428
+ png_ptr = reader->png_ptr;
429
+ info_ptr = reader->info_ptr;
430
+
431
+ height = png_get_image_height(png_ptr, info_ptr);
432
+ if (reader->lineno >= height)
433
+ return Qnil;
434
+
435
+ width = png_get_image_width(png_ptr, info_ptr);
436
+ components = get_components(png_ptr, info_ptr);
437
+ sl_width = width * components;
438
+
439
+ sl = rb_str_new(0, sl_width);
440
+ png_read_row(png_ptr, (png_bytep)RSTRING_PTR(sl), (png_bytep)NULL);
441
+ reader->lineno += 1;
442
+
443
+ if (reader->lineno >= height)
444
+ png_read_end(png_ptr, info_ptr);
445
+
446
+ return sl;
447
+ }
448
+
449
+ /*
450
+ * call-seq:
451
+ * reader.width -> number
452
+ *
453
+ * Retrieve the width of the image.
454
+ */
455
+
456
+ static VALUE
457
+ width(VALUE self)
458
+ {
459
+ struct png_data *reader;
460
+ png_structp png_ptr;
461
+ png_infop info_ptr;
462
+
463
+ Data_Get_Struct(self, struct png_data, reader);
464
+ png_ptr = reader->png_ptr;
465
+ info_ptr = reader->info_ptr;
466
+
467
+ return INT2FIX(png_get_image_width(png_ptr, info_ptr));
468
+ }
469
+
470
+ /*
471
+ * call-seq:
472
+ * reader.height -> number
473
+ *
474
+ * Retrieve the height of the image.
475
+ */
476
+
477
+ static VALUE
478
+ height(VALUE self)
479
+ {
480
+ struct png_data *reader;
481
+ png_structp png_ptr;
482
+ png_infop info_ptr;
483
+
484
+ Data_Get_Struct(self, struct png_data, reader);
485
+ png_ptr = reader->png_ptr;
486
+ info_ptr = reader->info_ptr;
487
+
488
+ return INT2FIX(png_get_image_height(png_ptr, info_ptr));
489
+ }
490
+
491
+ /*
492
+ * call-seq:
493
+ * reader.lineno -> number
494
+ *
495
+ * Returns the number of the next line to be read from the image, starting at
496
+ * 0.
497
+ */
498
+
499
+ static VALUE
500
+ lineno(VALUE self)
501
+ {
502
+ struct png_data *reader;
503
+ Data_Get_Struct(self, struct png_data, reader);
504
+ return INT2FIX(reader->lineno);
505
+ }
506
+
507
+ /*
508
+ * Document-class: Axon::PNG::Reader
509
+ *
510
+ * Read compressed PNG images from an IO.
511
+ */
512
+
513
+ void
514
+ Init_PNG()
515
+ {
516
+ VALUE mAxon, mPNG, cPNGReader;
517
+
518
+ mAxon = rb_define_module("Axon");
519
+ mPNG = rb_define_module_under(mAxon, "PNG");
520
+ rb_const_set(mPNG, rb_intern("LIB_VERSION"), INT2FIX(PNG_LIBPNG_VER));
521
+ rb_define_singleton_method(mPNG, "write", write_png, 2);
522
+
523
+ cPNGReader = rb_define_class_under(mPNG, "Reader", rb_cObject);
524
+ rb_define_alloc_func(cPNGReader, allocate);
525
+ rb_define_method(cPNGReader, "initialize", initialize, 1);
526
+ rb_define_method(cPNGReader, "color_model", color_model, 0);
527
+ rb_define_method(cPNGReader, "components", components, 0);
528
+ rb_define_method(cPNGReader, "width", width, 0);
529
+ rb_define_method(cPNGReader, "height", height, 0);
530
+ rb_define_method(cPNGReader, "gets", p_gets, 0);
531
+ rb_define_method(cPNGReader, "lineno", lineno, 0);
532
+
533
+ id_GRAYSCALE = rb_intern("GRAYSCALE");
534
+ id_RGB = rb_intern("RGB");
535
+ id_write = rb_intern("write");
536
+ id_read = rb_intern("read");
537
+ id_gets = rb_intern("gets");
538
+ id_width = rb_intern("width");
539
+ id_height = rb_intern("height");
540
+ id_color_model = rb_intern("color_model");
541
+ id_components = rb_intern("components");
542
+ }