image-file 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,12 @@
1
+ #include "internal.h"
2
+
3
+ VALUE mImageFile = Qnil;
4
+
5
+ void
6
+ Init_image_file(void)
7
+ {
8
+ mImageFile = rb_define_module("ImageFile");
9
+
10
+ rb_image_file_Init_image_file_image();
11
+ rb_image_file_Init_image_file_jpeg_reader();
12
+ }
@@ -0,0 +1,79 @@
1
+ #include <ruby.h>
2
+
3
+
4
+ #include <assert.h>
5
+
6
+ #undef ARG_UNUSED
7
+ #ifdef __GNUC__
8
+ # define ARG_UNUSED __attribute__ ((unused))
9
+ #else
10
+ # define ARG_UNUSED
11
+ #endif
12
+
13
+ #ifndef HAVE_UINT16_T
14
+ # if SIZEOF_SHORT == 2
15
+ typedef unsigned short uint16_t;
16
+ # elif SIZEOF_INT == 2
17
+ typedef unsigned int uint16_t;
18
+ # elif SIZEOF_CHAR == 2
19
+ typedef unsigned char uint16_t;
20
+ # elif SIZEOF_LONG == 2
21
+ typedef unsigned long uint16_t;
22
+ # else
23
+ # error needs 16-bit integer type
24
+ # endif
25
+ #endif
26
+
27
+ #ifndef HAVE_UINT32_T
28
+ # if SIZEOF_INT == 4
29
+ typedef unsigned int uint32_t;
30
+ # elif SIZEOF_LONG == 4
31
+ typedef unsigned long uint32_t;
32
+ # elif SIZEOF_SHORT == 4
33
+ typedef unsigned short uint32_t;
34
+ # elif SIZEOF_LONG_LONG == 4
35
+ typedef unsigned LONG_LONG uint32_t;
36
+ # else
37
+ # error needs 32-bit integer type
38
+ # endif
39
+ #endif
40
+
41
+ RUBY_EXTERN VALUE rb_image_file_mImageFile;
42
+ RUBY_EXTERN VALUE rb_image_file_cImageFileImage;
43
+ RUBY_EXTERN VALUE rb_image_file_cImageFileJpegReader;
44
+ RUBY_EXTERN VALUE rb_image_file_eImageFileJpegReaderError;
45
+
46
+ #define mImageFile rb_image_file_mImageFile
47
+ #define cImageFileImage rb_image_file_cImageFileImage
48
+ #define cImageFileJpegReader rb_image_file_cImageFileJpegReader
49
+ #define eImageFileJpegReaderError rb_image_file_eImageFileJpegReaderError
50
+
51
+ typedef enum {
52
+ RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_INVALID = -1,
53
+ RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_ARGB32 = 0,
54
+ RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_RGB24 = 1,
55
+ RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_RGB16_565 = 4,
56
+ } rb_image_file_image_pixel_format_t;
57
+
58
+ rb_image_file_image_pixel_format_t rb_image_file_image_symbol_to_pixel_format(VALUE symbol);
59
+ VALUE rb_image_file_image_pixel_format_to_symbol(rb_image_file_image_pixel_format_t const pf);
60
+ VALUE rb_image_file_image_get_buffer(VALUE obj);
61
+
62
+ void rb_image_file_Init_image_file_image(void);
63
+ void rb_image_file_Init_image_file_jpeg_reader(void);
64
+
65
+ static inline int
66
+ file_p(VALUE fname)
67
+ {
68
+ ID id_file_p;
69
+ CONST_ID(id_file_p, "file?");
70
+ return RTEST(rb_funcall(rb_cFile, id_file_p, 1, fname));
71
+ }
72
+
73
+ static inline void
74
+ check_file_not_found(VALUE fname)
75
+ {
76
+ if (!file_p(fname)) {
77
+ rb_raise(rb_eArgError, "file not found");
78
+ }
79
+ }
@@ -0,0 +1,938 @@
1
+ #include "internal.h"
2
+
3
+ #undef EXTERN
4
+ #include <jpeglib.h>
5
+ #include <jerror.h>
6
+
7
+ static size_t const INPUT_BUFFER_SIZE = 4096U;
8
+
9
+ VALUE cImageFileJpegReader = Qnil;
10
+ VALUE eImageFileJpegReaderError = Qnil;
11
+
12
+ static ID id_GRAYSCALE;
13
+ static ID id_RGB;
14
+ static ID id_YCbCr;
15
+ static ID id_CMYK;
16
+ static ID id_YCCK;
17
+ static ID id_ISLOW;
18
+ static ID id_IFAST;
19
+ static ID id_FLOAT;
20
+ static ID id_NONE;
21
+ static ID id_ORDERED;
22
+ static ID id_FS;
23
+ static ID id_new;
24
+ static ID id_close;
25
+ static ID id_read;
26
+ static ID id_pixel_format;
27
+ static ID id_width;
28
+ static ID id_height;
29
+ static ID id_row_stride;
30
+
31
+ static inline char const*
32
+ j_color_space_name(J_COLOR_SPACE const color_space)
33
+ {
34
+ switch (color_space) {
35
+ case JCS_GRAYSCALE:
36
+ return "JCS_GRAYSCALE";
37
+
38
+ case JCS_RGB:
39
+ return "JCS_RGB";
40
+
41
+ case JCS_UNKNOWN:
42
+ return "JCS_UNKNOWN";
43
+
44
+ case JCS_YCbCr:
45
+ return "JCS_YCbCr";
46
+
47
+ case JCS_CMYK:
48
+ return "JCS_CMYK";
49
+
50
+ case JCS_YCCK:
51
+ return "JCS_YCCK";
52
+
53
+ default:
54
+ break;
55
+ }
56
+ assert(0); /* MUST NOT REACH HERE */
57
+ return "(unknown color space)";
58
+ }
59
+
60
+ static rb_image_file_image_pixel_format_t
61
+ j_color_space_to_image_pixel_format(J_COLOR_SPACE const color_space)
62
+ {
63
+ switch (color_space) {
64
+ case JCS_GRAYSCALE:
65
+ case JCS_RGB:
66
+ return RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_RGB24;
67
+
68
+ case JCS_UNKNOWN:
69
+ case JCS_YCbCr:
70
+ case JCS_CMYK:
71
+ case JCS_YCCK:
72
+ return RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_INVALID;
73
+
74
+ default:
75
+ break;
76
+ }
77
+ assert(0); /* MUST NOT REACH HERE */
78
+ return RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_INVALID;
79
+ }
80
+
81
+ static J_COLOR_SPACE
82
+ image_pixel_format_to_j_color_space(rb_image_file_image_pixel_format_t const pf)
83
+ {
84
+ switch (pf) {
85
+ case RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_INVALID:
86
+ return JCS_UNKNOWN;
87
+
88
+ case RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_ARGB32:
89
+ case RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_RGB24:
90
+ case RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_RGB16_565:
91
+ return JCS_RGB;
92
+
93
+ default:
94
+ break;
95
+ }
96
+ assert(0); /* MUST NOT REACH HERE */
97
+ return JCS_UNKNOWN;
98
+ }
99
+
100
+ static VALUE
101
+ color_space_to_symbol(J_COLOR_SPACE const color_space)
102
+ {
103
+ switch (color_space) {
104
+ case JCS_UNKNOWN:
105
+ return Qnil;
106
+
107
+ case JCS_GRAYSCALE:
108
+ return ID2SYM(id_GRAYSCALE);
109
+
110
+ case JCS_RGB:
111
+ return ID2SYM(id_RGB);
112
+
113
+ case JCS_YCbCr:
114
+ return ID2SYM(id_YCbCr);
115
+
116
+ case JCS_CMYK:
117
+ return ID2SYM(id_CMYK);
118
+
119
+ case JCS_YCCK:
120
+ return ID2SYM(id_YCCK);
121
+
122
+ default:
123
+ break;
124
+ }
125
+ assert(0); /* MUST NOT REACH HERE */
126
+ return Qnil;
127
+ }
128
+
129
+ static J_COLOR_SPACE
130
+ id_to_color_space(ID const id)
131
+ {
132
+ if (id == id_GRAYSCALE)
133
+ return JCS_GRAYSCALE;
134
+ if (id == id_RGB)
135
+ return JCS_RGB;
136
+ if (id == id_YCbCr)
137
+ return JCS_YCbCr;
138
+ if (id == id_CMYK)
139
+ return JCS_CMYK;
140
+ if (id == id_YCCK)
141
+ return JCS_YCCK;
142
+ return JCS_UNKNOWN;
143
+ }
144
+
145
+ static VALUE
146
+ dct_method_to_symbol(J_DCT_METHOD const dct_method)
147
+ {
148
+ switch (dct_method) {
149
+ case JDCT_ISLOW:
150
+ return ID2SYM(id_ISLOW);
151
+
152
+ case JDCT_IFAST:
153
+ return ID2SYM(id_IFAST);
154
+
155
+ case JDCT_FLOAT:
156
+ return ID2SYM(id_FLOAT);
157
+
158
+ default:
159
+ break;
160
+ }
161
+ assert(0); /* DO NOT REACH HERE */
162
+ return Qnil;
163
+ }
164
+
165
+ static VALUE
166
+ dither_mode_to_symbol(J_DITHER_MODE const dither_mode)
167
+ {
168
+ switch (dither_mode) {
169
+ case JDITHER_NONE:
170
+ return ID2SYM(id_NONE);
171
+
172
+ case JDITHER_ORDERED:
173
+ return ID2SYM(id_ORDERED);
174
+
175
+ case JDITHER_FS:
176
+ return ID2SYM(id_FS);
177
+
178
+ default:
179
+ break;
180
+ }
181
+ assert(0); /* DO NOT REACH HERE */
182
+ return Qnil;
183
+ }
184
+
185
+ enum jpeg_reader_state {
186
+ READER_ALLOCATED = 0,
187
+ READER_INITIALIZED,
188
+ READER_RED_HEADER,
189
+ READER_STARTED_DECOMPRESS,
190
+ READER_FINISHED_DECOMPRESS
191
+ };
192
+
193
+ struct jpeg_reader_data {
194
+ struct jpeg_decompress_struct cinfo;
195
+ struct jpeg_error_mgr error;
196
+ VALUE source;
197
+ VALUE buffer;
198
+ enum jpeg_reader_state state;
199
+ unsigned close_source: 1;
200
+ unsigned start_of_file: 1;
201
+ };
202
+
203
+ static void
204
+ jpeg_reader_mark(void* ptr)
205
+ {
206
+ struct jpeg_reader_data* reader = (struct jpeg_reader_data*)ptr;
207
+ rb_gc_mark(reader->source);
208
+ rb_gc_mark(reader->buffer);
209
+ }
210
+
211
+ static void
212
+ jpeg_reader_free(void* ptr)
213
+ {
214
+ struct jpeg_reader_data* reader = (struct jpeg_reader_data*)ptr;
215
+
216
+ #if 0
217
+ if (reader->close_source && rb_respond_to(reader->source, id_close)) {
218
+ rb_funcall(reader->source, id_close, 0);
219
+ }
220
+ #endif
221
+ reader->source = Qnil;
222
+ jpeg_destroy_decompress(&reader->cinfo);
223
+ xfree(ptr);
224
+ }
225
+
226
+ static size_t
227
+ jpeg_reader_memsize(void const* ptr)
228
+ {
229
+ return ptr ? sizeof(struct jpeg_reader_data) : 0;
230
+ }
231
+
232
+ static rb_data_type_t const jpeg_reader_data_type = {
233
+ "image_file::jpeg_reader",
234
+ #if RUBY_VERSION >= 193
235
+ {
236
+ #endif
237
+ jpeg_reader_mark,
238
+ jpeg_reader_free,
239
+ jpeg_reader_memsize,
240
+ #if RUBY_VERSION >= 193
241
+ },
242
+ #endif
243
+ };
244
+
245
+ static VALUE
246
+ jpeg_reader_alloc(VALUE klass)
247
+ {
248
+ struct jpeg_reader_data* reader;
249
+ VALUE obj = TypedData_Make_Struct(
250
+ klass, struct jpeg_reader_data, &jpeg_reader_data_type, reader);
251
+ reader->source = Qnil;
252
+ reader->buffer = Qnil;
253
+ reader->state = READER_ALLOCATED;
254
+ reader->close_source = 0;
255
+ reader->start_of_file = 0;
256
+ return obj;
257
+ }
258
+
259
+ static struct jpeg_reader_data*
260
+ get_jpeg_reader_data(VALUE obj)
261
+ {
262
+ struct jpeg_reader_data* reader;
263
+ TypedData_Get_Struct(obj, struct jpeg_reader_data, &jpeg_reader_data_type, reader);
264
+ return reader;
265
+ }
266
+
267
+ static void
268
+ error_exit(j_common_ptr cinfo)
269
+ {
270
+ char message[JMSG_LENGTH_MAX];
271
+ (* cinfo->err->format_message)(cinfo, message);
272
+ rb_raise(eImageFileJpegReaderError, "%s", message);
273
+ }
274
+
275
+ static void
276
+ output_message(j_common_ptr cinfo)
277
+ {
278
+ char message[JMSG_LENGTH_MAX];
279
+ (* cinfo->err->format_message)(cinfo, message);
280
+ rb_warning("%s", message);
281
+ }
282
+
283
+ static struct jpeg_error_mgr*
284
+ init_error_mgr(struct jpeg_error_mgr* err)
285
+ {
286
+ jpeg_std_error(err);
287
+ err->error_exit = error_exit;
288
+ err->output_message = output_message;
289
+ return err;
290
+ }
291
+
292
+ static void
293
+ init_source(j_decompress_ptr cinfo)
294
+ {
295
+ VALUE obj;
296
+ struct jpeg_reader_data* reader;
297
+
298
+ assert(cinfo != NULL);
299
+
300
+ obj = (VALUE)cinfo->client_data;
301
+ reader = get_jpeg_reader_data(obj);
302
+ reader->start_of_file = 1;
303
+ }
304
+
305
+ static boolean
306
+ fill_input_buffer(j_decompress_ptr cinfo)
307
+ {
308
+ VALUE obj;
309
+ struct jpeg_reader_data* reader;
310
+
311
+ assert(cinfo != NULL);
312
+
313
+ obj = (VALUE)cinfo->client_data;
314
+ reader = get_jpeg_reader_data(obj);
315
+
316
+ reader->buffer = rb_funcall(reader->source, id_read, 1, INT2FIX(INPUT_BUFFER_SIZE));
317
+ if (NIL_P(reader->buffer)) { /* EOF */
318
+ if (reader->start_of_file) { /* empty file */
319
+ ERREXIT(cinfo, JERR_INPUT_EMPTY);
320
+ }
321
+ WARNMS(cinfo, JWRN_JPEG_EOF);
322
+ /* Insert fake EOI marker */
323
+ reader->buffer = rb_str_tmp_new(2);
324
+ RSTRING_PTR(reader->buffer)[0] = (JOCTET) 0xFF;
325
+ RSTRING_PTR(reader->buffer)[1] = (JOCTET) JPEG_EOI;
326
+ }
327
+
328
+ cinfo->src->next_input_byte = (JOCTET const*)RSTRING_PTR(reader->buffer);
329
+ cinfo->src->bytes_in_buffer = RSTRING_LEN(reader->buffer);
330
+ reader->start_of_file = 0;
331
+
332
+ return TRUE;
333
+ }
334
+
335
+ static void
336
+ skip_input_data(j_decompress_ptr cinfo, long num_bytes)
337
+ {
338
+ if (num_bytes > 0) {
339
+ while (num_bytes > (long)cinfo->src->bytes_in_buffer) {
340
+ num_bytes -= (long)cinfo->src->bytes_in_buffer;
341
+ (* cinfo->src->fill_input_buffer)(cinfo);
342
+ }
343
+ cinfo->src->next_input_byte += num_bytes;
344
+ cinfo->src->bytes_in_buffer -= num_bytes;
345
+ }
346
+ }
347
+
348
+ void
349
+ term_source(j_decompress_ptr cinfo ARG_UNUSED)
350
+ {
351
+ /* nothing to do */
352
+ }
353
+
354
+ static void
355
+ init_source_mgr(struct jpeg_reader_data* reader)
356
+ {
357
+ struct jpeg_source_mgr* src = NULL;
358
+
359
+ assert(reader != NULL);
360
+
361
+ if (reader->cinfo.src == NULL) {
362
+ reader->cinfo.src = (struct jpeg_source_mgr*)
363
+ (* reader->cinfo.mem->alloc_small)(
364
+ (j_common_ptr)&reader->cinfo,
365
+ JPOOL_PERMANENT,
366
+ sizeof(struct jpeg_source_mgr));
367
+ }
368
+
369
+ src = (struct jpeg_source_mgr*)reader->cinfo.src;
370
+ src->init_source = init_source;
371
+ src->fill_input_buffer = fill_input_buffer;
372
+ src->skip_input_data = skip_input_data;
373
+ src->resync_to_restart = jpeg_resync_to_restart;
374
+ src->term_source = term_source;
375
+ src->bytes_in_buffer = 0;
376
+ src->next_input_byte = NULL;
377
+ }
378
+
379
+ static VALUE
380
+ jpeg_reader_initialize(VALUE obj, VALUE source)
381
+ {
382
+ struct jpeg_reader_data* reader;
383
+ reader = get_jpeg_reader_data(obj);
384
+ reader->cinfo.err = init_error_mgr(&reader->error);
385
+ jpeg_create_decompress(&reader->cinfo);
386
+ init_source_mgr(reader);
387
+ reader->source = source;
388
+ reader->cinfo.client_data = (void*)obj;
389
+ reader->state = READER_INITIALIZED;
390
+ return obj;
391
+ }
392
+
393
+ static VALUE
394
+ jpeg_reader_s_open(VALUE klass, VALUE path)
395
+ {
396
+ struct jpeg_reader_data* reader;
397
+ VALUE obj, io;
398
+
399
+ io = rb_file_open_str(path, "rb");
400
+ obj = rb_funcall(klass, id_new, 1, io);
401
+ reader = get_jpeg_reader_data(obj);
402
+ reader->close_source = 1;
403
+
404
+ return obj;
405
+ }
406
+
407
+ static VALUE
408
+ jpeg_reader_source_will_be_closed(VALUE obj)
409
+ {
410
+ struct jpeg_reader_data* reader;
411
+ reader = get_jpeg_reader_data(obj);
412
+ return reader->close_source ? Qtrue : Qfalse;
413
+ }
414
+
415
+ static inline void
416
+ reader_check_initialized(struct jpeg_reader_data* reader)
417
+ {
418
+ assert(reader != NULL);
419
+ if (reader->state < READER_INITIALIZED) {
420
+ rb_raise(eImageFileJpegReaderError, "reader not initialized");
421
+ }
422
+ }
423
+
424
+ static inline void
425
+ read_header(struct jpeg_reader_data* reader)
426
+ {
427
+ assert(reader != NULL);
428
+ reader_check_initialized(reader);
429
+ if (reader->state < READER_RED_HEADER) {
430
+ jpeg_read_header(&reader->cinfo, TRUE);
431
+ reader->state = READER_RED_HEADER;
432
+ }
433
+ }
434
+
435
+ static VALUE
436
+ jpeg_reader_get_image_width(VALUE obj)
437
+ {
438
+ struct jpeg_reader_data* reader;
439
+ reader = get_jpeg_reader_data(obj);
440
+ read_header(reader);
441
+ return ULONG2NUM(reader->cinfo.image_width);
442
+ }
443
+
444
+ static VALUE
445
+ jpeg_reader_get_image_height(VALUE obj)
446
+ {
447
+ struct jpeg_reader_data* reader;
448
+ reader = get_jpeg_reader_data(obj);
449
+ read_header(reader);
450
+ return ULONG2NUM(reader->cinfo.image_height);
451
+ }
452
+
453
+ static VALUE
454
+ jpeg_reader_get_num_components(VALUE obj)
455
+ {
456
+ struct jpeg_reader_data* reader;
457
+ reader = get_jpeg_reader_data(obj);
458
+ read_header(reader);
459
+ return LONG2NUM(reader->cinfo.num_components);
460
+ }
461
+
462
+ static VALUE
463
+ jpeg_reader_get_jpeg_color_space(VALUE obj)
464
+ {
465
+ struct jpeg_reader_data* reader;
466
+ reader = get_jpeg_reader_data(obj);
467
+ read_header(reader);
468
+ return color_space_to_symbol(reader->cinfo.jpeg_color_space);
469
+ }
470
+
471
+ static VALUE
472
+ jpeg_reader_get_out_color_space(VALUE obj)
473
+ {
474
+ struct jpeg_reader_data* reader;
475
+ reader = get_jpeg_reader_data(obj);
476
+ read_header(reader);
477
+ return color_space_to_symbol(reader->cinfo.out_color_space);
478
+ }
479
+
480
+ static VALUE
481
+ jpeg_reader_get_scale(VALUE obj)
482
+ {
483
+ struct jpeg_reader_data* reader;
484
+ VALUE num, denom;
485
+ reader = get_jpeg_reader_data(obj);
486
+ read_header(reader);
487
+ num = UINT2NUM(reader->cinfo.scale_num);
488
+ denom = UINT2NUM(reader->cinfo.scale_denom);
489
+ return rb_Rational(num, denom);
490
+ }
491
+
492
+ static VALUE
493
+ jpeg_reader_set_scale(VALUE obj, VALUE scale)
494
+ {
495
+ struct jpeg_reader_data* reader;
496
+ VALUE r;
497
+ reader = get_jpeg_reader_data(obj);
498
+ read_header(reader);
499
+ r = rb_Rational1(scale);
500
+ reader->cinfo.scale_num = NUM2UINT(RRATIONAL(r)->num);
501
+ reader->cinfo.scale_denom = NUM2UINT(RRATIONAL(r)->den);
502
+ return scale;
503
+ }
504
+
505
+ static VALUE
506
+ jpeg_reader_get_output_gamma(VALUE obj)
507
+ {
508
+ struct jpeg_reader_data* reader;
509
+ reader = get_jpeg_reader_data(obj);
510
+ read_header(reader);
511
+ return DBL2NUM(reader->cinfo.output_gamma);
512
+ }
513
+
514
+ static VALUE
515
+ jpeg_reader_set_output_gamma(VALUE obj, VALUE gamma)
516
+ {
517
+ struct jpeg_reader_data* reader;
518
+ VALUE f;
519
+ reader = get_jpeg_reader_data(obj);
520
+ read_header(reader);
521
+ f = rb_Float(gamma);
522
+ reader->cinfo.output_gamma = RFLOAT_VALUE(f);
523
+ return gamma;
524
+ }
525
+
526
+ static VALUE
527
+ jpeg_reader_is_buffered_image(VALUE obj)
528
+ {
529
+ struct jpeg_reader_data* reader;
530
+ reader = get_jpeg_reader_data(obj);
531
+ read_header(reader);
532
+ return reader->cinfo.buffered_image ? Qtrue : Qfalse;
533
+ }
534
+
535
+ static VALUE
536
+ jpeg_reader_get_dct_method(VALUE obj)
537
+ {
538
+ struct jpeg_reader_data* reader;
539
+ reader = get_jpeg_reader_data(obj);
540
+ read_header(reader);
541
+ return dct_method_to_symbol(reader->cinfo.dct_method);
542
+ }
543
+
544
+ static VALUE
545
+ jpeg_reader_is_quantize_colors(VALUE obj)
546
+ {
547
+ struct jpeg_reader_data* reader;
548
+ reader = get_jpeg_reader_data(obj);
549
+ read_header(reader);
550
+ return reader->cinfo.quantize_colors ? Qtrue : Qfalse;
551
+ }
552
+
553
+ static VALUE
554
+ jpeg_reader_get_dither_mode(VALUE obj)
555
+ {
556
+ struct jpeg_reader_data* reader;
557
+ reader = get_jpeg_reader_data(obj);
558
+ read_header(reader);
559
+ return dither_mode_to_symbol(reader->cinfo.dither_mode);
560
+ }
561
+
562
+ static inline void
563
+ calc_output_dimensions(struct jpeg_reader_data* reader)
564
+ {
565
+ assert(reader != NULL);
566
+ if (reader->state < READER_STARTED_DECOMPRESS) {
567
+ read_header(reader);
568
+ jpeg_calc_output_dimensions(&reader->cinfo);
569
+ }
570
+ }
571
+
572
+ static VALUE
573
+ jpeg_reader_get_output_width(VALUE obj)
574
+ {
575
+ struct jpeg_reader_data* reader;
576
+ reader = get_jpeg_reader_data(obj);
577
+ calc_output_dimensions(reader);
578
+ return UINT2NUM(reader->cinfo.output_width);
579
+ }
580
+
581
+ static VALUE
582
+ jpeg_reader_get_output_height(VALUE obj)
583
+ {
584
+ struct jpeg_reader_data* reader;
585
+ reader = get_jpeg_reader_data(obj);
586
+ calc_output_dimensions(reader);
587
+ return UINT2NUM(reader->cinfo.output_height);
588
+ }
589
+
590
+ static VALUE
591
+ jpeg_reader_get_output_components(VALUE obj)
592
+ {
593
+ struct jpeg_reader_data* reader;
594
+ reader = get_jpeg_reader_data(obj);
595
+ calc_output_dimensions(reader);
596
+ return INT2NUM(reader->cinfo.output_components);
597
+ }
598
+
599
+ static inline void
600
+ start_decompress(struct jpeg_reader_data* reader)
601
+ {
602
+ assert(reader != NULL);
603
+ if (reader->state < READER_STARTED_DECOMPRESS) {
604
+ read_header(reader);
605
+ jpeg_start_decompress(&reader->cinfo);
606
+ reader->state = READER_STARTED_DECOMPRESS;
607
+ }
608
+ }
609
+
610
+ static inline uint32_t
611
+ cmyk_to_rgb24(JSAMPROW cmyk)
612
+ {
613
+ double const c = cmyk[0]/255;
614
+ double const m = cmyk[1]/255;
615
+ double const y = cmyk[2]/255;
616
+ double const k = cmyk[3]/255;
617
+ double const r = c*(1 - k) + k;
618
+ double const g = m*(1 - k) + k;
619
+ double const b = y*(1 - k) + k;
620
+ return ((uint32_t)(r*255) << 16) | ((uint32_t)(g*255) << 8) | ((uint32_t)(b*255));
621
+ }
622
+
623
+ static inline uint16_t
624
+ cmyk_to_rgb16_565(JSAMPROW cmyk)
625
+ {
626
+ double const c = cmyk[0]/255;
627
+ double const m = cmyk[1]/255;
628
+ double const y = cmyk[2]/255;
629
+ double const k = cmyk[3]/255;
630
+ double const r = c*(1 - k) + k;
631
+ double const g = m*(1 - k) + k;
632
+ double const b = y*(1 - k) + k;
633
+ return ((uint16_t)(r*31) << 11) | ((uint16_t)(g*63) << 6) | ((uint16_t)(b*31));
634
+ }
635
+
636
+ static void
637
+ convert_scanlines_from_CMYK(
638
+ VALUE image_buffer, JSAMPARRAY rows,
639
+ long const sl_beg, long const sl_end,
640
+ rb_image_file_image_pixel_format_t const pixel_format,
641
+ long const width, long const stride)
642
+ {
643
+ long i, j;
644
+ JSAMPROW src;
645
+
646
+ assert(TYPE(image_buffer) == T_STRING);
647
+ assert(rows != NULL);
648
+ assert(sl_beg < sl_end);
649
+ assert(pixel_format != RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_INVALID);
650
+
651
+ switch (pixel_format) {
652
+ case RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_ARGB32:
653
+ case RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_RGB24:
654
+ for (i = sl_beg; i < sl_end; ++i) {
655
+ uint32_t* dst = (uint32_t*)(RSTRING_PTR(image_buffer) + i*stride*4);
656
+ src = rows[i];
657
+ for (j = 0; j < width; ++j) {
658
+ uint32_t const pixel = cmyk_to_rgb24(src + 4*j);
659
+ *dst++ = pixel;
660
+ }
661
+ while (j++ < stride) *dst++ = 0;
662
+ }
663
+ break;
664
+
665
+ case RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_RGB16_565:
666
+ for (i = sl_beg; i < sl_end; ++i) {
667
+ uint16_t* dst = (uint16_t*)(RSTRING_PTR(image_buffer) + i*stride*2);
668
+ src = rows[i];
669
+ for (j = 0; j < width; ++j) {
670
+ uint16_t const pixel = cmyk_to_rgb16_565(src + 4*j);
671
+ *dst++ = pixel;
672
+ }
673
+ while (j++ < stride) *dst++ = 0;
674
+ }
675
+ break;
676
+
677
+ default:
678
+ rb_bug("invalid pixel format");
679
+ break;
680
+ }
681
+ }
682
+
683
+ static void
684
+ convert_scanlines_from_RGB(
685
+ VALUE image_buffer, JSAMPARRAY rows,
686
+ long const sl_beg, long const sl_end,
687
+ rb_image_file_image_pixel_format_t const pixel_format,
688
+ long const width, long const stride)
689
+ {
690
+ long i, j;
691
+ JSAMPROW src;
692
+
693
+ assert(TYPE(image_buffer) == T_STRING);
694
+ assert(rows != NULL);
695
+ assert(sl_beg < sl_end);
696
+ assert(pixel_format != RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_INVALID);
697
+
698
+ switch (pixel_format) {
699
+ case RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_ARGB32:
700
+ case RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_RGB24:
701
+ for (i = sl_beg; i < sl_end; ++i) {
702
+ uint32_t* dst = (uint32_t*)(RSTRING_PTR(image_buffer) + i*stride*4);
703
+ src = rows[i];
704
+ for (j = 0; j < width; ++j) {
705
+ uint32_t pixel = (uint32_t)(*src++) << 16;
706
+ pixel |= (uint32_t)(*src++) << 8;
707
+ pixel |= *src++;
708
+ *dst++ = pixel;
709
+ }
710
+ while (j++ < stride) *dst++ = 0;
711
+ }
712
+ break;
713
+
714
+ case RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_RGB16_565:
715
+ for (i = sl_beg; i < sl_end; ++i) {
716
+ uint16_t* dst = (uint16_t*)(RSTRING_PTR(image_buffer) + i*stride*2);
717
+ src = rows[i];
718
+ for (j = 0; j < width; ++j) {
719
+ uint16_t pixel = (uint16_t)(*src++ >> 3) << 11;
720
+ pixel |= (uint16_t)(*src++ >> 2) << 5;
721
+ pixel |= *src++ >> 3;
722
+ *dst++ = pixel;
723
+ }
724
+ while (j++ < stride) *dst++ = 0;
725
+ }
726
+ break;
727
+
728
+ default:
729
+ rb_bug("invalid pixel format");
730
+ break;
731
+ }
732
+ }
733
+
734
+ static void
735
+ process_arguments_of_read_image(int argc, VALUE* argv, struct jpeg_reader_data* reader,
736
+ VALUE* params_ptr,
737
+ rb_image_file_image_pixel_format_t* pixel_format_ptr,
738
+ long* width_ptr,
739
+ long* height_ptr,
740
+ long* stride_ptr
741
+ )
742
+ {
743
+ VALUE params, width, height;
744
+ VALUE pixel_format = Qnil;
745
+ VALUE stride = Qnil;
746
+
747
+ rb_image_file_image_pixel_format_t pf;
748
+ long st;
749
+
750
+ assert(reader != NULL);
751
+ assert(reader->state < READER_STARTED_DECOMPRESS);
752
+
753
+ rb_scan_args(argc, argv, "01", &params);
754
+ if (TYPE(params) == T_HASH) {
755
+ pixel_format = rb_hash_lookup(params, ID2SYM(id_pixel_format));
756
+ stride = rb_hash_lookup(params, ID2SYM(id_row_stride));
757
+ }
758
+ else {
759
+ rb_warning("invalid arguments are ignored.");
760
+ params = rb_hash_new();
761
+ }
762
+
763
+ if (!NIL_P(pixel_format) && TYPE(pixel_format) != T_SYMBOL) {
764
+ rb_warning("pixel_format is not a symbol.");
765
+ pixel_format = Qnil;
766
+ }
767
+ if (!NIL_P(pixel_format)) {
768
+ pf = rb_image_file_image_symbol_to_pixel_format(pixel_format);
769
+ if (RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_INVALID == pf) {
770
+ VALUE str = rb_id2str(SYM2ID(pixel_format));
771
+ rb_warning("invalid pixel_format (%s), use default instead.", StringValueCStr(str));
772
+ pixel_format = Qnil;
773
+ }
774
+ else {
775
+ J_COLOR_SPACE const jcs = image_pixel_format_to_j_color_space(pf);
776
+ if (JCS_UNKNOWN == jcs)
777
+ rb_warning("unknown pixel format, use default instead.");
778
+ else
779
+ reader->cinfo.out_color_space = jcs;
780
+ }
781
+ }
782
+
783
+ if (!NIL_P(stride) && TYPE(stride) != T_FIXNUM && TYPE(stride) != T_BIGNUM) {
784
+ rb_warning("stride is not an integer.");
785
+ stride = Qnil;
786
+ }
787
+
788
+ start_decompress(reader);
789
+
790
+ if (NIL_P(pixel_format)) {
791
+ pf = j_color_space_to_image_pixel_format(reader->cinfo.out_color_space);
792
+ if (RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_INVALID == pf) {
793
+ if (reader->cinfo.out_color_space != JCS_CMYK) {
794
+ char const* jcs_name = j_color_space_name(reader->cinfo.out_color_space);
795
+ rb_raise(eImageFileJpegReaderError,
796
+ "unsupported output color space (%s)", jcs_name);
797
+ }
798
+ pf = RB_IMAGE_FILE_IMAGE_PIXEL_FORMAT_RGB24;
799
+ }
800
+ rb_hash_aset(params, ID2SYM(id_pixel_format),
801
+ rb_image_file_image_pixel_format_to_symbol(pf));
802
+ }
803
+ else
804
+ rb_hash_aset(params, ID2SYM(id_pixel_format), pixel_format);
805
+
806
+ width = UINT2NUM(reader->cinfo.output_width);
807
+ rb_hash_aset(params, ID2SYM(id_width), width);
808
+
809
+ height = UINT2NUM(reader->cinfo.output_height);
810
+ rb_hash_aset(params, ID2SYM(id_height), height);
811
+
812
+ if (!NIL_P(stride)) {
813
+ st = NUM2LONG(stride);
814
+ if (st < (long)reader->cinfo.output_width) {
815
+ rb_warning("stride less than output_width.");
816
+ st = (long)reader->cinfo.output_width;
817
+ stride = LONG2NUM(st);
818
+ }
819
+ }
820
+ else
821
+ st = (long)reader->cinfo.output_width;
822
+ rb_hash_aset(params, ID2SYM(id_row_stride), stride);
823
+
824
+ *params_ptr = params;
825
+ *pixel_format_ptr = pf;
826
+ *width_ptr = (long)reader->cinfo.output_width;
827
+ *height_ptr = (long)reader->cinfo.output_height;
828
+ *stride_ptr = st;
829
+ }
830
+
831
+ static VALUE
832
+ jpeg_reader_read_image(int argc, VALUE* argv, VALUE obj)
833
+ {
834
+ struct jpeg_reader_data* reader;
835
+ VALUE params, image, image_buffer;
836
+ rb_image_file_image_pixel_format_t pf;
837
+ long wd, ht, st, i, nc;
838
+
839
+ VALUE row_buffer;
840
+ VALUE sample_buffer;
841
+ JSAMPARRAY rows;
842
+
843
+ reader = get_jpeg_reader_data(obj);
844
+
845
+ process_arguments_of_read_image(argc, argv, reader, &params, &pf, &wd, &ht, &st);
846
+ assert(reader->state >= READER_STARTED_DECOMPRESS);
847
+
848
+ image = rb_funcall(cImageFileImage, id_new, 1, params);
849
+ image_buffer = rb_image_file_image_get_buffer(image);
850
+
851
+ row_buffer = rb_str_tmp_new(sizeof(JSAMPROW)*ht);
852
+ rows = (JSAMPARRAY)(RSTRING_PTR(row_buffer));
853
+
854
+ nc = (long)reader->cinfo.output_components;
855
+ sample_buffer = rb_str_tmp_new(sizeof(JSAMPLE)*ht*wd*nc);
856
+ for (i = 0; i < ht; ++i)
857
+ rows[i] = (JSAMPROW)(RSTRING_PTR(sample_buffer) + i*wd*nc);
858
+
859
+ while ((long)reader->cinfo.output_scanline < ht) {
860
+ long sl_beg, sl_end;
861
+
862
+ sl_beg = reader->cinfo.output_scanline;
863
+ jpeg_read_scanlines(
864
+ &reader->cinfo,
865
+ rows + reader->cinfo.output_scanline,
866
+ (JDIMENSION)ht - reader->cinfo.output_scanline);
867
+ sl_end = reader->cinfo.output_scanline;
868
+
869
+ switch (reader->cinfo.out_color_space) {
870
+ case JCS_RGB:
871
+ convert_scanlines_from_RGB(image_buffer, rows, sl_beg, sl_end, pf, wd, st);
872
+ break;
873
+ case JCS_CMYK:
874
+ convert_scanlines_from_CMYK(image_buffer, rows, sl_beg, sl_end, pf, wd, st);
875
+ break;
876
+ default:
877
+ break;
878
+ }
879
+ }
880
+
881
+ jpeg_finish_decompress(&reader->cinfo);
882
+ reader->state = READER_FINISHED_DECOMPRESS;
883
+
884
+ return image;
885
+ }
886
+
887
+ void
888
+ rb_image_file_Init_image_file_jpeg_reader(void)
889
+ {
890
+ cImageFileJpegReader = rb_define_class_under(mImageFile, "JpegReader", rb_cObject);
891
+ rb_define_alloc_func(cImageFileJpegReader, jpeg_reader_alloc);
892
+ rb_define_singleton_method(cImageFileJpegReader, "open", jpeg_reader_s_open, 1);
893
+ rb_define_method(cImageFileJpegReader, "initialize", jpeg_reader_initialize, 1);
894
+ rb_define_method(cImageFileJpegReader, "source_will_be_closed?", jpeg_reader_source_will_be_closed, 0);
895
+
896
+ rb_define_method(cImageFileJpegReader, "image_width", jpeg_reader_get_image_width, 0);
897
+ rb_define_method(cImageFileJpegReader, "image_height", jpeg_reader_get_image_height, 0);
898
+ rb_define_method(cImageFileJpegReader, "num_components", jpeg_reader_get_num_components, 0);
899
+ rb_define_method(cImageFileJpegReader, "jpeg_color_space", jpeg_reader_get_jpeg_color_space, 0);
900
+
901
+ rb_define_method(cImageFileJpegReader, "out_color_space", jpeg_reader_get_out_color_space, 0);
902
+ rb_define_method(cImageFileJpegReader, "scale", jpeg_reader_get_scale, 0);
903
+ rb_define_method(cImageFileJpegReader, "scale=", jpeg_reader_set_scale, 1);
904
+ rb_define_method(cImageFileJpegReader, "output_gamma", jpeg_reader_get_output_gamma, 0);
905
+ rb_define_method(cImageFileJpegReader, "output_gamma=", jpeg_reader_set_output_gamma, 1);
906
+ rb_define_method(cImageFileJpegReader, "buffered_image?", jpeg_reader_is_buffered_image, 0);
907
+ rb_define_method(cImageFileJpegReader, "dct_method", jpeg_reader_get_dct_method, 0);
908
+ rb_define_method(cImageFileJpegReader, "quantize_colors?", jpeg_reader_is_quantize_colors, 0);
909
+ rb_define_method(cImageFileJpegReader, "dither_mode", jpeg_reader_get_dither_mode, 0);
910
+
911
+ rb_define_method(cImageFileJpegReader, "output_width", jpeg_reader_get_output_width, 0);
912
+ rb_define_method(cImageFileJpegReader, "output_height", jpeg_reader_get_output_height, 0);
913
+ rb_define_method(cImageFileJpegReader, "output_components", jpeg_reader_get_output_components, 0);
914
+
915
+ rb_define_method(cImageFileJpegReader, "read_image", jpeg_reader_read_image, -1);
916
+
917
+ eImageFileJpegReaderError = rb_define_class_under(
918
+ cImageFileJpegReader, "Error", rb_eStandardError);
919
+
920
+ CONST_ID(id_GRAYSCALE, "GRAYSCALE");
921
+ CONST_ID(id_RGB, "RGB");
922
+ CONST_ID(id_YCbCr, "YCbCr");
923
+ CONST_ID(id_CMYK, "CMYK");
924
+ CONST_ID(id_YCCK, "YCCK");
925
+ CONST_ID(id_ISLOW, "ISLOW");
926
+ CONST_ID(id_IFAST, "IFAST");
927
+ CONST_ID(id_FLOAT, "FLOAT");
928
+ CONST_ID(id_NONE, "NONE");
929
+ CONST_ID(id_ORDERED, "ORDERED");
930
+ CONST_ID(id_FS, "FS");
931
+ CONST_ID(id_new, "new");
932
+ CONST_ID(id_close, "close");
933
+ CONST_ID(id_read, "read");
934
+ CONST_ID(id_pixel_format, "pixel_format");
935
+ CONST_ID(id_width, "width");
936
+ CONST_ID(id_height,"height");
937
+ CONST_ID(id_row_stride, "row_stride");
938
+ }