image-file 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.
@@ -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
+ }