axon 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }