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,774 @@
1
+ #include "jpeg_common.h"
2
+
3
+ static ID id_read, id_rewind, id_ISLOW, id_IFAST, id_FLOAT, id_DEFAULT,
4
+ id_FASTEST;
5
+
6
+ struct readerdata {
7
+ struct jpeg_decompress_struct cinfo;
8
+ struct jpeg_source_mgr mgr;
9
+
10
+ int header_read;
11
+ int locked;
12
+ int rewind_after_scanlines;
13
+
14
+ VALUE source_io;
15
+ VALUE buffer;
16
+ };
17
+
18
+ static void
19
+ raise_if_locked(struct readerdata *reader)
20
+ {
21
+ if (reader->locked)
22
+ rb_raise(rb_eRuntimeError, "Can't modify a locked Reader");
23
+ }
24
+
25
+ static void
26
+ deallocate(struct readerdata *reader)
27
+ {
28
+ jpeg_destroy_decompress(&reader->cinfo);
29
+ free(reader);
30
+ }
31
+
32
+ static void
33
+ mark(struct readerdata *reader)
34
+ {
35
+ if (!NIL_P(reader->source_io))
36
+ rb_gc_mark(reader->source_io);
37
+
38
+ if (!NIL_P(reader->buffer))
39
+ rb_gc_mark(reader->buffer);
40
+ }
41
+
42
+ /* Data Source Callbacks */
43
+
44
+ static void
45
+ init_source(j_decompress_ptr cinfo)
46
+ {
47
+ /* do nothing */
48
+ }
49
+
50
+ static void
51
+ term_source(j_decompress_ptr cinfo)
52
+ {
53
+ /* do nothing */
54
+ }
55
+
56
+ static void
57
+ set_input_buffer(struct readerdata *reader, VALUE string)
58
+ {
59
+ size_t nbytes = 0;
60
+ JOCTET *buffer;
61
+
62
+ if (!NIL_P(string)) {
63
+ StringValue(string);
64
+ OBJ_FREEZE(string);
65
+ nbytes = (size_t)RSTRING_LEN(string);
66
+ buffer = (JOCTET *)RSTRING_PTR(string);
67
+ reader->buffer = string;
68
+ }
69
+
70
+ if (!nbytes) {
71
+ nbytes = 2;
72
+ reader->buffer = rb_str_new(0, 2);
73
+
74
+ buffer = (JOCTET *)RSTRING_PTR(reader->buffer);
75
+ buffer[0] = (JOCTET) 0xFF;
76
+ buffer[1] = (JOCTET) JPEG_EOI;
77
+ }
78
+
79
+ reader->mgr.next_input_byte = buffer;
80
+ reader->mgr.bytes_in_buffer = nbytes;
81
+ }
82
+
83
+ static boolean
84
+ fill_input_buffer(j_decompress_ptr cinfo)
85
+ {
86
+ VALUE string;
87
+ struct readerdata *reader;
88
+
89
+ reader = (struct readerdata *)cinfo;
90
+ string = rb_funcall(reader->source_io, id_read, 0);
91
+ set_input_buffer(reader, string);
92
+
93
+ return TRUE;
94
+ }
95
+
96
+ static void
97
+ skip_input_data(j_decompress_ptr cinfo, long num_bytes)
98
+ {
99
+ struct jpeg_source_mgr * src = cinfo->src;
100
+
101
+ if (num_bytes > 0) {
102
+ while (num_bytes > (long) src->bytes_in_buffer) {
103
+ num_bytes -= (long) src->bytes_in_buffer;
104
+ (void) (*src->fill_input_buffer) (cinfo);
105
+ }
106
+ src->next_input_byte += (size_t) num_bytes;
107
+ src->bytes_in_buffer -= (size_t) num_bytes;
108
+ }
109
+ }
110
+
111
+ static VALUE
112
+ allocate(VALUE klass)
113
+ {
114
+ struct readerdata *reader;
115
+ VALUE self;
116
+
117
+ self = Data_Make_Struct(klass, struct readerdata, mark, deallocate, reader);
118
+
119
+ reader->cinfo.err = &jerr;
120
+ jpeg_create_decompress(&reader->cinfo);
121
+
122
+ reader->cinfo.src = &reader->mgr;
123
+ reader->mgr.init_source = init_source;
124
+ reader->mgr.fill_input_buffer = fill_input_buffer;
125
+ reader->mgr.skip_input_data = skip_input_data;
126
+ reader->mgr.resync_to_restart = jpeg_resync_to_restart;
127
+ reader->mgr.term_source = term_source;
128
+
129
+ return self;
130
+ }
131
+
132
+ static VALUE
133
+ read_header2(VALUE arg)
134
+ {
135
+ struct readerdata *reader;
136
+
137
+ reader = (struct readerdata *)arg;
138
+ jpeg_read_header(&reader->cinfo, TRUE);
139
+ reader->header_read = 1;
140
+
141
+ return Qnil;
142
+ }
143
+
144
+ static void
145
+ read_header(struct readerdata *reader, VALUE markers)
146
+ {
147
+ int i, marker_code, state;
148
+ j_decompress_ptr cinfo;
149
+
150
+ cinfo = &reader->cinfo;
151
+
152
+ if (!NIL_P(markers)) {
153
+ Check_Type(markers, T_ARRAY);
154
+ for (i = 0; i < RARRAY_LEN(markers); i++) {
155
+ marker_code = sym_to_marker_code(RARRAY_PTR(markers)[i]);
156
+ jpeg_save_markers(cinfo, marker_code, 0xFFFF);
157
+ }
158
+ }
159
+
160
+ rb_protect(read_header2, (VALUE)reader, &state);
161
+
162
+ if(state) {
163
+ jpeg_abort_decompress(&reader->cinfo);
164
+ rb_jump_tag(state);
165
+ }
166
+
167
+ jpeg_calc_output_dimensions(cinfo);
168
+ }
169
+
170
+ /*
171
+ * call-seq:
172
+ * Reader.new(io[, markers, rewind_after_scanlines]) -> reader
173
+ *
174
+ * Create a new JPEG Reader. string_or_io may be an object that responds to
175
+ * read or a string.
176
+ *
177
+ * markers should be an array of valid JPEG header marker symbols. Valid
178
+ * symbols are :APP0 through :APP15 and :COM.
179
+ *
180
+ * If performance is important, you can avoid reading all header markers by
181
+ * supplying an empty array, [].
182
+ *
183
+ * When markers are not specified, we read all known JPEG markers.
184
+ */
185
+ static VALUE
186
+ initialize(int argc, VALUE *argv, VALUE self)
187
+ {
188
+ struct readerdata *reader;
189
+ j_decompress_ptr cinfo;
190
+ VALUE io, markers, rewind_after_scanlines;
191
+ int i;
192
+
193
+ Data_Get_Struct(self, struct readerdata, reader);
194
+ raise_if_locked(reader);
195
+ cinfo = &reader->cinfo;
196
+
197
+ rb_scan_args(argc, argv, "12", &io, &markers, &rewind_after_scanlines);
198
+
199
+ reader->source_io = io;
200
+ reader->mgr.bytes_in_buffer = 0;
201
+
202
+ if(NIL_P(markers)) {
203
+ jpeg_save_markers(cinfo, JPEG_COM, 0xFFFF);
204
+
205
+ for (i = 0; i < 16; i++)
206
+ jpeg_save_markers(cinfo, JPEG_APP0 + i, 0xFFFF);
207
+ }
208
+
209
+ reader->rewind_after_scanlines = RTEST(rewind_after_scanlines);
210
+ read_header(reader, markers);
211
+
212
+ return self;
213
+ }
214
+
215
+ /*
216
+ * call-seq:
217
+ * reader.num_components -> number
218
+ *
219
+ * Retrieve the number of components as stored in the JPEG image.
220
+ */
221
+ static VALUE
222
+ components(VALUE self)
223
+ {
224
+ struct jpeg_decompress_struct * cinfo;
225
+
226
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
227
+
228
+ return INT2FIX(cinfo->num_components);
229
+ }
230
+
231
+ /*
232
+ * call-seq:
233
+ * reader.in_color_model -> symbol
234
+ *
235
+ * Returns a symbol representing the color space in which the JPEG is stored.
236
+ *
237
+ * This does not have to be set explicitly and can be relied upon when the file
238
+ * conforms to JFIF or Adobe conventions. Otherwise it is guessed.
239
+ *
240
+ * Possible color spaces are: GRAYSCALE, RGB, YCbCr, CMYK, and YCCK. This method
241
+ * will return nil if the color space is not recognized.
242
+ */
243
+ static VALUE
244
+ in_color_model(VALUE self)
245
+ {
246
+ struct jpeg_decompress_struct * cinfo;
247
+ ID id;
248
+
249
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
250
+ id = j_color_space_to_id(cinfo->jpeg_color_space);
251
+
252
+ return ID2SYM(id);
253
+ }
254
+
255
+ /*
256
+ * call-seq:
257
+ * reader.color_space = symbol
258
+ *
259
+ * Explicitly sets the color space the JPEG will be read in. This will override
260
+ * the guessed color space.
261
+ */
262
+ static VALUE
263
+ set_in_color_model(VALUE self, VALUE cs)
264
+ {
265
+ struct readerdata *reader;
266
+
267
+ Data_Get_Struct(self, struct readerdata, reader);
268
+ raise_if_locked(reader);
269
+ reader->cinfo.jpeg_color_space = id_to_j_color_space(SYM2ID(cs));
270
+
271
+ return cs;
272
+ }
273
+
274
+ /*
275
+ * call-seq:
276
+ * reader.color_model -> symbol
277
+ *
278
+ * Returns a symbol representing the color space into which the JPEG will be
279
+ * transformed as it is read.
280
+ *
281
+ * By default this color space is based on Reader#color_space.
282
+ *
283
+ * Possible color spaces are: GRAYSCALE, RGB, YCbCr, CMYK, and YCCK. This method
284
+ * will return nil if the color space is not recognized.
285
+ */
286
+ static VALUE
287
+ color_model(VALUE self)
288
+ {
289
+ ID id;
290
+ struct jpeg_decompress_struct * cinfo;
291
+
292
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
293
+
294
+ id = j_color_space_to_id(cinfo->out_color_space);
295
+ return ID2SYM(id);
296
+ }
297
+
298
+ /*
299
+ * call-seq:
300
+ * reader.color_model = symbol
301
+ *
302
+ * Explicitly sets the color space to which the JPEG will be transformed as it
303
+ * is read.
304
+ *
305
+ * Legal transformations are:
306
+ * YCbCr to GRAYSCALE, YCbCr to RGB, GRAYSCALE to RGB, and YCCK to CMYK
307
+ */
308
+ static VALUE
309
+ set_color_model(VALUE self, VALUE cs)
310
+ {
311
+ struct readerdata *reader;
312
+
313
+ Data_Get_Struct(self, struct readerdata, reader);
314
+ raise_if_locked(reader);
315
+
316
+ reader->cinfo.out_color_space = id_to_j_color_space(SYM2ID(cs));
317
+ return cs;
318
+ }
319
+
320
+ /*
321
+ * call-seq:
322
+ * reader.scale_num -> number
323
+ *
324
+ * Retrieve the numerator of the fraction by which the JPEG will be scaled as
325
+ * it is read. This is always 1 for libjpeg version 6b. In version 8b this can
326
+ * be 1 to 16.
327
+ */
328
+ static VALUE
329
+ scale_num(VALUE self)
330
+ {
331
+ struct jpeg_decompress_struct * cinfo;
332
+
333
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
334
+ return INT2FIX(cinfo->scale_num);
335
+ }
336
+
337
+ /*
338
+ * call-seq:
339
+ * reader.scale_num = number
340
+ *
341
+ * Set the numerator of the fraction by which the JPEG will be scaled as it is
342
+ * read. This must always be 1 for libjpeg version 6b. In version 8b this can
343
+ * be set to 1 through 16.
344
+ */
345
+ static VALUE
346
+ set_scale_num(VALUE self, VALUE scale_num)
347
+ {
348
+ struct readerdata *reader;
349
+
350
+ Data_Get_Struct(self, struct readerdata, reader);
351
+ raise_if_locked(reader);
352
+
353
+ reader->cinfo.scale_num = FIX2INT(scale_num);
354
+ jpeg_calc_output_dimensions(&reader->cinfo);
355
+ return scale_num;
356
+ }
357
+
358
+ /*
359
+ * call-seq:
360
+ * reader.scale_denom -> number
361
+ *
362
+ * Retrieve the denominator of the fraction by which the JPEG will be scaled as
363
+ * it is read. This is 1, 2, 4, or 8 for libjpeg version 6b. In version 8b this
364
+ * is always the source DCT size, which is 8 for baseline JPEG.
365
+ */
366
+ static VALUE
367
+ scale_denom(VALUE self)
368
+ {
369
+ struct jpeg_decompress_struct * cinfo;
370
+
371
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
372
+
373
+ return INT2FIX(cinfo->scale_denom);
374
+ }
375
+
376
+ /*
377
+ * call-seq:
378
+ * reader.scale_denom = number
379
+ *
380
+ * Set the denominator of the fraction by which the JPEG will be scaled as it is
381
+ * read. This can be set to 1, 2, 4, or 8 for libjpeg version 6b. In version 8b
382
+ * this must always be the source DCT size, which is 8 for baseline JPEG.
383
+ */
384
+ static VALUE
385
+ set_scale_denom(VALUE self, VALUE scale_denom)
386
+ {
387
+ struct readerdata *reader;
388
+
389
+ Data_Get_Struct(self, struct readerdata, reader);
390
+ raise_if_locked(reader);
391
+
392
+ reader->cinfo.scale_denom = FIX2INT(scale_denom);
393
+ jpeg_calc_output_dimensions(&reader->cinfo);
394
+ return scale_denom;
395
+ }
396
+
397
+ static ID
398
+ j_dct_method_to_id(J_DCT_METHOD dct_method)
399
+ {
400
+ switch (dct_method) {
401
+ case JDCT_ISLOW: return id_ISLOW;
402
+ case JDCT_IFAST: return id_IFAST;
403
+ case JDCT_FLOAT: return id_FLOAT;
404
+ }
405
+
406
+ return Qnil;
407
+ }
408
+
409
+ static J_DCT_METHOD
410
+ id_to_j_dct_method(ID rb)
411
+ {
412
+ if (rb == id_ISLOW) return JDCT_ISLOW;
413
+ else if (rb == id_IFAST) return JDCT_IFAST;
414
+ else if (rb == id_FLOAT) return JDCT_FLOAT;
415
+
416
+ return (J_DCT_METHOD)NULL;
417
+ }
418
+
419
+ /*
420
+ * call-seq:
421
+ * reader.dct_method -> symbol
422
+ *
423
+ * Returns a symbol representing the algorithm used for the DCT (discrete cosine
424
+ * transform) step in JPEG encoding.
425
+ *
426
+ * Possible DCT algorithms are: ISLOW, IFAST, and IFLOAT.
427
+ */
428
+ static VALUE
429
+ dct_method(VALUE self)
430
+ {
431
+ struct jpeg_decompress_struct * cinfo;
432
+ ID id;
433
+
434
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
435
+
436
+ id = j_dct_method_to_id(cinfo->dct_method);
437
+
438
+ if (NIL_P(id))
439
+ return Qnil;
440
+ else
441
+ return ID2SYM(id);
442
+ }
443
+
444
+ /*
445
+ * call-seq:
446
+ * reader.dct_method = symbol
447
+ *
448
+ * Sets the algorithm used for the DCT step in JPEG encoding.
449
+ */
450
+ static VALUE
451
+ set_dct_method(VALUE self, VALUE dct_method)
452
+ {
453
+ struct readerdata *reader;
454
+ J_DCT_METHOD val;
455
+
456
+ Data_Get_Struct(self, struct readerdata, reader);
457
+ raise_if_locked(reader);
458
+
459
+ val = id_to_j_dct_method(SYM2ID(dct_method));
460
+ if (val == (J_DCT_METHOD)NULL) {
461
+ return Qnil;
462
+ } else {
463
+ reader->cinfo.dct_method = val;
464
+ return dct_method;
465
+ }
466
+ }
467
+
468
+ static VALUE
469
+ each3(VALUE arg)
470
+ {
471
+ struct jpeg_decompress_struct * cinfo;
472
+ struct readerdata *reader;
473
+ VALUE sl;
474
+ int width, height, components, sl_width, i;
475
+ JSAMPROW ijg_buffer;
476
+
477
+ reader = (struct readerdata *)arg;
478
+ cinfo = &reader->cinfo;
479
+
480
+ width = cinfo->output_width;
481
+ height = cinfo->output_height;
482
+ components = cinfo->output_components;
483
+
484
+ sl_width = width * components;
485
+
486
+ for (i = 0; i < height; i++) {
487
+ sl = rb_str_new(0, sl_width);
488
+ ijg_buffer = (JSAMPROW)RSTRING_PTR(sl);
489
+ jpeg_read_scanlines(cinfo, &ijg_buffer, 1);
490
+
491
+ if (rb_block_given_p())
492
+ rb_yield(sl);
493
+ }
494
+
495
+ jpeg_finish_decompress(cinfo);
496
+
497
+ return Qnil;
498
+ }
499
+
500
+ static VALUE
501
+ each3_rescue(VALUE arg)
502
+ {
503
+ struct readerdata *reader;
504
+
505
+ reader = (struct readerdata *)arg;
506
+ jpeg_abort_decompress(&reader->cinfo);
507
+
508
+ return Qnil;
509
+ }
510
+
511
+ static VALUE
512
+ each2(VALUE arg)
513
+ {
514
+ rb_rescue(each3, arg, each3_rescue, arg);
515
+ }
516
+
517
+ static VALUE
518
+ each2_ensure(VALUE arg)
519
+ {
520
+ struct readerdata *reader;
521
+
522
+ reader = (struct readerdata *)arg;
523
+ reader->locked = 0;
524
+ reader->header_read = 0;
525
+ if (reader->rewind_after_scanlines)
526
+ rb_funcall(reader->source_io, id_rewind, 0);
527
+
528
+ return Qnil;
529
+ }
530
+
531
+ /*
532
+ * call-seq:
533
+ * reader.each_scanline(&block)
534
+ *
535
+ * Iterate over each decoded scanline in the JPEG image. During this operation
536
+ * the reader is locked, and you can't change any decoding parameters or re
537
+ * initialize the reader.
538
+ *
539
+ * Should a major exception occur (anything other than a StandardError), then
540
+ * the reader will be left in a locked state.
541
+ */
542
+ static VALUE
543
+ each(VALUE self)
544
+ {
545
+ struct jpeg_decompress_struct * cinfo;
546
+ struct readerdata *reader;
547
+
548
+ Data_Get_Struct(self, struct readerdata, reader);
549
+ raise_if_locked(reader);
550
+
551
+ if (!reader->header_read)
552
+ read_header(reader, Qnil);
553
+
554
+ reader->locked = 1;
555
+ jpeg_start_decompress(&reader->cinfo);
556
+
557
+ rb_ensure(each2, (VALUE)reader, each2_ensure, (VALUE)reader);
558
+
559
+ return self;
560
+ }
561
+
562
+ /*
563
+ * call-seq:
564
+ * reader.width -> number
565
+ *
566
+ * Retrieve the width of the image as it will be written out. This is primarily
567
+ * affected by scale_num and scale_denom if they are set.
568
+ *
569
+ * Note that this value is not automatically calculated unless you call
570
+ * Reader#calc_output_dimensions or after Reader#each_scanline has been called.
571
+ */
572
+ static VALUE
573
+ width(VALUE self)
574
+ {
575
+ struct jpeg_decompress_struct * cinfo;
576
+
577
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
578
+
579
+ return INT2FIX(cinfo->output_width);
580
+ }
581
+
582
+ /*
583
+ * call-seq:
584
+ * reader.height -> number
585
+ *
586
+ * Retrieve the height of the image as it will be written out. This is primarily
587
+ * affected by scale_num and scale_denom if they are set.
588
+ *
589
+ * Note that this value is not automatically calculated unless you call
590
+ * Reader#calc_output_dimensions or after Reader#each_scanline has been called.
591
+ */
592
+ static VALUE
593
+ height(VALUE self)
594
+ {
595
+ struct jpeg_decompress_struct * cinfo;
596
+
597
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
598
+
599
+ return INT2FIX(cinfo->output_height);
600
+ }
601
+
602
+ /*
603
+ * call-seq:
604
+ * reader.icc_profile -> string
605
+ *
606
+ * Read the icc_profile from the JPEG. This requires that the APP2 segment
607
+ * has been selected by save_markers (this is the default behaviour).
608
+ */
609
+ static VALUE
610
+ icc_profile(VALUE self)
611
+ {
612
+ struct jpeg_decompress_struct * cinfo;
613
+ JOCTET *icc_embed_buffer;
614
+ unsigned int icc_embed_len;
615
+ VALUE str;
616
+
617
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
618
+ read_icc_profile(cinfo, &icc_embed_buffer, &icc_embed_len);
619
+
620
+ if (icc_embed_len <= 0) {
621
+ return Qnil;
622
+ } else {
623
+ str = rb_str_new(icc_embed_buffer, icc_embed_len);
624
+ free(icc_embed_buffer);
625
+ return str;
626
+ }
627
+ }
628
+
629
+ static boolean
630
+ marker_is_exif(jpeg_saved_marker_ptr marker)
631
+ {
632
+ return marker->marker == EXIF_MARKER &&
633
+ marker->data_length >= EXIF_OVERHEAD_LEN &&
634
+ /* verify the identifying string */
635
+ GETJOCTET(marker->data[0]) == 0x45 &&
636
+ GETJOCTET(marker->data[1]) == 0x78 &&
637
+ GETJOCTET(marker->data[2]) == 0x69 &&
638
+ GETJOCTET(marker->data[3]) == 0x66 &&
639
+ GETJOCTET(marker->data[4]) == 0x0 &&
640
+ GETJOCTET(marker->data[5]) == 0x0;
641
+ }
642
+
643
+ /*
644
+ * call-seq:
645
+ * reader.exif -> string
646
+ *
647
+ * Read the Exif data from the JPEG. This requires that the APP1 segment
648
+ * has been selected by save_markers (this is the default behaviour).
649
+ */
650
+ static VALUE
651
+ exif(VALUE self)
652
+ {
653
+ struct jpeg_decompress_struct * cinfo;
654
+ jpeg_saved_marker_ptr marker;
655
+ int len;
656
+
657
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
658
+
659
+ for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
660
+ if (marker_is_exif(marker)) {
661
+ len = marker->data_length - EXIF_OVERHEAD_LEN;
662
+ return rb_str_new(marker->data + EXIF_OVERHEAD_LEN, len);
663
+ }
664
+ }
665
+
666
+ return Qnil;
667
+ }
668
+
669
+ /*
670
+ * call-seq:
671
+ * reader[marker] -> array
672
+ *
673
+ * Read raw data from the given JPEG header marker. Note that the marker must
674
+ * have been specified by Reader#save_markers prior to the call to
675
+ * Reader#read_header.
676
+ *
677
+ * The return from this method is an array, since there may be multiple
678
+ * instances of a single marker in a JPEG header.
679
+ */
680
+ static VALUE
681
+ aref(VALUE self, VALUE marker_sym)
682
+ {
683
+ struct jpeg_decompress_struct * cinfo;
684
+ jpeg_saved_marker_ptr marker;
685
+ VALUE ary = rb_ary_new();
686
+ int marker_i = sym_to_marker_code(marker_sym);
687
+
688
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
689
+
690
+ for (marker = cinfo->marker_list; marker != NULL; marker = marker->next)
691
+ if (marker->marker == marker_i)
692
+ rb_ary_push(ary, rb_str_new(marker->data, marker->data_length));
693
+
694
+ return ary;
695
+ }
696
+
697
+ /*
698
+ * call-seq:
699
+ * reader.saw_jfif_marker -> boolean
700
+ *
701
+ * Indicate that a JFIF marker was found in the header.
702
+ */
703
+ static VALUE
704
+ saw_jfif_marker(VALUE self)
705
+ {
706
+ struct jpeg_decompress_struct * cinfo;
707
+
708
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
709
+
710
+ return cinfo->saw_JFIF_marker ? Qtrue : Qfalse;
711
+ }
712
+
713
+ /*
714
+ * call-seq:
715
+ * reader.saw_adobe_marker -> boolean
716
+ *
717
+ * Indicate that an Adobe marker was found in the header.
718
+ */
719
+ static VALUE
720
+ saw_adobe_marker(VALUE self)
721
+ {
722
+ struct jpeg_decompress_struct * cinfo;
723
+
724
+ Data_Get_Struct(self, struct jpeg_decompress_struct, cinfo);
725
+
726
+ return cinfo->saw_Adobe_marker ? Qtrue : Qfalse;
727
+ }
728
+
729
+ void
730
+ Init_jpeg_reader()
731
+ {
732
+ VALUE mAxon = rb_define_module("Axon");
733
+ VALUE cJPEGReader = rb_define_class_under(mAxon, "JPEGReader", rb_cObject);
734
+ VALUE mImage = rb_define_module_under(mAxon, "Image");
735
+
736
+ rb_include_module(cJPEGReader, mImage);
737
+ rb_include_module(cJPEGReader, rb_mEnumerable);
738
+
739
+ rb_define_alloc_func(cJPEGReader, allocate);
740
+
741
+ rb_define_method(cJPEGReader, "initialize", initialize, -1);
742
+ rb_define_method(cJPEGReader, "icc_profile", icc_profile, 0);
743
+ rb_define_method(cJPEGReader, "exif", exif, 0);
744
+ rb_define_method(cJPEGReader, "saw_jfif_marker", saw_jfif_marker, 0);
745
+ rb_define_method(cJPEGReader, "saw_adobe_marker", saw_adobe_marker, 0);
746
+ rb_define_method(cJPEGReader, "[]", aref, 1);
747
+ rb_define_method(cJPEGReader, "in_color_model", in_color_model, 0);
748
+ rb_define_method(cJPEGReader, "in_color_model=", set_in_color_model, 1);
749
+ rb_define_method(cJPEGReader, "color_model", color_model, 0);
750
+ rb_define_method(cJPEGReader, "color_model=", set_color_model, 1);
751
+ rb_define_method(cJPEGReader, "components", components, 0);
752
+ rb_define_method(cJPEGReader, "scale_num", scale_num, 0);
753
+ rb_define_method(cJPEGReader, "scale_num=", set_scale_num, 1);
754
+ rb_define_method(cJPEGReader, "scale_denom", scale_denom, 0);
755
+ rb_define_method(cJPEGReader, "scale_denom=", set_scale_denom, 1);
756
+ rb_define_method(cJPEGReader, "dct_method", dct_method, 0);
757
+ rb_define_method(cJPEGReader, "dct_method=", set_dct_method, 1);
758
+ rb_define_method(cJPEGReader, "width", width, 0);
759
+ rb_define_method(cJPEGReader, "height", height, 0);
760
+ rb_define_method(cJPEGReader, "each", each, 0);
761
+
762
+ id_read = rb_intern("read");
763
+ id_rewind = rb_intern("rewind");
764
+ id_IFAST = rb_intern("IFAST");
765
+ id_ISLOW = rb_intern("ISLOW");
766
+ id_FLOAT = rb_intern("FLOAT");
767
+ id_DEFAULT = rb_intern("DEFAULT");
768
+ id_FASTEST = rb_intern("FASTEST");
769
+
770
+ rb_const_set(cJPEGReader, rb_intern("DEFAULT_DCT"),
771
+ ID2SYM(j_dct_method_to_id(JDCT_DEFAULT)));
772
+ rb_const_set(cJPEGReader, rb_intern("FASTEST_DCT"),
773
+ ID2SYM(j_dct_method_to_id(JDCT_FASTEST)));
774
+ }