libjpeg-ruby 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/ext/jpeg/jpeg.c ADDED
@@ -0,0 +1,1506 @@
1
+ /*
2
+ * JPEG encode/decode library for Ruby
3
+ *
4
+ * Copyright (C) 2015 Hiroshi Kuwagata <kgt9221@gmail.com>
5
+ */
6
+
7
+ /*
8
+ * $Id: jpeg.c 159 2017-12-17 18:40:28Z pi $
9
+ */
10
+
11
+ /*
12
+ * TODO
13
+ * libjpegのエラーハンドリングを全くやっていないので
14
+ * いずれ修正する事
15
+ */
16
+
17
+ #include <stdio.h>
18
+ #include <stdint.h>
19
+ #include <strings.h>
20
+ #include <setjmp.h>
21
+
22
+ #include <jpeglib.h>
23
+
24
+ #include "ruby.h"
25
+
26
+ #define UNIT_LINES 10
27
+
28
+ #ifdef DEFAULT_QUALITY
29
+ #undef DEFAULT_QUALITY
30
+ #endif /* defined(DEFAULT_QUALITY) */
31
+
32
+ #define DEFAULT_QUALITY 75
33
+ #define DEFAULT_INPUT_COLOR_SPACE JCS_YCbCr
34
+ #define DEFAULT_INPUT_COMPONENTS 2
35
+
36
+ #define FMT_YUV422 1
37
+ #define FMT_RGB565 2
38
+ #define FMT_GRAYSCALE 3
39
+ #define FMT_YUV 4
40
+ #define FMT_RGB 5
41
+ #define FMT_BGR 6
42
+ #define FMT_RGB32 7
43
+ #define FMT_BGR32 8
44
+
45
+ #define FMT_YVU 20 /* original extend */
46
+
47
+ #define N(x) (sizeof(x)/sizeof(*x))
48
+
49
+ #define RUNTIME_ERROR(msg) rb_raise(rb_eRuntimeError, (msg))
50
+ #define ARGUMENT_ERROR(msg) rb_raise(rb_eArgError, (msg))
51
+
52
+ #define IS_COLORMAPPED(ci) (((ci)->actual_number_of_colors > 0) &&\
53
+ ((ci)->colormap != NULL) && \
54
+ ((ci)->output_components == 1) && \
55
+ (((ci)->out_color_components == 1) || \
56
+ ((ci)->out_color_components == 3)))
57
+
58
+ #define ALLOC_ARRAY() \
59
+ ((JSAMPARRAY)xmalloc(sizeof(JSAMPROW) * UNIT_LINES))
60
+ #define ALLOC_ROWS(w,c) \
61
+ ((JSAMPROW)xmalloc(sizeof(JSAMPLE) * (w) * (c) * UNIT_LINES))
62
+
63
+ #define EQ_STR(val,str) (rb_to_id(val) == rb_intern(str))
64
+ #define EQ_INT(val,n) (FIX2INT(val) == n)
65
+
66
+ static VALUE module;
67
+ static VALUE encoder_klass;
68
+ static VALUE encerr_klass;
69
+
70
+ static VALUE decoder_klass;
71
+ static VALUE meta_klass;
72
+ static VALUE decerr_klass;
73
+
74
+ static ID id_meta;
75
+ static ID id_width;
76
+ static ID id_height;
77
+ static ID id_orig_cs;
78
+ static ID id_out_cs;
79
+ static ID id_ncompo;
80
+
81
+ static const char* encoder_opts_keys[] = {
82
+ "pixel_format", // {str}
83
+ "quality", // {integer}
84
+ "scale", // {rational} or {float}
85
+ };
86
+
87
+ static ID encoder_opts_ids[N(encoder_opts_keys)];
88
+
89
+ typedef struct {
90
+ int format;
91
+ int width;
92
+ int height;
93
+
94
+ int data_size;
95
+
96
+ struct jpeg_compress_struct cinfo;
97
+ struct jpeg_error_mgr jerr;
98
+
99
+ JSAMPARRAY array;
100
+ JSAMPROW rows;
101
+ } jpeg_encode_t;
102
+
103
+ static const char* decoder_opts_keys[] = {
104
+ "pixel_format", // {str}
105
+ "output_gamma", // {float}
106
+ "do_fancy_upsampling", // {bool}
107
+ "do_smoothing", // {bool}
108
+ "dither", // [{str}MODE, {bool}2PASS, {int}NUM_COLORS]
109
+ "use_1pass_quantizer", // {bool}
110
+ "use_external_colormap", // {bool}
111
+ "use_2pass_quantizer", // {bool}
112
+ "without_meta", // {bool}
113
+ "expand_colormap", // {bool}
114
+ "scale", // {rational} or {float}
115
+ };
116
+
117
+ static ID decoder_opts_ids[N(decoder_opts_keys)];
118
+
119
+ typedef struct {
120
+ struct jpeg_error_mgr jerr;
121
+
122
+ char msg[JMSG_LENGTH_MAX+10];
123
+ jmp_buf jmpbuf;
124
+ } ext_error_t;
125
+
126
+ typedef struct {
127
+ int format;
128
+ int need_meta;
129
+ int expand_colormap;
130
+
131
+ J_COLOR_SPACE out_color_space;
132
+ int scale_num;
133
+ int scale_denom;
134
+ int out_color_components;
135
+ double output_gamma;
136
+ boolean buffered_image;
137
+ boolean do_fancy_upsampling;
138
+ boolean do_block_smoothing;
139
+ boolean quantize_colors;
140
+ J_DITHER_MODE dither_mode;
141
+ boolean two_pass_quantize;
142
+ int desired_number_of_colors;
143
+ boolean enable_1pass_quant;
144
+ boolean enable_external_quant;
145
+ boolean enable_2pass_quant;
146
+
147
+ struct jpeg_decompress_struct cinfo;
148
+ ext_error_t err_mgr;
149
+ } jpeg_decode_t;
150
+
151
+ static void
152
+ encode_output_message(j_common_ptr cinfo)
153
+ {
154
+ char msg[JMSG_LENGTH_MAX];
155
+
156
+ (*cinfo->err->format_message)(cinfo, msg);
157
+ }
158
+
159
+ static void
160
+ encode_error_exit(j_common_ptr cinfo)
161
+ {
162
+ char msg[JMSG_LENGTH_MAX];
163
+
164
+ (*cinfo->err->format_message)(cinfo, msg);
165
+
166
+ jpeg_destroy_compress((j_compress_ptr)cinfo);
167
+ rb_raise(encerr_klass, "%s", msg);
168
+ }
169
+
170
+
171
+ static void
172
+ rb_encoder_free( void* _ptr)
173
+ {
174
+ jpeg_encode_t* ptr;
175
+
176
+ ptr = (jpeg_encode_t*)_ptr;
177
+
178
+ if (ptr->array != NULL) xfree(ptr->array);
179
+ if (ptr->rows != NULL) xfree(ptr->rows);
180
+
181
+ jpeg_destroy_compress(&ptr->cinfo);
182
+
183
+ free(_ptr);
184
+ }
185
+
186
+ static VALUE
187
+ rb_encoder_alloc(VALUE self)
188
+ {
189
+ jpeg_encode_t* ptr;
190
+
191
+ ptr = ALLOC(jpeg_encode_t);
192
+ memset(ptr, 0, sizeof(*ptr));
193
+
194
+ return Data_Wrap_Struct(encoder_klass, 0, rb_encoder_free, ptr);
195
+ }
196
+
197
+ static void
198
+ set_encoder_context(jpeg_encode_t* ptr, int wd, int ht, VALUE opt)
199
+ {
200
+ VALUE opts[N(encoder_opts_ids)];
201
+ int format;
202
+ int color_space;
203
+ int components;
204
+ int data_size;
205
+ int quality;
206
+ int scale_num;
207
+ int scale_denom;
208
+ int i;
209
+
210
+ /*
211
+ * parse options
212
+ */
213
+ rb_get_kwargs(opt, encoder_opts_ids, 0, N(encoder_opts_ids), opts);
214
+
215
+ /*
216
+ * eval :pixel_format option
217
+ */
218
+ if (opts[0] == Qundef||EQ_STR(opts[0], "YUV422")||EQ_STR(opts[0], "YUYV")) {
219
+ format = FMT_YUV422;
220
+ color_space = JCS_YCbCr;
221
+ components = 3;
222
+ data_size = (wd * ht * 2);
223
+
224
+ } else if (EQ_STR(opts[0], "RGB565")) {
225
+ format = FMT_RGB565;
226
+ color_space = JCS_RGB;
227
+ components = 3;
228
+ data_size = (wd * ht * 2);
229
+
230
+ } else if (EQ_STR(opts[0], "RGB") || EQ_STR(opts[0], "RGB24")) {
231
+ format = FMT_RGB;
232
+ color_space = JCS_RGB;
233
+ components = 3;
234
+ data_size = (wd * ht * 3);
235
+
236
+ } else if (EQ_STR(opts[0], "BGR") || EQ_STR(opts[0], "BGR24")) {
237
+ format = FMT_BGR;
238
+ color_space = JCS_EXT_BGR;
239
+ components = 3;
240
+ data_size = (wd * ht * 3);
241
+
242
+ } else if (EQ_STR(opts[0], "YUV444") || EQ_STR(opts[0], "YCbCr")) {
243
+ format = FMT_YUV;
244
+ color_space = JCS_YCbCr;
245
+ components = 3;
246
+ data_size = (wd * ht * 3);
247
+
248
+ } else if (EQ_STR(opts[0], "RGBX") || EQ_STR(opts[0], "RGB32")) {
249
+ format = FMT_RGB32;
250
+ color_space = JCS_EXT_RGBX;
251
+ components = 4;
252
+ data_size = (wd * ht * 4);
253
+
254
+
255
+ } else if (EQ_STR(opts[0], "BGRX") || EQ_STR(opts[0], "BGR32")) {
256
+ format = FMT_BGR32;
257
+ color_space = JCS_EXT_BGRX;
258
+ components = 4;
259
+ data_size = (wd * ht * 4);
260
+
261
+
262
+ } else if (EQ_STR(opts[0], "GRAYSCALE")) {
263
+ format = FMT_GRAYSCALE;
264
+ color_space = JCS_GRAYSCALE;
265
+ components = 1;
266
+ data_size = (wd * ht);
267
+
268
+ } else {
269
+ ARGUMENT_ERROR("Unsupportd :pixel_format option value.");
270
+ }
271
+
272
+ /*
273
+ * eval :quality option
274
+ */
275
+ if (opts[1] == Qundef) {
276
+ quality = DEFAULT_QUALITY;
277
+
278
+ } else {
279
+ if (TYPE(opts[1]) != T_FIXNUM) {
280
+ ARGUMENT_ERROR("Unsupportd :quality option value.");
281
+
282
+ } else {
283
+ quality = FIX2INT(opts[1]);
284
+ }
285
+
286
+ if (quality < 0) {
287
+ ARGUMENT_ERROR(":quality value is to little.");
288
+
289
+ } else if (quality > 100) {
290
+ ARGUMENT_ERROR(":quality value is to big.");
291
+ }
292
+ }
293
+
294
+ /*
295
+ * eval scale option
296
+ */
297
+ switch (TYPE(opts[2])) {
298
+ case T_UNDEF:
299
+ // Nothing
300
+ break;
301
+
302
+ case T_FLOAT:
303
+ scale_num = 1000;
304
+ scale_denom = (int)(NUM2DBL(opts[2]) * 1000.0);
305
+ break;
306
+
307
+ case T_RATIONAL:
308
+ scale_num = FIX2INT(rb_rational_num(opts[2]));
309
+ scale_denom = FIX2INT(rb_rational_den(opts[2]));
310
+ break;
311
+
312
+ default:
313
+ ARGUMENT_ERROR("Unsupportd :scale option value.");
314
+ break;
315
+ }
316
+
317
+
318
+ /*
319
+ * set context
320
+ */
321
+ ptr->format = format;
322
+ ptr->width = wd;
323
+ ptr->height = ht;
324
+ ptr->data_size = data_size;
325
+ ptr->array = ALLOC_ARRAY();
326
+ ptr->rows = ALLOC_ROWS(wd, components);
327
+
328
+ for (i = 0; i < UNIT_LINES; i++) {
329
+ ptr->array[i] = ptr->rows + (i * components * wd);
330
+ }
331
+
332
+ jpeg_create_compress(&ptr->cinfo);
333
+
334
+ ptr->cinfo.err = jpeg_std_error(&ptr->jerr);
335
+ ptr->jerr.output_message = encode_output_message;
336
+ ptr->jerr.error_exit = encode_error_exit;
337
+
338
+ ptr->cinfo.image_width = wd;
339
+ ptr->cinfo.image_height = ht;
340
+ ptr->cinfo.in_color_space = color_space;
341
+ ptr->cinfo.input_components = components;
342
+
343
+ ptr->cinfo.optimize_coding = TRUE;
344
+ ptr->cinfo.arith_code = TRUE;
345
+ ptr->cinfo.raw_data_in = FALSE;
346
+ ptr->cinfo.dct_method = JDCT_FASTEST;
347
+
348
+ jpeg_set_defaults(&ptr->cinfo);
349
+ jpeg_set_quality(&ptr->cinfo, quality, TRUE);
350
+ jpeg_suppress_tables(&ptr->cinfo, TRUE);
351
+ }
352
+
353
+ static VALUE
354
+ rb_encoder_initialize(int argc, VALUE *argv, VALUE self)
355
+ {
356
+ jpeg_encode_t* ptr;
357
+ VALUE wd;
358
+ VALUE ht;
359
+ VALUE opt;
360
+
361
+ /*
362
+ * initialize
363
+ */
364
+ Data_Get_Struct(self, jpeg_encode_t, ptr);
365
+
366
+ /*
367
+ * parse arguments
368
+ */
369
+ rb_scan_args(argc, argv, "21", &wd, &ht, &opt);
370
+
371
+ Check_Type(wd, T_FIXNUM);
372
+ Check_Type(ht, T_FIXNUM);
373
+ if (opt != Qnil) Check_Type(opt, T_HASH);
374
+
375
+ /*
376
+ * set context
377
+ */
378
+ set_encoder_context(ptr, FIX2INT(wd), FIX2INT(ht), opt);
379
+
380
+ return Qtrue;
381
+ }
382
+
383
+ static int
384
+ push_rows_yuv422(JSAMPROW rows, int wd, uint8_t* data, int nrow)
385
+ {
386
+ int size;
387
+ int i;
388
+
389
+ size = wd * nrow;
390
+
391
+ for (i = 0; i < size; i += 2) {
392
+ rows[0] = data[0];
393
+ rows[1] = data[1];
394
+ rows[2] = data[3];
395
+ rows[3] = data[2];
396
+ rows[4] = data[1];
397
+ rows[5] = data[3];
398
+
399
+ rows += 6;
400
+ data += 4;
401
+ }
402
+
403
+ return (size * 2);
404
+ }
405
+
406
+ static int
407
+ push_rows_rgb565(JSAMPROW rows, int wd, uint8_t* data, int nrow)
408
+ {
409
+ int size;
410
+ int i;
411
+
412
+ size = wd * nrow;
413
+
414
+ for (i = 0; i < size; i += 1) {
415
+ rows[0] = data[1] & 0xf8;
416
+ rows[1] = ((data[1] << 5) & 0xe0) | ((data[0] >> 3) & 0x1c);
417
+ rows[2] = (data[0] << 3) & 0xf8;
418
+
419
+ rows += 3;
420
+ data += 2;
421
+ }
422
+
423
+ return (size * 2);
424
+ }
425
+
426
+ static int
427
+ push_rows_comp3(JSAMPROW rows, int wd, uint8_t* data, int nrow)
428
+ {
429
+ int size;
430
+
431
+ size = wd * nrow * 3;
432
+ memcpy(rows, data, size);
433
+
434
+ return size;
435
+ }
436
+
437
+ static int
438
+ push_rows_comp4(JSAMPROW rows, int wd, uint8_t* data, int nrow)
439
+ {
440
+ int size;
441
+
442
+ size = wd * nrow * 4;
443
+ memcpy(rows, data, size);
444
+
445
+ return size;
446
+ }
447
+
448
+
449
+
450
+ static int
451
+ push_rows_grayscale(JSAMPROW rows, int wd, uint8_t* data, int nrow)
452
+ {
453
+ int size;
454
+
455
+ size = wd * nrow;
456
+ memcpy(rows, data, size);
457
+
458
+ return size;
459
+ }
460
+
461
+ static int
462
+ push_rows(jpeg_encode_t* ptr, uint8_t* data, int nrow)
463
+ {
464
+ int ret;
465
+
466
+ switch (ptr->format) {
467
+ case FMT_YUV422:
468
+ ret = push_rows_yuv422(ptr->rows, ptr->width, data, nrow);
469
+ break;
470
+
471
+ case FMT_RGB565:
472
+ ret = push_rows_rgb565(ptr->rows, ptr->width, data, nrow);
473
+ break;
474
+
475
+ case FMT_YUV:
476
+ case FMT_RGB:
477
+ case FMT_BGR:
478
+ ret = push_rows_comp3(ptr->rows, ptr->width, data, nrow);
479
+ break;
480
+
481
+ case FMT_RGB32:
482
+ case FMT_BGR32:
483
+ ret = push_rows_comp4(ptr->rows, ptr->width, data, nrow);
484
+ break;
485
+
486
+ case FMT_GRAYSCALE:
487
+ ret = push_rows_grayscale(ptr->rows, ptr->width, data, nrow);
488
+ break;
489
+
490
+ default:
491
+ RUNTIME_ERROR("Really?");
492
+ }
493
+
494
+ return ret;
495
+ }
496
+
497
+ static VALUE
498
+ do_encode(jpeg_encode_t* ptr, uint8_t* data)
499
+ {
500
+ VALUE ret;
501
+
502
+ unsigned char* buf;
503
+ unsigned long buf_size;
504
+ int nrow;
505
+
506
+ buf = NULL;
507
+
508
+
509
+ jpeg_mem_dest(&ptr->cinfo, &buf, &buf_size);
510
+
511
+ jpeg_start_compress(&ptr->cinfo, TRUE);
512
+
513
+ while (ptr->cinfo.next_scanline < ptr->cinfo.image_height) {
514
+ nrow = ptr->cinfo.image_height - ptr->cinfo.next_scanline;
515
+ if (nrow > UNIT_LINES) nrow = UNIT_LINES;
516
+
517
+ data += push_rows(ptr, data, nrow);
518
+ jpeg_write_scanlines(&ptr->cinfo, ptr->array, nrow);
519
+ }
520
+
521
+ jpeg_finish_compress(&ptr->cinfo);
522
+
523
+ /*
524
+ * create return data
525
+ */
526
+ ret = rb_str_buf_new(buf_size);
527
+ rb_str_set_len(ret, buf_size);
528
+
529
+ memcpy(RSTRING_PTR(ret), buf, buf_size);
530
+
531
+ /*
532
+ * post process
533
+ */
534
+ free(buf);
535
+
536
+ return ret;
537
+ }
538
+
539
+ static VALUE
540
+ rb_encoder_encode(VALUE self, VALUE data)
541
+ {
542
+ VALUE ret;
543
+ jpeg_encode_t* ptr;
544
+
545
+ /*
546
+ * initialize
547
+ */
548
+ Data_Get_Struct(self, jpeg_encode_t, ptr);
549
+
550
+ /*
551
+ * argument check
552
+ */
553
+ Check_Type(data, T_STRING);
554
+
555
+ if (RSTRING_LEN(data) < ptr->data_size) {
556
+ ARGUMENT_ERROR("raw image data is too short.");
557
+ }
558
+
559
+ if (RSTRING_LEN(data) > ptr->data_size) {
560
+ ARGUMENT_ERROR("raw image data is too large.");
561
+ }
562
+
563
+ /*
564
+ * do encode
565
+ */
566
+ ret = do_encode(ptr, (uint8_t*)RSTRING_PTR(data));
567
+
568
+ return ret;
569
+ }
570
+
571
+ static void
572
+ rb_decoder_free(void* ptr)
573
+ {
574
+ //jpeg_destroy_decompress(&(((jpeg_decode_t*)ptr)->cinfo));
575
+
576
+ free(ptr);
577
+ }
578
+
579
+ static void
580
+ decode_output_message(j_common_ptr cinfo)
581
+ {
582
+ ext_error_t* err;
583
+
584
+ err = (ext_error_t*)cinfo->err;
585
+
586
+ (*err->jerr.format_message)(cinfo, err->msg);
587
+ }
588
+
589
+ static void
590
+ decode_emit_message(j_common_ptr cinfo, int msg_level)
591
+ {
592
+ ext_error_t* err;
593
+
594
+ if (msg_level < 0) {
595
+ err = (ext_error_t*)cinfo->err;
596
+ (*err->jerr.format_message)(cinfo, err->msg);
597
+ /*
598
+ * 以前はemit_messageが呼ばれるとエラー扱いしていたが、
599
+ * Logicoolの一部のモデルなどで問題が出るので無視する
600
+ * ようにした。
601
+ * また本来であれば、警告表示を行うべきでもあるが一部
602
+ * のモデルで大量にemitされる場合があるので表示しない
603
+ * ようにしている。
604
+ * 問題が発生した際は、最後のメッセージをオブジェクト
605
+ * のインスタンスとして持たすべき。
606
+ */
607
+ // longjmp(err->jmpbuf, 1);
608
+ }
609
+ }
610
+
611
+ static void
612
+ decode_error_exit(j_common_ptr cinfo)
613
+ {
614
+ ext_error_t* err;
615
+
616
+ err = (ext_error_t*)cinfo->err;
617
+ (*err->jerr.format_message)(cinfo, err->msg);
618
+ longjmp(err->jmpbuf, 1);
619
+ }
620
+
621
+ static VALUE
622
+ rb_decoder_alloc(VALUE self)
623
+ {
624
+ jpeg_decode_t* ptr;
625
+
626
+ ptr = ALLOC(jpeg_decode_t);
627
+ memset(ptr, 0, sizeof(*ptr));
628
+
629
+ ptr->format = FMT_RGB;
630
+ ptr->need_meta = !0;
631
+ ptr->expand_colormap = 0;
632
+
633
+ ptr->out_color_space = JCS_RGB;
634
+
635
+ ptr->scale_num = 1;
636
+ ptr->scale_denom = 1;
637
+
638
+ ptr->out_color_components = 3;
639
+ ptr->output_gamma = 0.0;
640
+ ptr->buffered_image = FALSE;
641
+ ptr->do_fancy_upsampling = FALSE;
642
+ ptr->do_block_smoothing = FALSE;
643
+ ptr->quantize_colors = FALSE;
644
+ ptr->dither_mode = JDITHER_NONE;
645
+ ptr->desired_number_of_colors = 0;
646
+ ptr->enable_1pass_quant = FALSE;
647
+ ptr->enable_external_quant = FALSE;
648
+ ptr->enable_2pass_quant = FALSE;
649
+
650
+ return Data_Wrap_Struct(decoder_klass, 0, rb_decoder_free, ptr);
651
+ }
652
+
653
+ static void
654
+ eval_decoder_opt_pixel_format(jpeg_decode_t* ptr, VALUE opt)
655
+ {
656
+ int format;
657
+ int color_space;
658
+ int components;
659
+
660
+ if (opt != Qundef) {
661
+ if(EQ_STR(opt, "RGB") || EQ_STR(opt, "RGB24")) {
662
+ format = FMT_RGB;
663
+ color_space = JCS_RGB;
664
+ components = 3;
665
+
666
+ } else if (EQ_STR(opt, "YUV422") || EQ_STR(opt, "YUYV")) {
667
+ format = FMT_YUV422;
668
+ color_space = JCS_YCbCr;
669
+ components = 3;
670
+
671
+ } else if (EQ_STR(opt, "RGB565")) {
672
+ format = FMT_RGB565;
673
+ color_space = JCS_RGB;
674
+ components = 3;
675
+
676
+ } else if (EQ_STR(opt, "GRAYSCALE")) {
677
+ format = FMT_GRAYSCALE;
678
+ color_space = JCS_GRAYSCALE;
679
+ components = 1;
680
+
681
+ } else if (EQ_STR(opt, "YUV444") || EQ_STR(opt, "YCbCr")) {
682
+ format = FMT_YUV;
683
+ color_space = JCS_YCbCr;
684
+ components = 3;
685
+
686
+ } else if (EQ_STR(opt, "BGR") || EQ_STR(opt, "BGR24")) {
687
+ format = FMT_BGR;
688
+ color_space = JCS_EXT_BGR;
689
+ components = 3;
690
+
691
+ } else if (EQ_STR(opt, "YVU444") || EQ_STR(opt, "YCrCb")) {
692
+ format = FMT_YVU;
693
+ color_space = JCS_YCbCr;
694
+ components = 3;
695
+
696
+ } else if (EQ_STR(opt, "RGBX") || EQ_STR(opt, "RGB32")) {
697
+ format = FMT_RGB32;
698
+ color_space = JCS_EXT_RGBX;
699
+ components = 4;
700
+
701
+ } else if (EQ_STR(opt, "BGRX") || EQ_STR(opt, "BGR32")) {
702
+ format = FMT_BGR32;
703
+ color_space = JCS_EXT_BGRX;
704
+ components = 4;
705
+
706
+ } else {
707
+ ARGUMENT_ERROR("Unsupportd :pixel_format option value.");
708
+ }
709
+
710
+ ptr->format = format;
711
+ ptr->out_color_space = color_space;
712
+ ptr->out_color_components = components;
713
+ }
714
+ }
715
+
716
+ static void
717
+ eval_decoder_opt_output_gamma(jpeg_decode_t* ptr, VALUE opt)
718
+ {
719
+ switch (TYPE(opt)) {
720
+ case T_UNDEF:
721
+ // Nothing
722
+ break;
723
+
724
+ case T_FLOAT:
725
+ ptr->output_gamma = NUM2DBL(opt);
726
+ break;
727
+
728
+ default:
729
+ ARGUMENT_ERROR("Unsupportd :output_gamma option value.");
730
+ break;
731
+ }
732
+ }
733
+
734
+ static void
735
+ eval_decoder_opt_do_fancy_upsampling(jpeg_decode_t* ptr, VALUE opt)
736
+ {
737
+ switch (TYPE(opt)) {
738
+ case T_UNDEF:
739
+ // Nothing
740
+ break;
741
+
742
+ case T_TRUE:
743
+ ptr->do_fancy_upsampling = TRUE;
744
+ break;
745
+
746
+ case T_FALSE:
747
+ ptr->do_fancy_upsampling = FALSE;
748
+ break;
749
+
750
+ default:
751
+ ARGUMENT_ERROR("Unsupportd :do_fancy_up_sampling option value.");
752
+ break;
753
+ }
754
+ }
755
+
756
+ static void
757
+ eval_decoder_opt_do_smoothing(jpeg_decode_t* ptr, VALUE opt)
758
+ {
759
+ switch (TYPE(opt)) {
760
+ case T_UNDEF:
761
+ // Nothing
762
+ break;
763
+
764
+ case T_TRUE:
765
+ ptr->do_block_smoothing = TRUE;
766
+ break;
767
+
768
+ case T_FALSE:
769
+ ptr->do_block_smoothing = FALSE;
770
+ break;
771
+
772
+ default:
773
+ ARGUMENT_ERROR("Unsupportd :do_smoothing option value.");
774
+ break;
775
+ }
776
+ }
777
+
778
+ static void
779
+ eval_decoder_opt_dither( jpeg_decode_t* ptr, VALUE opt)
780
+ {
781
+ VALUE dmode;
782
+ VALUE pass2;
783
+ VALUE n_col;
784
+
785
+ if (opt != Qundef) {
786
+ if (TYPE(opt) != T_ARRAY) {
787
+ ARGUMENT_ERROR("Unsupportd :dither option value.");
788
+ }
789
+
790
+ if (RARRAY_LEN(opt) != 3) {
791
+ ARGUMENT_ERROR(":dither is illeagal length (shall be 3 entries).");
792
+ }
793
+
794
+ dmode = RARRAY_AREF(opt, 0);
795
+ pass2 = RARRAY_AREF(opt, 1);
796
+ n_col = RARRAY_AREF(opt, 2);
797
+
798
+ if (EQ_STR(dmode, "NONE")) {
799
+ ptr->dither_mode = JDITHER_NONE;
800
+ ptr->quantize_colors = FALSE;
801
+
802
+ } else if(EQ_STR(dmode, "ORDERED")) {
803
+ ptr->dither_mode = JDITHER_ORDERED;
804
+ ptr->quantize_colors = TRUE;
805
+
806
+ } else if(EQ_STR(dmode, "FS")) {
807
+ ptr->dither_mode = JDITHER_FS;
808
+ ptr->quantize_colors = TRUE;
809
+
810
+ } else {
811
+ ARGUMENT_ERROR("dither mode is illeagal value.");
812
+ }
813
+
814
+ switch (TYPE(pass2)) {
815
+ case T_TRUE:
816
+ ptr->two_pass_quantize = TRUE;
817
+ break;
818
+
819
+ case T_FALSE:
820
+ ptr->two_pass_quantize = FALSE;
821
+ break;
822
+
823
+ default:
824
+ ARGUMENT_ERROR( "2pass quantize flag is illeagal value.");
825
+ }
826
+
827
+ if (TYPE(n_col) == T_FIXNUM) {
828
+ ptr->desired_number_of_colors = FIX2INT(n_col);
829
+ } else {
830
+ ARGUMENT_ERROR( "number of dithered colors is illeagal value.");
831
+ }
832
+ }
833
+ }
834
+
835
+ static void
836
+ eval_decoder_opt_use_1pass_quantizer(jpeg_decode_t* ptr, VALUE opt)
837
+ {
838
+ switch (TYPE(opt)) {
839
+ case T_UNDEF:
840
+ // Nothing
841
+ break;
842
+
843
+ case T_TRUE:
844
+ ptr->enable_1pass_quant = TRUE;
845
+ ptr->buffered_image = TRUE;
846
+ break;
847
+
848
+ case T_FALSE:
849
+ ptr->enable_1pass_quant = FALSE;
850
+ break;
851
+
852
+ default:
853
+ ARGUMENT_ERROR("Unsupportd :use_1pass_quantizer option value.");
854
+ break;
855
+ }
856
+ }
857
+
858
+ static void
859
+ eval_decoder_opt_use_external_colormap(jpeg_decode_t* ptr, VALUE opt)
860
+ {
861
+ switch (TYPE(opt)) {
862
+ case T_UNDEF:
863
+ // Nothing
864
+ break;
865
+
866
+ case T_TRUE:
867
+ ptr->enable_external_quant = TRUE;
868
+ ptr->buffered_image = TRUE;
869
+ break;
870
+
871
+ case T_FALSE:
872
+ ptr->enable_external_quant = FALSE;
873
+ break;
874
+
875
+ default:
876
+ ARGUMENT_ERROR("Unsupportd :use_external_colormap option value.");
877
+ break;
878
+ }
879
+ }
880
+
881
+ static void
882
+ eval_decoder_opt_use_2pass_quantizer(jpeg_decode_t* ptr, VALUE opt)
883
+ {
884
+ switch (TYPE(opt)) {
885
+ case T_UNDEF:
886
+ // Nothing
887
+ break;
888
+
889
+ case T_TRUE:
890
+ ptr->enable_2pass_quant = TRUE;
891
+ ptr->buffered_image = TRUE;
892
+ break;
893
+
894
+ case T_FALSE:
895
+ ptr->enable_2pass_quant = FALSE;
896
+ break;
897
+
898
+ default:
899
+ ARGUMENT_ERROR("Unsupportd :use_2pass_quantizer option value.");
900
+ break;
901
+ }
902
+ }
903
+
904
+ static void
905
+ eval_decoder_opt_without_meta(jpeg_decode_t* ptr, VALUE opt)
906
+ {
907
+ switch (TYPE(opt)) {
908
+ case T_UNDEF:
909
+ // Nothing
910
+ break;
911
+
912
+ case T_TRUE:
913
+ ptr->need_meta = 0;
914
+ break;
915
+
916
+ case T_FALSE:
917
+ ptr->need_meta = !0;
918
+ break;
919
+
920
+ default:
921
+ ARGUMENT_ERROR("Unsupportd :without_meta option value.");
922
+ break;
923
+ }
924
+ }
925
+
926
+ static void
927
+ eval_decoder_opt_expand_colormap(jpeg_decode_t* ptr, VALUE opt)
928
+ {
929
+ switch (TYPE(opt)) {
930
+ case T_UNDEF:
931
+ // Nothing
932
+ break;
933
+
934
+ case T_TRUE:
935
+ ptr->expand_colormap = !0;
936
+ break;
937
+
938
+ case T_FALSE:
939
+ ptr->expand_colormap = 0;
940
+ break;
941
+
942
+ default:
943
+ ARGUMENT_ERROR("Unsupportd :exapnd_colormap option value.");
944
+ break;
945
+ }
946
+ }
947
+
948
+ static void
949
+ eval_decoder_opt_scale(jpeg_decode_t* ptr, VALUE opt)
950
+ {
951
+ switch (TYPE(opt)) {
952
+ case T_UNDEF:
953
+ // Nothing
954
+ break;
955
+
956
+ case T_FLOAT:
957
+ ptr->scale_num = 1000;
958
+ ptr->scale_denom = (int)(NUM2DBL(opt) * 1000.0);
959
+ break;
960
+
961
+ case T_RATIONAL:
962
+ ptr->scale_num = FIX2INT(rb_rational_num(opt));
963
+ ptr->scale_denom = FIX2INT(rb_rational_den(opt));
964
+ break;
965
+
966
+ default:
967
+ ARGUMENT_ERROR("Unsupportd :exapnd_colormap option value.");
968
+ break;
969
+ }
970
+ }
971
+
972
+ static void
973
+ set_decoder_context( jpeg_decode_t* ptr, VALUE opt)
974
+ {
975
+ VALUE opts[N(decoder_opts_ids)];
976
+
977
+ /*
978
+ * parse options
979
+ */
980
+ rb_get_kwargs( opt, decoder_opts_ids, 0, N(decoder_opts_ids), opts);
981
+
982
+ /*
983
+ * set context
984
+ */
985
+ eval_decoder_opt_pixel_format(ptr, opts[0]);
986
+ eval_decoder_opt_output_gamma(ptr, opts[1]);
987
+ eval_decoder_opt_do_fancy_upsampling(ptr, opts[2]);
988
+ eval_decoder_opt_do_smoothing(ptr, opts[3]);
989
+ eval_decoder_opt_dither(ptr, opts[4]);
990
+ eval_decoder_opt_use_1pass_quantizer(ptr, opts[5]);
991
+ eval_decoder_opt_use_external_colormap(ptr, opts[6]);
992
+ eval_decoder_opt_use_2pass_quantizer(ptr, opts[7]);
993
+
994
+ eval_decoder_opt_without_meta(ptr, opts[8]);
995
+ eval_decoder_opt_expand_colormap(ptr, opts[9]);
996
+
997
+ eval_decoder_opt_scale(ptr, opts[10]);
998
+ }
999
+
1000
+ static VALUE
1001
+ rb_decoder_initialize( int argc, VALUE *argv, VALUE self)
1002
+ {
1003
+ jpeg_decode_t* ptr;
1004
+ VALUE opt;
1005
+
1006
+ /*
1007
+ * initialize
1008
+ */
1009
+ Data_Get_Struct(self, jpeg_decode_t, ptr);
1010
+
1011
+ /*
1012
+ * parse arguments
1013
+ */
1014
+ rb_scan_args( argc, argv, "01", &opt);
1015
+
1016
+ if (opt != Qnil) Check_Type(opt, T_HASH);
1017
+
1018
+ /*
1019
+ * set context
1020
+ */
1021
+ set_decoder_context(ptr, opt);
1022
+
1023
+ return Qtrue;
1024
+ }
1025
+
1026
+ static VALUE
1027
+ rb_decoder_set(VALUE self, VALUE opt)
1028
+ {
1029
+ VALUE ret;
1030
+ jpeg_decode_t* ptr;
1031
+
1032
+ /*
1033
+ * initialize
1034
+ */
1035
+ Data_Get_Struct(self, jpeg_decode_t, ptr);
1036
+
1037
+ /*
1038
+ * check argument
1039
+ */
1040
+ Check_Type( opt, T_HASH);
1041
+
1042
+ /*
1043
+ * set context
1044
+ */
1045
+ set_decoder_context(ptr, opt);
1046
+
1047
+ return Qtrue;
1048
+ }
1049
+
1050
+
1051
+
1052
+ static VALUE
1053
+ get_colorspace_str( J_COLOR_SPACE cs)
1054
+ {
1055
+ const char* cstr;
1056
+
1057
+ switch (cs) {
1058
+ case JCS_GRAYSCALE:
1059
+ cstr = "GRAYSCALE";
1060
+ break;
1061
+
1062
+ case JCS_RGB:
1063
+ cstr = "RGB";
1064
+ break;
1065
+
1066
+ case JCS_YCbCr:
1067
+ cstr = "YCbCr";
1068
+ break;
1069
+
1070
+ case JCS_CMYK:
1071
+ cstr = "CMYK";
1072
+ break;
1073
+
1074
+ case JCS_YCCK:
1075
+ cstr = "YCCK";
1076
+ break;
1077
+ #if JPEG_LIB_VERSION < 90
1078
+ case JCS_EXT_RGB:
1079
+ cstr = "RGB";
1080
+ break;
1081
+
1082
+ case JCS_EXT_RGBX:
1083
+ cstr = "RGBX";
1084
+ break;
1085
+
1086
+ case JCS_EXT_BGR:
1087
+ cstr = "BGR";
1088
+ break;
1089
+
1090
+ case JCS_EXT_BGRX:
1091
+ cstr = "BGRX";
1092
+ break;
1093
+
1094
+ case JCS_EXT_XBGR:
1095
+ cstr = "XBGR";
1096
+ break;
1097
+
1098
+ case JCS_EXT_XRGB:
1099
+ cstr = "XRGB";
1100
+ break;
1101
+
1102
+ case JCS_EXT_RGBA:
1103
+ cstr = "RGBA";
1104
+ break;
1105
+
1106
+ case JCS_EXT_BGRA:
1107
+ cstr = "BGRA";
1108
+ break;
1109
+
1110
+ case JCS_EXT_ABGR:
1111
+ cstr = "ABGR";
1112
+ break;
1113
+
1114
+ case JCS_EXT_ARGB:
1115
+ cstr = "ARGB";
1116
+ break;
1117
+ #endif /* JPEG_LIB_VERSION < 90 */
1118
+
1119
+ default:
1120
+ cstr = "UNKNOWN";
1121
+ break;
1122
+ }
1123
+
1124
+ return rb_str_new_cstr(cstr);
1125
+ }
1126
+
1127
+ static VALUE
1128
+ create_meta(jpeg_decode_t* ptr)
1129
+ {
1130
+ VALUE ret;
1131
+ struct jpeg_decompress_struct* cinfo;
1132
+
1133
+ /* TODO: そのうちディザをかけた場合のカラーマップをメタで返すようにする */
1134
+
1135
+ ret = rb_obj_alloc( meta_klass);
1136
+ cinfo = &ptr->cinfo;
1137
+
1138
+ rb_ivar_set(ret, id_width, INT2FIX(cinfo->output_width));
1139
+ rb_ivar_set(ret, id_height, INT2FIX(cinfo->output_height));
1140
+ rb_ivar_set(ret, id_orig_cs, get_colorspace_str(cinfo->jpeg_color_space));
1141
+
1142
+ if (ptr->format == FMT_YVU) {
1143
+ rb_ivar_set(ret, id_out_cs, rb_str_new_cstr("YCrCb"));
1144
+ } else {
1145
+ rb_ivar_set(ret, id_out_cs, get_colorspace_str(cinfo->out_color_space));
1146
+ }
1147
+
1148
+ rb_ivar_set(ret, id_ncompo, INT2FIX(cinfo->output_components));
1149
+
1150
+ return ret;
1151
+ }
1152
+
1153
+ static VALUE
1154
+ do_read_header(jpeg_decode_t* ptr, uint8_t* jpg, size_t jpg_sz)
1155
+ {
1156
+ VALUE ret;
1157
+
1158
+ switch (ptr->format) {
1159
+ case FMT_YUV422:
1160
+ case FMT_RGB565:
1161
+ RUNTIME_ERROR( "Not implement");
1162
+ break;
1163
+ }
1164
+
1165
+ jpeg_create_decompress(&ptr->cinfo);
1166
+
1167
+ ptr->cinfo.err = jpeg_std_error(&ptr->err_mgr.jerr);
1168
+ ptr->err_mgr.jerr.output_message = decode_output_message;
1169
+ ptr->err_mgr.jerr.emit_message = decode_emit_message;
1170
+ ptr->err_mgr.jerr.error_exit = decode_error_exit;
1171
+
1172
+ ptr->cinfo.raw_data_out = FALSE;
1173
+ ptr->cinfo.dct_method = JDCT_FLOAT;
1174
+
1175
+ if (setjmp(ptr->err_mgr.jmpbuf)) {
1176
+ jpeg_destroy_decompress(&ptr->cinfo);
1177
+ rb_raise(decerr_klass, "%s", ptr->err_mgr.msg);
1178
+ } else {
1179
+ jpeg_mem_src(&ptr->cinfo, jpg, jpg_sz);
1180
+ jpeg_read_header(&ptr->cinfo, TRUE);
1181
+ jpeg_calc_output_dimensions(&ptr->cinfo);
1182
+
1183
+ ret = create_meta(ptr);
1184
+
1185
+ jpeg_destroy_decompress(&ptr->cinfo);
1186
+ }
1187
+
1188
+ return ret;
1189
+ }
1190
+
1191
+ static VALUE
1192
+ rb_decoder_read_header(VALUE self, VALUE data)
1193
+ {
1194
+ VALUE ret;
1195
+ jpeg_decode_t* ptr;
1196
+
1197
+ /*
1198
+ * initialize
1199
+ */
1200
+ Data_Get_Struct(self, jpeg_decode_t, ptr);
1201
+
1202
+ /*
1203
+ * argument check
1204
+ */
1205
+ Check_Type(data, T_STRING);
1206
+
1207
+ /*
1208
+ * do encode
1209
+ */
1210
+ ret = do_read_header(ptr, (uint8_t*)RSTRING_PTR(data), RSTRING_LEN(data));
1211
+
1212
+ return ret;
1213
+ }
1214
+
1215
+ static VALUE
1216
+ rb_decode_result_meta(VALUE self)
1217
+ {
1218
+ return rb_ivar_get(self, id_meta);
1219
+ }
1220
+
1221
+ static void
1222
+ add_meta(VALUE obj, jpeg_decode_t* ptr)
1223
+ {
1224
+ VALUE meta;
1225
+
1226
+ meta = create_meta(ptr);
1227
+
1228
+ rb_ivar_set(obj, id_meta, meta);
1229
+ rb_define_singleton_method(obj, "meta", rb_decode_result_meta, 0);
1230
+ }
1231
+
1232
+ static VALUE
1233
+ expand_colormap(struct jpeg_decompress_struct* cinfo, uint8_t* src)
1234
+ {
1235
+ /*
1236
+ * 本関数はcinfo->out_color_componentsが1または3であることを前提に
1237
+ * 作成されています。
1238
+ */
1239
+
1240
+ VALUE ret;
1241
+ int i;
1242
+ int n;
1243
+ uint8_t* dst;
1244
+ JSAMPARRAY map;
1245
+
1246
+ n = cinfo->output_width * cinfo->output_height;
1247
+ ret = rb_str_buf_new(n * cinfo->out_color_components);
1248
+ dst = (uint8_t*)RSTRING_PTR(ret);
1249
+ map = cinfo->colormap;
1250
+
1251
+ switch (cinfo->out_color_components) {
1252
+ case 1:
1253
+ for (i = 0; i < n; i++) {
1254
+ dst[i] = map[0][src[i]];
1255
+ }
1256
+ break;
1257
+
1258
+ case 3:
1259
+ for (i = 0; i < n; i++) {
1260
+ dst[0] = map[0][src[i]];
1261
+ dst[1] = map[1][src[i]];
1262
+ dst[2] = map[2][src[i]];
1263
+
1264
+ dst += 3;
1265
+ }
1266
+ break;
1267
+
1268
+ default:
1269
+ RUNTIME_ERROR("Really?");
1270
+ }
1271
+
1272
+ rb_str_set_len(ret, n * cinfo->out_color_components);
1273
+
1274
+ return ret;
1275
+ }
1276
+
1277
+ static void
1278
+ swap_cbcr(uint8_t* p, size_t size)
1279
+ {
1280
+ int i;
1281
+ uint8_t tmp;
1282
+
1283
+ for (i = 0; i < size; i++) {
1284
+ tmp = p[1];
1285
+ p[1] = p[2];
1286
+ p[2] = tmp;
1287
+ }
1288
+ }
1289
+
1290
+ static VALUE
1291
+ do_decode(jpeg_decode_t* ptr, uint8_t* jpg, size_t jpg_sz)
1292
+ {
1293
+ VALUE ret;
1294
+ struct jpeg_decompress_struct* cinfo;
1295
+ JSAMPARRAY array;
1296
+
1297
+ size_t stride;
1298
+ size_t raw_sz;
1299
+ uint8_t* raw;
1300
+ int i;
1301
+ int j;
1302
+
1303
+ ret = Qundef; // warning対策
1304
+ cinfo = &ptr->cinfo;
1305
+ array = ALLOC_ARRAY();
1306
+
1307
+ switch (ptr->format) {
1308
+ case FMT_YUV422:
1309
+ case FMT_RGB565:
1310
+ RUNTIME_ERROR( "Not implement");
1311
+ break;
1312
+
1313
+ case FMT_GRAYSCALE:
1314
+ case FMT_YUV:
1315
+ case FMT_RGB:
1316
+ case FMT_BGR:
1317
+ case FMT_YVU:
1318
+ case FMT_RGB32:
1319
+ case FMT_BGR32:
1320
+ jpeg_create_decompress(cinfo);
1321
+
1322
+ cinfo->err = jpeg_std_error(&ptr->err_mgr.jerr);
1323
+ ptr->err_mgr.jerr.output_message = decode_output_message;
1324
+ ptr->err_mgr.jerr.emit_message = decode_emit_message;
1325
+ ptr->err_mgr.jerr.error_exit = decode_error_exit;
1326
+
1327
+ if (setjmp(ptr->err_mgr.jmpbuf)) {
1328
+ jpeg_abort_decompress(cinfo);
1329
+ jpeg_destroy_decompress(&ptr->cinfo);
1330
+
1331
+ free(array);
1332
+ rb_raise(decerr_klass, "%s", ptr->err_mgr.msg);
1333
+
1334
+ } else {
1335
+ jpeg_mem_src(cinfo, jpg, jpg_sz);
1336
+ jpeg_read_header(cinfo, TRUE);
1337
+
1338
+ cinfo->raw_data_out = FALSE;
1339
+ cinfo->dct_method = JDCT_FLOAT;
1340
+
1341
+ cinfo->out_color_space = ptr->out_color_space;
1342
+ cinfo->out_color_components = ptr->out_color_components;
1343
+ cinfo->scale_num = ptr->scale_num;
1344
+ cinfo->scale_denom = ptr->scale_denom;
1345
+ cinfo->output_gamma = ptr->output_gamma;
1346
+ cinfo->do_fancy_upsampling = ptr->do_fancy_upsampling;
1347
+ cinfo->do_block_smoothing = ptr->do_block_smoothing;
1348
+ cinfo->quantize_colors = ptr->quantize_colors;
1349
+ cinfo->dither_mode = ptr->dither_mode;
1350
+ cinfo->two_pass_quantize = ptr->two_pass_quantize;
1351
+ cinfo->desired_number_of_colors = ptr->desired_number_of_colors;
1352
+ cinfo->enable_1pass_quant = ptr->enable_1pass_quant;
1353
+ cinfo->enable_external_quant = ptr->enable_external_quant;
1354
+ cinfo->enable_2pass_quant = ptr->enable_2pass_quant;
1355
+
1356
+ jpeg_calc_output_dimensions(cinfo);
1357
+ jpeg_start_decompress(cinfo);
1358
+
1359
+ stride = cinfo->output_components * cinfo->output_width;
1360
+ raw_sz = stride * cinfo->output_height;
1361
+ ret = rb_str_buf_new(raw_sz);
1362
+ raw = (uint8_t*)RSTRING_PTR(ret);
1363
+
1364
+
1365
+ while (cinfo->output_scanline < cinfo->output_height) {
1366
+ for (i = 0, j = cinfo->output_scanline; i < UNIT_LINES; i++, j++) {
1367
+ array[i] = raw + (j * stride);
1368
+ }
1369
+
1370
+ jpeg_read_scanlines(cinfo, array, UNIT_LINES);
1371
+ }
1372
+
1373
+ if (ptr->expand_colormap && IS_COLORMAPPED( cinfo)) {
1374
+ ret = expand_colormap(cinfo, raw);
1375
+ } else {
1376
+ rb_str_set_len( ret, raw_sz);
1377
+ }
1378
+
1379
+ if (ptr->need_meta) add_meta(ret, ptr);
1380
+ if (ptr->format == FMT_YVU) swap_cbcr(raw, raw_sz);
1381
+
1382
+ jpeg_finish_decompress(cinfo);
1383
+ jpeg_destroy_decompress(&ptr->cinfo);
1384
+ }
1385
+ break;
1386
+ }
1387
+
1388
+ free(array);
1389
+
1390
+ return ret;
1391
+ }
1392
+
1393
+ static VALUE
1394
+ rb_decoder_decode(VALUE self, VALUE data)
1395
+ {
1396
+ VALUE ret;
1397
+ jpeg_decode_t* ptr;
1398
+
1399
+ /*
1400
+ * initialize
1401
+ */
1402
+ Data_Get_Struct(self, jpeg_decode_t, ptr);
1403
+
1404
+ /*
1405
+ * argument check
1406
+ */
1407
+ Check_Type(data, T_STRING);
1408
+
1409
+ /*
1410
+ * do encode
1411
+ */
1412
+ ret = do_decode(ptr, (uint8_t*)RSTRING_PTR(data), RSTRING_LEN(data));
1413
+
1414
+ return ret;
1415
+ }
1416
+
1417
+ static VALUE
1418
+ rb_test_image(VALUE self, VALUE data)
1419
+ {
1420
+ VALUE ret;
1421
+ struct jpeg_decompress_struct cinfo;
1422
+ ext_error_t err_mgr;
1423
+
1424
+ jpeg_create_decompress(&cinfo);
1425
+
1426
+ cinfo.err = jpeg_std_error(&err_mgr.jerr);
1427
+
1428
+ err_mgr.jerr.output_message = decode_output_message;
1429
+ err_mgr.jerr.emit_message = decode_emit_message;
1430
+ err_mgr.jerr.error_exit = decode_error_exit;
1431
+
1432
+ cinfo.raw_data_out = FALSE;
1433
+ cinfo.dct_method = JDCT_FLOAT;
1434
+
1435
+ if (setjmp(err_mgr.jmpbuf)) {
1436
+ ret = Qtrue;
1437
+ } else {
1438
+ ret = Qfalse;
1439
+
1440
+ jpeg_mem_src(&cinfo, RSTRING_PTR(data), RSTRING_LEN(data));
1441
+ jpeg_read_header(&cinfo, TRUE);
1442
+ jpeg_calc_output_dimensions(&cinfo);
1443
+ }
1444
+
1445
+ jpeg_destroy_decompress(&cinfo);
1446
+
1447
+ return ret;
1448
+ }
1449
+
1450
+ void
1451
+ Init_jpeg()
1452
+ {
1453
+ int i;
1454
+
1455
+ module = rb_define_module("JPEG");
1456
+ rb_define_singleton_method(module, "broken?", rb_test_image, 1);
1457
+
1458
+ encoder_klass = rb_define_class_under(module, "Encoder", rb_cObject);
1459
+ rb_define_alloc_func(encoder_klass, rb_encoder_alloc);
1460
+ rb_define_method(encoder_klass, "initialize", rb_encoder_initialize, -1);
1461
+ rb_define_method(encoder_klass, "encode", rb_encoder_encode, 1);
1462
+ rb_define_alias(encoder_klass, "compress", "encode");
1463
+ rb_define_alias(encoder_klass, "<<", "encode");
1464
+
1465
+ encerr_klass = rb_define_class_under(module,
1466
+ "EncodeError", rb_eRuntimeError);
1467
+
1468
+ decoder_klass = rb_define_class_under(module, "Decoder", rb_cObject);
1469
+ rb_define_alloc_func(decoder_klass, rb_decoder_alloc);
1470
+ rb_define_method(decoder_klass, "initialize", rb_decoder_initialize, -1);
1471
+ rb_define_method(decoder_klass, "set", rb_decoder_set, 1);
1472
+ rb_define_method(decoder_klass, "read_header", rb_decoder_read_header, 1);
1473
+ rb_define_method(decoder_klass, "decode", rb_decoder_decode, 1);
1474
+ rb_define_alias(decoder_klass, "decompress", "decode");
1475
+ rb_define_alias(decoder_klass, "<<", "decode");
1476
+
1477
+ meta_klass = rb_define_class_under(module, "Meta", rb_cObject);
1478
+ rb_define_attr(meta_klass, "width", 1, 0);
1479
+ rb_define_attr(meta_klass, "height", 1, 0);
1480
+ rb_define_attr(meta_klass, "original_colorspace", 1, 0);
1481
+ rb_define_attr(meta_klass, "output_colorspace", 1, 0);
1482
+ rb_define_attr(meta_klass, "num_components", 1, 0);
1483
+
1484
+ decerr_klass = rb_define_class_under(module,
1485
+ "DecodeError", rb_eRuntimeError);
1486
+
1487
+ /*
1488
+ * 必要になる都度ID計算をさせるとコストがかかるので、本ライブラリで使用
1489
+ * するID値の計算を先に済ませておく
1490
+ * 但し、可読性を優先して随時計算する箇所を残しているので注意すること。
1491
+ */
1492
+ for (i = 0; i < (int)N(encoder_opts_keys); i++) {
1493
+ encoder_opts_ids[i] = rb_intern_const(encoder_opts_keys[i]);
1494
+ }
1495
+
1496
+ for (i = 0; i < (int)N(decoder_opts_keys); i++) {
1497
+ decoder_opts_ids[i] = rb_intern_const(decoder_opts_keys[i]);
1498
+ }
1499
+
1500
+ id_meta = rb_intern_const("@meta");
1501
+ id_width = rb_intern_const("@width");
1502
+ id_height = rb_intern_const("@height");
1503
+ id_orig_cs = rb_intern_const("@original_colorspace");
1504
+ id_out_cs = rb_intern_const("@output_colorspace");
1505
+ id_ncompo = rb_intern_const("@num_components");
1506
+ }