libjpeg-ruby 0.7.0 → 0.9.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7117f5dece9f78e847213078d1cba56591a30c01ea4b9de58cb4886647d5c530
4
- data.tar.gz: 7bb5c400d519e4958fb9522c8a3af313931dc033a45c404a288d0d5020f02b7c
3
+ metadata.gz: 3523da7ee57f41d57b81fe97558564e0b0459c5977321ba88a298a450cd9273a
4
+ data.tar.gz: 103f379375f273f0f9a77109cc3dff8018779ce94837d2182e1e0ce50bb78213
5
5
  SHA512:
6
- metadata.gz: 457f1b3f55ba6c164a40e66303a9c34df6e9ec590137d161c98cee69bbac1385aa4b309c60feb853637c0c392afabdb356996b6809adefd0ed5827051eb318f6
7
- data.tar.gz: 9427246e376ff7a9464b0e4c8cf22b0dff47e9036649f13a49a213b769f7ceab7d3e76dbbd258d65c6c65e5c03bda362cf06acc1b921c83bde74bd49d0c8c85f
6
+ metadata.gz: fd38c818582812ce2ef404ade310846a5af944e2b05c2b16df4df66bf62dd3404c9c68c6508e65e8a603041ab6f0d21484d911680e967f7e11aa9ad94a7d2782
7
+ data.tar.gz: a05940b338d1660f90345c7eebdeea897629ac37c33949720cde9d4c4d5794eb73dcb0f1be568d078110185ffe223425e4f5cb61da10f3075dd63a6c1a1fe185
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .rbenv-gemsets
3
+ .ruby-version
4
+ /.config
5
+ /pkg/
6
+ /doc/
7
+ /tmp/
8
+ /.yardoc/
data/README.md CHANGED
@@ -50,7 +50,8 @@ IO.binwrite("test.bgr", raw)
50
50
  | :expand_colormap | Booblean | T.B.D |
51
51
  | :scale | Rational or Float | T.B.D |
52
52
  | :dct_method | String or Symbol | T.B.D |
53
- | :with_exif | Boolean | Specify whether to read Exif tag. When set to true, the content of Exif tag will included in the meta information. |
53
+ | :with_exif_tags | Boolean | Specify whether to read Exif tag. When set to true, the content of Exif tag will included in the meta information. |
54
+ | :orientation | Boolean | Specify whether to parse Exif orientation. When set to true, apply orientation for decode result. |
54
55
 
55
56
  #### supported output format
56
57
  RGB RGB24 YUV422 YUYV RGB565 YUV444 YCbCr BGR BGR24 RGBX RGB32 BGRX BGR32
@@ -75,4 +76,5 @@ IO.binwrite("test.jpg", enc << IO.binread("test.raw"))
75
76
  | :quality | Integer | encode quality (0-100) |
76
77
  | :scale | Rational or Float | |
77
78
  | :dct_method | String or Symbol | T.B.D |
79
+ | :orientation | Integer | Specify Exif orientation value (1-8). |
78
80
 
data/ext/jpeg/extconf.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'mkmf'
2
2
  require 'optparse'
3
+ require 'rbconfig'
3
4
 
4
5
  OptionParser.new { |opt|
5
6
  opt.on('--with-jpeg-include=PATH', String) { |path|
@@ -16,4 +17,14 @@ OptionParser.new { |opt|
16
17
  have_library( "jpeg")
17
18
  have_header( "jpeglib.h")
18
19
 
20
+ RbConfig::CONFIG.instance_eval {
21
+ flag = false
22
+
23
+ if /gcc/ =~ self['CC']
24
+ flag = ['CFLAGS', 'CPPFLAGS'].any? {|s| /-D_FORTIFY_SOURCE/ =~ self[s]}
25
+ end
26
+
27
+ have_library( "ssp") if flag
28
+ }
29
+
19
30
  create_makefile( "jpeg/jpeg")
data/ext/jpeg/jpeg.c CHANGED
@@ -4,16 +4,6 @@
4
4
  * Copyright (C) 2015 Hiroshi Kuwagata <kgt9221@gmail.com>
5
5
  */
6
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
7
  #include <stdio.h>
18
8
  #include <stdint.h>
19
9
  #include <strings.h>
@@ -22,6 +12,7 @@
22
12
  #include <jpeglib.h>
23
13
 
24
14
  #include "ruby.h"
15
+ #include "ruby/version.h"
25
16
  #include "ruby/encoding.h"
26
17
 
27
18
  #define UNIT_LINES 10
@@ -33,6 +24,23 @@
33
24
  #define DEFAULT_QUALITY 75
34
25
  #define DEFAULT_INPUT_COLOR_SPACE JCS_YCbCr
35
26
  #define DEFAULT_INPUT_COMPONENTS 2
27
+ #define DEFAULT_ENCODE_FLAGS (0)
28
+ #define DEFAULT_DECODE_FLAGS (F_NEED_META)
29
+
30
+ #define F_NEED_META 0x00000001
31
+ #define F_EXPAND_COLORMAP 0x00000002
32
+ #define F_PARSE_EXIF 0x00000004
33
+ #define F_APPLY_ORIENTATION 0x00000008
34
+ #define F_DITHER 0x00000010
35
+ #define F_CREAT 0x00010000
36
+
37
+ #define SET_FLAG(ptr, msk) ((ptr)->flags |= (msk))
38
+ #define CLR_FLAG(ptr, msk) ((ptr)->flags &= ~(msk))
39
+ #define TEST_FLAG(ptr, msk) ((ptr)->flags & (msk))
40
+ #define TEST_FLAG_ALL(ptr, msk) (((ptr)->flags & (msk)) == (msk))
41
+
42
+ #define SET_DATA(ptr, dat) ((ptr)->data = (dat))
43
+ #define CLR_DATA(ptr) ((ptr)->data = Qnil)
36
44
 
37
45
  #define FMT_YUV422 1
38
46
  #define FMT_RGB565 2
@@ -48,20 +56,25 @@
48
56
  #define JPEG_APP1 0xe1 /* Exif marker */
49
57
 
50
58
  #define N(x) (sizeof(x)/sizeof(*x))
59
+ #define SWAP(a,b,t) \
60
+ do {t c; c = (a); (a) = (b); (b) = c;} while (0)
51
61
 
52
62
  #define RUNTIME_ERROR(msg) rb_raise(rb_eRuntimeError, (msg))
53
63
  #define ARGUMENT_ERROR(msg) rb_raise(rb_eArgError, (msg))
64
+ #define TYPE_ERROR(msg) rb_raise(rb_eTypeError, (msg))
65
+ #define RANGE_ERROR(msg) rb_raise(rb_eRangeError, (msg))
66
+ #define NOT_IMPLEMENTED_ERROR(msg) rb_raise(rb_eNotImpError, (msg))
54
67
 
55
68
  #define IS_COLORMAPPED(ci) (((ci)->actual_number_of_colors > 0) &&\
56
69
  ((ci)->colormap != NULL) && \
57
70
  ((ci)->output_components == 1) && \
58
- (((ci)->out_color_components == 1) || \
59
- ((ci)->out_color_components == 3)))
71
+ (((ci)->out_color_components == 1) || \
72
+ ((ci)->out_color_components == 3)))
60
73
 
61
74
  #define ALLOC_ARRAY() \
62
- ((JSAMPARRAY)xmalloc(sizeof(JSAMPROW) * UNIT_LINES))
75
+ ((JSAMPARRAY)malloc(sizeof(JSAMPROW) * UNIT_LINES))
63
76
  #define ALLOC_ROWS(w,c) \
64
- ((JSAMPROW)xmalloc(sizeof(JSAMPLE) * (w) * (c) * UNIT_LINES))
77
+ ((JSAMPROW)malloc(sizeof(JSAMPLE) * (w) * (c) * UNIT_LINES))
65
78
 
66
79
  #define EQ_STR(val,str) (rb_to_id(val) == rb_intern(str))
67
80
  #define EQ_INT(val,n) (FIX2INT(val) == n)
@@ -76,11 +89,13 @@ static VALUE decerr_klass;
76
89
 
77
90
  static ID id_meta;
78
91
  static ID id_width;
92
+ static ID id_stride;
79
93
  static ID id_height;
80
94
  static ID id_orig_cs;
81
95
  static ID id_out_cs;
82
96
  static ID id_ncompo;
83
- static ID id_exif;
97
+ static ID id_exif_tags;
98
+ static ID id_colormap;
84
99
 
85
100
  typedef struct {
86
101
  int tag;
@@ -246,25 +261,47 @@ tag_entry_t tag_i14y[] = {
246
261
  static const char* encoder_opts_keys[] = {
247
262
  "pixel_format", // {str}
248
263
  "quality", // {integer}
249
- "scale", // {rational} or {float}
250
- "dct_method" // {str}
264
+ "dct_method", // {str}
265
+ "orientation", // {integer}
266
+ "stride", // {integer}
251
267
  };
252
268
 
253
269
  static ID encoder_opts_ids[N(encoder_opts_keys)];
254
270
 
255
271
  typedef struct {
256
- int format;
272
+ struct jpeg_error_mgr jerr;
273
+
274
+ char msg[JMSG_LENGTH_MAX+10];
275
+ jmp_buf jmpbuf;
276
+ } ext_error_t;
277
+
278
+ typedef struct {
279
+ int flags;
257
280
  int width;
281
+ int stride;
258
282
  int height;
259
-
260
283
  int data_size;
284
+
285
+ int format;
286
+ int color_space;
287
+ int components;
288
+ int quality;
261
289
  J_DCT_METHOD dct_method;
262
290
 
263
291
  struct jpeg_compress_struct cinfo;
264
- struct jpeg_error_mgr jerr;
292
+ ext_error_t err_mgr;
265
293
 
266
294
  JSAMPARRAY array;
267
295
  JSAMPROW rows;
296
+
297
+ VALUE data;
298
+
299
+ struct {
300
+ unsigned char* mem;
301
+ unsigned long size;
302
+ } buf;
303
+
304
+ int orientation;
268
305
  } jpeg_encode_t;
269
306
 
270
307
  static const char* decoder_opts_keys[] = {
@@ -273,30 +310,24 @@ static const char* decoder_opts_keys[] = {
273
310
  "do_fancy_upsampling", // {bool}
274
311
  "do_smoothing", // {bool}
275
312
  "dither", // [{str}MODE, {bool}2PASS, {int}NUM_COLORS]
313
+ #if 0
276
314
  "use_1pass_quantizer", // {bool}
277
315
  "use_external_colormap", // {bool}
278
316
  "use_2pass_quantizer", // {bool}
317
+ #endif
279
318
  "without_meta", // {bool}
280
319
  "expand_colormap", // {bool}
281
320
  "scale", // {rational} or {float}
282
321
  "dct_method", // {str}
283
- "with_exif" // {bool}
322
+ "with_exif_tags", // {bool}
323
+ "orientation" // {bool}
284
324
  };
285
325
 
286
326
  static ID decoder_opts_ids[N(decoder_opts_keys)];
287
327
 
288
328
  typedef struct {
289
- struct jpeg_error_mgr jerr;
290
-
291
- char msg[JMSG_LENGTH_MAX+10];
292
- jmp_buf jmpbuf;
293
- } ext_error_t;
294
-
295
- typedef struct {
329
+ int flags;
296
330
  int format;
297
- int need_meta;
298
- int expand_colormap;
299
- int parse_exif;
300
331
 
301
332
  J_COLOR_SPACE out_color_space;
302
333
  int scale_num;
@@ -317,8 +348,131 @@ typedef struct {
317
348
 
318
349
  struct jpeg_decompress_struct cinfo;
319
350
  ext_error_t err_mgr;
351
+
352
+ JSAMPARRAY array;
353
+
354
+ VALUE data;
355
+
356
+ struct {
357
+ int value;
358
+ VALUE buf;
359
+ } orientation;
320
360
  } jpeg_decode_t;
321
361
 
362
+ #if 0
363
+ static VALUE
364
+ create_runtime_error(const char* fmt, ...)
365
+ {
366
+ VALUE ret;
367
+ va_list ap;
368
+
369
+ va_start(ap, fmt);
370
+ ret = rb_exc_new_str(rb_eRuntimeError, rb_vsprintf(fmt, ap));
371
+ va_end(ap);
372
+
373
+ return ret;
374
+ }
375
+ #endif
376
+
377
+ static VALUE
378
+ create_argument_error(const char* fmt, ...)
379
+ {
380
+ VALUE ret;
381
+ va_list ap;
382
+
383
+ va_start(ap, fmt);
384
+ ret = rb_exc_new_str(rb_eArgError, rb_vsprintf(fmt, ap));
385
+ va_end(ap);
386
+
387
+ return ret;
388
+ }
389
+
390
+ static VALUE
391
+ create_type_error(const char* fmt, ...)
392
+ {
393
+ VALUE ret;
394
+ va_list ap;
395
+
396
+ va_start(ap, fmt);
397
+ ret = rb_exc_new_str(rb_eTypeError, rb_vsprintf(fmt, ap));
398
+ va_end(ap);
399
+
400
+ return ret;
401
+ }
402
+
403
+ static VALUE
404
+ create_range_error(const char* fmt, ...)
405
+ {
406
+ VALUE ret;
407
+ va_list ap;
408
+
409
+ va_start(ap, fmt);
410
+ ret = rb_exc_new_str(rb_eRangeError, rb_vsprintf(fmt, ap));
411
+ va_end(ap);
412
+
413
+ return ret;
414
+ }
415
+
416
+ static VALUE
417
+ create_not_implement_error(const char* fmt, ...)
418
+ {
419
+ VALUE ret;
420
+ va_list ap;
421
+
422
+ va_start(ap, fmt);
423
+ ret = rb_exc_new_str(rb_eNotImpError, rb_vsprintf(fmt, ap));
424
+ va_end(ap);
425
+
426
+ return ret;
427
+ }
428
+
429
+ static VALUE
430
+ create_memory_error()
431
+ {
432
+ return rb_exc_new_str(rb_eRangeError, rb_str_new_cstr("no memory"));
433
+ }
434
+
435
+ static void
436
+ output_message(j_common_ptr cinfo)
437
+ {
438
+ ext_error_t* err;
439
+
440
+ err = (ext_error_t*)cinfo->err;
441
+
442
+ (*err->jerr.format_message)(cinfo, err->msg);
443
+ }
444
+
445
+ static void
446
+ emit_message(j_common_ptr cinfo, int msg_level)
447
+ {
448
+ ext_error_t* err;
449
+
450
+ if (msg_level < 0) {
451
+ err = (ext_error_t*)cinfo->err;
452
+ (*err->jerr.format_message)(cinfo, err->msg);
453
+ /*
454
+ * 以前はemit_messageが呼ばれるとエラー扱いしていたが、
455
+ * Logicoolの一部のモデルなどで問題が出るので無視する
456
+ * ようにした。
457
+ * また本来であれば、警告表示を行うべきでもあるが一部
458
+ * のモデルで大量にemitされる場合があるので表示しない
459
+ * ようにしている。
460
+ * 問題が発生した際は、最後のメッセージをオブジェクト
461
+ * のインスタンスとして持たすべき。
462
+ */
463
+ // longjmp(err->jmpbuf, 1);
464
+ }
465
+ }
466
+
467
+ static void
468
+ error_exit(j_common_ptr cinfo)
469
+ {
470
+ ext_error_t* err;
471
+
472
+ err = (ext_error_t*)cinfo->err;
473
+ (*err->jerr.format_message)(cinfo, err->msg);
474
+ longjmp(err->jmpbuf, 1);
475
+ }
322
476
 
323
477
  static VALUE
324
478
  lookup_tag_symbol(tag_entry_t* tbl, size_t n, int tag)
@@ -361,40 +515,87 @@ lookup_tag_symbol(tag_entry_t* tbl, size_t n, int tag)
361
515
  }
362
516
 
363
517
  static void
364
- encode_output_message(j_common_ptr cinfo)
518
+ rb_encoder_mark(void* _ptr)
365
519
  {
366
- char msg[JMSG_LENGTH_MAX];
520
+ jpeg_encode_t* ptr;
521
+
522
+ ptr = (jpeg_encode_t*)_ptr;
367
523
 
368
- (*cinfo->err->format_message)(cinfo, msg);
524
+ if (ptr->data != Qnil) {
525
+ rb_gc_mark(ptr->data);
526
+ }
369
527
  }
370
528
 
371
529
  static void
372
- encode_error_exit(j_common_ptr cinfo)
530
+ rb_encoder_free(void* _ptr)
373
531
  {
374
- char msg[JMSG_LENGTH_MAX];
532
+ jpeg_encode_t* ptr;
375
533
 
376
- (*cinfo->err->format_message)(cinfo, msg);
534
+ ptr = (jpeg_encode_t*)_ptr;
377
535
 
378
- jpeg_destroy_compress((j_compress_ptr)cinfo);
379
- rb_raise(encerr_klass, "%s", msg);
380
- }
536
+ if (ptr->array != NULL) {
537
+ free(ptr->array);
538
+ }
381
539
 
540
+ if (ptr->rows != NULL) {
541
+ free(ptr->rows);
542
+ }
382
543
 
383
- static void
384
- rb_encoder_free( void* _ptr)
544
+ if (ptr->buf.mem != NULL) {
545
+ free(ptr->buf.mem);
546
+ }
547
+
548
+ if (TEST_FLAG(ptr, F_CREAT)) {
549
+ jpeg_destroy_compress(&ptr->cinfo);
550
+ }
551
+
552
+ free(ptr);
553
+ }
554
+
555
+ static size_t
556
+ rb_encoder_size(const void* _ptr)
385
557
  {
558
+ size_t ret;
386
559
  jpeg_encode_t* ptr;
387
560
 
388
- ptr = (jpeg_encode_t*)_ptr;
389
-
390
- if (ptr->array != NULL) xfree(ptr->array);
391
- if (ptr->rows != NULL) xfree(ptr->rows);
561
+ ptr = (jpeg_encode_t*)_ptr;
392
562
 
393
- jpeg_destroy_compress(&ptr->cinfo);
563
+ ret = sizeof(jpeg_encode_t);
564
+ ret += sizeof(JSAMPROW) * UNIT_LINES;
565
+ ret += sizeof(JSAMPLE) * ptr->stride * UNIT_LINES;
394
566
 
395
- free(_ptr);
567
+ return ret;
396
568
  }
397
569
 
570
+ #if RUBY_API_VERSION_CODE > 20600
571
+ static const rb_data_type_t jpeg_encoder_data_type = {
572
+ "libjpeg-ruby encoder object", // wrap_struct_name
573
+ {
574
+ rb_encoder_mark, // function.dmark
575
+ rb_encoder_free, // function.dfree
576
+ rb_encoder_size, // function.dsize
577
+ NULL, // function.dcompact
578
+ {NULL}, // function.reserved
579
+ },
580
+ NULL, // parent
581
+ NULL, // data
582
+ (VALUE)RUBY_TYPED_FREE_IMMEDIATELY // flags
583
+ };
584
+ #else /* RUBY_API_VERSION_CODE > 20600 */
585
+ static const rb_data_type_t jpeg_encoder_data_type = {
586
+ "libjpeg-ruby encoder object", // wrap_struct_name
587
+ {
588
+ rb_encoder_mark, // function.dmark
589
+ rb_encoder_free, // function.dfree
590
+ rb_encoder_size, // function.dsize
591
+ {NULL, NULL}, // function.reserved
592
+ },
593
+ NULL, // parent
594
+ NULL, // data
595
+ (VALUE)RUBY_TYPED_FREE_IMMEDIATELY // flags
596
+ };
597
+ #endif /* RUBY_API_VERSION_CODE > 20600 */
598
+
398
599
  static VALUE
399
600
  rb_encoder_alloc(VALUE self)
400
601
  {
@@ -403,192 +604,396 @@ rb_encoder_alloc(VALUE self)
403
604
  ptr = ALLOC(jpeg_encode_t);
404
605
  memset(ptr, 0, sizeof(*ptr));
405
606
 
406
- ptr->dct_method = JDCT_FASTEST;
607
+ ptr->flags = DEFAULT_ENCODE_FLAGS;
407
608
 
408
- return Data_Wrap_Struct(encoder_klass, 0, rb_encoder_free, ptr);
609
+ return TypedData_Wrap_Struct(encoder_klass, &jpeg_encoder_data_type, ptr);
409
610
  }
410
611
 
411
- static void
412
- set_encoder_context(jpeg_encode_t* ptr, int wd, int ht, VALUE opt)
612
+ static VALUE
613
+ eval_encoder_pixel_format_opt(jpeg_encode_t* ptr, VALUE opt)
413
614
  {
414
- VALUE opts[N(encoder_opts_ids)];
615
+ VALUE ret;
415
616
  int format;
416
617
  int color_space;
417
618
  int components;
418
- int data_size;
419
- int quality;
420
- int scale_num;
421
- int scale_denom;
422
- int i;
423
619
 
424
- /*
425
- * parse options
426
- */
427
- rb_get_kwargs(opt, encoder_opts_ids, 0, N(encoder_opts_ids), opts);
620
+ ret = Qnil;
428
621
 
429
- /*
430
- * eval :pixel_format option
431
- */
432
- if (opts[0] == Qundef||EQ_STR(opts[0], "YUV422")||EQ_STR(opts[0], "YUYV")) {
622
+ switch (TYPE(opt)) {
623
+ case T_UNDEF:
433
624
  format = FMT_YUV422;
434
625
  color_space = JCS_YCbCr;
435
626
  components = 3;
436
- data_size = (wd * ht * 2);
627
+ break;
437
628
 
438
- } else if (EQ_STR(opts[0], "RGB565")) {
439
- format = FMT_RGB565;
440
- color_space = JCS_RGB;
441
- components = 3;
442
- data_size = (wd * ht * 2);
629
+ case T_STRING:
630
+ case T_SYMBOL:
631
+ if (EQ_STR(opt, "YUV422") || EQ_STR(opt, "YUYV")) {
632
+ format = FMT_YUV422;
633
+ color_space = JCS_YCbCr;
634
+ components = 3;
443
635
 
444
- } else if (EQ_STR(opts[0], "RGB") || EQ_STR(opts[0], "RGB24")) {
445
- format = FMT_RGB;
446
- color_space = JCS_RGB;
447
- components = 3;
448
- data_size = (wd * ht * 3);
636
+ } else if (EQ_STR(opt, "RGB565")) {
637
+ format = FMT_RGB565;
638
+ color_space = JCS_RGB;
639
+ components = 3;
449
640
 
450
- } else if (EQ_STR(opts[0], "BGR") || EQ_STR(opts[0], "BGR24")) {
451
- format = FMT_BGR;
452
- color_space = JCS_EXT_BGR;
453
- components = 3;
454
- data_size = (wd * ht * 3);
641
+ } else if (EQ_STR(opt, "RGB") || EQ_STR(opt, "RGB24")) {
642
+ format = FMT_RGB;
643
+ color_space = JCS_RGB;
644
+ components = 3;
455
645
 
456
- } else if (EQ_STR(opts[0], "YUV444") || EQ_STR(opts[0], "YCbCr")) {
457
- format = FMT_YUV;
458
- color_space = JCS_YCbCr;
459
- components = 3;
460
- data_size = (wd * ht * 3);
646
+ } else if (EQ_STR(opt, "BGR") || EQ_STR(opt, "BGR24")) {
647
+ format = FMT_BGR;
648
+ color_space = JCS_EXT_BGR;
649
+ components = 3;
461
650
 
462
- } else if (EQ_STR(opts[0], "RGBX") || EQ_STR(opts[0], "RGB32")) {
463
- format = FMT_RGB32;
464
- color_space = JCS_EXT_RGBX;
465
- components = 4;
466
- data_size = (wd * ht * 4);
651
+ } else if (EQ_STR(opt, "YUV444") || EQ_STR(opt, "YCbCr")) {
652
+ format = FMT_YUV;
653
+ color_space = JCS_YCbCr;
654
+ components = 3;
467
655
 
656
+ } else if (EQ_STR(opt, "RGBX") || EQ_STR(opt, "RGB32")) {
657
+ format = FMT_RGB32;
658
+ color_space = JCS_EXT_RGBX;
659
+ components = 4;
468
660
 
469
- } else if (EQ_STR(opts[0], "BGRX") || EQ_STR(opts[0], "BGR32")) {
470
- format = FMT_BGR32;
471
- color_space = JCS_EXT_BGRX;
472
- components = 4;
473
- data_size = (wd * ht * 4);
661
+ } else if (EQ_STR(opt, "BGRX") || EQ_STR(opt, "BGR32")) {
662
+ format = FMT_BGR32;
663
+ color_space = JCS_EXT_BGRX;
664
+ components = 4;
474
665
 
666
+ } else if (EQ_STR(opt, "GRAYSCALE")) {
667
+ format = FMT_GRAYSCALE;
668
+ color_space = JCS_GRAYSCALE;
669
+ components = 1;
475
670
 
476
- } else if (EQ_STR(opts[0], "GRAYSCALE")) {
477
- format = FMT_GRAYSCALE;
478
- color_space = JCS_GRAYSCALE;
479
- components = 1;
480
- data_size = (wd * ht);
671
+ } else {
672
+ ret = create_argument_error("unsupportd :pixel_format option value");
673
+ }
674
+ break;
481
675
 
482
- } else {
483
- ARGUMENT_ERROR("Unsupportd :pixel_format option value.");
676
+ default:
677
+ ret = create_type_error("unsupportd :pixel_format option type");
678
+ break;
484
679
  }
485
680
 
486
- /*
487
- * eval :quality option
488
- */
489
- if (opts[1] == Qundef) {
490
- quality = DEFAULT_QUALITY;
491
-
492
- } else {
493
- if (TYPE(opts[1]) != T_FIXNUM) {
494
- ARGUMENT_ERROR("Unsupportd :quality option value.");
681
+ if (!RTEST(ret)) {
682
+ ptr->format = format;
683
+ ptr->color_space = color_space;
684
+ ptr->components = components;
685
+ }
495
686
 
496
- } else {
497
- quality = FIX2INT(opts[1]);
498
- }
687
+ return ret;
688
+ }
499
689
 
500
- if (quality < 0) {
501
- ARGUMENT_ERROR(":quality value is to little.");
690
+ static VALUE
691
+ eval_encoder_quality_opt(jpeg_encode_t* ptr, VALUE opt)
692
+ {
693
+ VALUE ret;
694
+ long quality;
502
695
 
503
- } else if (quality > 100) {
504
- ARGUMENT_ERROR(":quality value is to big.");
505
- }
506
- }
507
-
508
- /*
509
- * eval scale option
510
- */
511
- (void)scale_num;
512
- (void)scale_denom;
696
+ ret = Qnil;
513
697
 
514
- switch (TYPE(opts[2])) {
698
+ switch (TYPE(opt)) {
515
699
  case T_UNDEF:
516
- // Nothing
700
+ quality = DEFAULT_QUALITY;
517
701
  break;
518
702
 
519
703
  case T_FLOAT:
520
- scale_num = 1000;
521
- scale_denom = (int)(NUM2DBL(opts[2]) * 1000.0);
704
+ if (isnan(NUM2DBL(opt)) || isinf(NUM2DBL(opt))) {
705
+ ret = create_argument_error("unsupportd :quality option value");
706
+
707
+ } else if (NUM2DBL(opt) < 0.0) {
708
+ ret = create_range_error(":quality less than 0");
709
+
710
+ } else if (NUM2DBL(opt) > 100.0) {
711
+ ret = create_range_error(":quality greater than 100");
712
+
713
+ } else {
714
+ quality = NUM2INT(opt);
715
+ }
522
716
  break;
523
717
 
524
- case T_RATIONAL:
525
- scale_num = FIX2INT(rb_rational_num(opts[2]));
526
- scale_denom = FIX2INT(rb_rational_den(opts[2]));
718
+ case T_FIXNUM:
719
+ if (FIX2LONG(opt) < 0) {
720
+ ret = create_range_error(":quality less than 0");
721
+
722
+ } if (FIX2LONG(opt) > 100) {
723
+ ret = create_range_error(":quality greater than 100");
724
+
725
+ } else {
726
+ quality = FIX2INT(opt);
727
+ }
527
728
  break;
528
729
 
529
730
  default:
530
- ARGUMENT_ERROR("Unsupportd :scale option value.");
731
+ ret = create_type_error("unsupportd :quality option type");
531
732
  break;
532
733
  }
533
734
 
534
- /*
535
- * eval dct_method option
536
- */
537
- if (opts[3] == Qundef || EQ_STR(opts[3], "FASTEST")) {
538
- ptr->dct_method = JDCT_FASTEST;
735
+ if (!RTEST(ret)) ptr->quality = quality;
539
736
 
540
- } else if (EQ_STR(opts[3], "ISLOW")) {
541
- ptr->dct_method = JDCT_ISLOW;
737
+ return ret;
738
+ }
542
739
 
543
- } else if (EQ_STR(opts[3], "IFAST")) {
544
- ptr->dct_method = JDCT_IFAST;
740
+ static VALUE
741
+ eval_encoder_dct_method_opt(jpeg_encode_t* ptr, VALUE opt)
742
+ {
743
+ VALUE ret;
744
+ int dct_method;
545
745
 
546
- } else if (EQ_STR(opts[3], "FLOAT")) {
547
- ptr->dct_method = JDCT_FLOAT;
746
+ ret = Qnil;
548
747
 
549
- } else {
550
- ARGUMENT_ERROR("Unsupportd :dct_method option value.");
551
- }
748
+ switch (TYPE(opt)) {
749
+ case T_UNDEF:
750
+ dct_method = JDCT_FASTEST;
751
+ break;
552
752
 
553
- /*
554
- * set context
555
- */
556
- ptr->format = format;
557
- ptr->width = wd;
558
- ptr->height = ht;
559
- ptr->data_size = data_size;
560
- ptr->array = ALLOC_ARRAY();
561
- ptr->rows = ALLOC_ROWS(wd, components);
753
+ case T_STRING:
754
+ case T_SYMBOL:
755
+ if (EQ_STR(opt, "FASTEST")) {
756
+ dct_method = JDCT_FASTEST;
562
757
 
563
- for (i = 0; i < UNIT_LINES; i++) {
564
- ptr->array[i] = ptr->rows + (i * components * wd);
565
- }
758
+ } else if (EQ_STR(opt, "ISLOW")) {
759
+ dct_method = JDCT_ISLOW;
566
760
 
567
- jpeg_create_compress(&ptr->cinfo);
761
+ } else if (EQ_STR(opt, "IFAST")) {
762
+ dct_method = JDCT_IFAST;
763
+
764
+ } else if (EQ_STR(opt, "FLOAT")) {
765
+ dct_method = JDCT_FLOAT;
766
+
767
+ } else {
768
+ ret = create_argument_error("unsupportd :dct_method option value");
769
+ }
770
+ break;
771
+
772
+ default:
773
+ ret = create_type_error("unsupportd :dct_method option type");
774
+ break;
775
+ }
776
+
777
+ if (!RTEST(ret)) ptr->dct_method = dct_method;
778
+
779
+ return ret;
780
+ }
781
+
782
+ static VALUE
783
+ eval_encoder_orientation_opt(jpeg_encode_t* ptr, VALUE opt)
784
+ {
785
+ VALUE ret;
786
+ int orientation;
787
+
788
+ ret = Qnil;
789
+
790
+ switch (TYPE(opt)) {
791
+ case T_UNDEF:
792
+ orientation = 0;
793
+ break;
794
+
795
+ case T_FIXNUM:
796
+ orientation = FIX2INT(opt);
797
+ if (orientation < 1 || orientation > 8) {
798
+ ret = create_range_error(":orientation option ouf range");
799
+ }
800
+ break;
801
+
802
+ default:
803
+ ret = create_type_error("Unsupportd :orientation option type.");
804
+ break;
805
+ }
806
+
807
+ if (!RTEST(ret)) ptr->orientation = orientation;
808
+
809
+ return ret;
810
+ }
811
+
812
+ static VALUE
813
+ eval_encoder_stride_opt(jpeg_encode_t* ptr, VALUE opt)
814
+ {
815
+ VALUE ret;
816
+ int stride;
817
+
818
+ ret = Qnil;
819
+
820
+ switch (TYPE(opt)) {
821
+ case T_UNDEF:
822
+ stride = ptr->width * ptr->components;
823
+ break;
824
+
825
+ case T_FIXNUM:
826
+ stride = FIX2INT(opt);
827
+ if (stride < (ptr->width * ptr->components)) {
828
+ ret = create_range_error(":stride too little");
829
+ }
830
+ break;
831
+
832
+ default:
833
+ ret = create_type_error("unsupportd :stride option type");
834
+ }
835
+
836
+ if (!RTEST(ret)) ptr->stride = stride;
837
+
838
+ return ret;
839
+ }
840
+
841
+ static VALUE
842
+ set_encoder_context(jpeg_encode_t* ptr, int wd, int ht, VALUE opt)
843
+ {
844
+ VALUE ret;
845
+ VALUE opts[N(encoder_opts_ids)];
846
+ JSAMPARRAY ary;
847
+ JSAMPROW rows;
848
+
849
+ int i;
850
+
851
+ /*
852
+ * initialize
853
+ */
854
+ ret = Qnil;
855
+ ary = NULL;
856
+ rows = NULL;
857
+
858
+ /*
859
+ * argument check
860
+ */
861
+ do {
862
+ if (wd <= 0) {
863
+ ret = create_range_error("image width less equal zero");
864
+ break;
865
+ }
866
+
867
+ if (ht <= 0) {
868
+ ret = create_range_error("image height less equal zero");
869
+ break;
870
+ }
871
+ } while (0);
872
+
873
+ /*
874
+ * parse options
875
+ */
876
+ if (!RTEST(ret)) do {
877
+ rb_get_kwargs(opt, encoder_opts_ids, 0, N(encoder_opts_ids), opts);
878
+
879
+ // オプション評価で使用するので前もって設定しておく
880
+ ptr->width = wd;
881
+ ptr->height = ht;
882
+
883
+ ret = eval_encoder_pixel_format_opt(ptr, opts[0]);
884
+ if (RTEST(ret)) break;
885
+
886
+ ret = eval_encoder_quality_opt(ptr, opts[1]);
887
+ if (RTEST(ret)) break;
888
+
889
+ ret = eval_encoder_dct_method_opt(ptr, opts[2]);
890
+ if (RTEST(ret)) break;
891
+
892
+ ret = eval_encoder_orientation_opt(ptr, opts[3]);
893
+ if (RTEST(ret)) break;
894
+
895
+ ret = eval_encoder_stride_opt(ptr, opts[4]);
896
+ if (RTEST(ret)) break;
897
+ } while (0);
898
+
899
+ /*
900
+ * alloc memory
901
+ */
902
+ if (!RTEST(ret)) do {
903
+ ary = ALLOC_ARRAY();
904
+ if (ary == NULL) {
905
+ ret = create_memory_error();
906
+ break;
907
+ }
908
+
909
+ rows = ALLOC_ROWS(ptr->width, ptr->components);
910
+ if (rows == NULL) {
911
+ ret = create_memory_error();
912
+ break;
913
+ }
914
+ } while (0);
568
915
 
569
- ptr->cinfo.err = jpeg_std_error(&ptr->jerr);
570
- ptr->jerr.output_message = encode_output_message;
571
- ptr->jerr.error_exit = encode_error_exit;
916
+ /*
917
+ * set the rest context parameter
918
+ */
919
+ if (!RTEST(ret)) {
920
+ ptr->err_mgr.jerr.output_message = output_message;
921
+ ptr->err_mgr.jerr.emit_message = emit_message;
922
+ ptr->err_mgr.jerr.error_exit = error_exit;
923
+
924
+ ptr->data_size = ptr->stride * ptr->height;
925
+ ptr->buf.mem = NULL;
926
+ ptr->buf.size = 0;
927
+ ptr->array = ary;
928
+ ptr->rows = rows;
929
+ ptr->data = Qnil;
930
+
931
+ for (i = 0; i < UNIT_LINES; i++) {
932
+ ptr->array[i] = ptr->rows + (i * ptr->width * ptr->components);
933
+ }
934
+ }
572
935
 
573
- ptr->cinfo.image_width = wd;
574
- ptr->cinfo.image_height = ht;
575
- ptr->cinfo.in_color_space = color_space;
576
- ptr->cinfo.input_components = components;
936
+ /*
937
+ * setup libjpeg
938
+ */
939
+ if (!RTEST(ret)) {
940
+ jpeg_create_compress(&ptr->cinfo);
941
+ SET_FLAG(ptr, F_CREAT);
942
+
943
+ ptr->cinfo.err = jpeg_std_error(&ptr->err_mgr.jerr);
944
+ ptr->cinfo.image_width = ptr->width;
945
+ ptr->cinfo.image_height = ptr->height;
946
+ ptr->cinfo.in_color_space = ptr->color_space;
947
+ ptr->cinfo.input_components = ptr->components;
948
+
949
+ ptr->cinfo.optimize_coding = TRUE;
950
+ ptr->cinfo.arith_code = TRUE;
951
+ ptr->cinfo.raw_data_in = FALSE;
952
+ ptr->cinfo.dct_method = ptr->dct_method;
953
+
954
+ jpeg_set_defaults(&ptr->cinfo);
955
+ jpeg_set_quality(&ptr->cinfo, ptr->quality, TRUE);
956
+ jpeg_suppress_tables(&ptr->cinfo, TRUE);
957
+ }
577
958
 
578
- ptr->cinfo.optimize_coding = TRUE;
579
- ptr->cinfo.arith_code = TRUE;
580
- ptr->cinfo.raw_data_in = FALSE;
581
- ptr->cinfo.dct_method = ptr->dct_method;
959
+ /*
960
+ * post process
961
+ */
962
+ if (RTEST(ret)) {
963
+ if (ary != NULL) free(ary);
964
+ if (rows != NULL) free(rows);
965
+ }
582
966
 
583
- jpeg_set_defaults(&ptr->cinfo);
584
- jpeg_set_quality(&ptr->cinfo, quality, TRUE);
585
- jpeg_suppress_tables(&ptr->cinfo, TRUE);
967
+ return ret;
586
968
  }
587
969
 
970
+ /**
971
+ * initialize encoder object
972
+ *
973
+ * @overload initialize(width, height, opts)
974
+ *
975
+ * @param width [Integer] width of input image (px)
976
+ * @param height [Integer] height of input image (px)
977
+ * @param opts [Hash] options to initialize object
978
+ *
979
+ * @option opts [Symbol] :pixel_format
980
+ * specifies the format of the input image. possible values are:
981
+ * YUV422 YUYV RGB565 RGB RGB24 BGR BGR24 YUV444 YCbCr
982
+ * RGBX RGB32 BGRX BGR32 GRAYSCALE
983
+ *
984
+ * @option opts [Integer] :quality
985
+ * specifies the quality of the compressed image.
986
+ * You can specify from 0 (lowest) to 100 (best).
987
+ *
988
+ * @option opts [Symbol] :dct_method
989
+ * specifies how encoding is handled. possible values are:
990
+ * FASTEST ISLOW IFAST FLOAT
991
+ */
588
992
  static VALUE
589
993
  rb_encoder_initialize(int argc, VALUE *argv, VALUE self)
590
994
  {
591
995
  jpeg_encode_t* ptr;
996
+ VALUE exc;
592
997
  VALUE wd;
593
998
  VALUE ht;
594
999
  VALUE opt;
@@ -596,191 +1001,281 @@ rb_encoder_initialize(int argc, VALUE *argv, VALUE self)
596
1001
  /*
597
1002
  * initialize
598
1003
  */
599
- Data_Get_Struct(self, jpeg_encode_t, ptr);
1004
+ exc = Qnil;
1005
+
1006
+ TypedData_Get_Struct(self, jpeg_encode_t, &jpeg_encoder_data_type, ptr);
600
1007
 
601
1008
  /*
602
1009
  * parse arguments
603
1010
  */
604
- rb_scan_args(argc, argv, "21", &wd, &ht, &opt);
1011
+ rb_scan_args(argc, argv, "2:", &wd, &ht, &opt);
1012
+
1013
+ /*
1014
+ * argument check
1015
+ */
1016
+ do {
1017
+ if (TYPE(wd) != T_FIXNUM) {
1018
+ exc = create_argument_error("invalid width");
1019
+ break;
1020
+ }
605
1021
 
606
- Check_Type(wd, T_FIXNUM);
607
- Check_Type(ht, T_FIXNUM);
608
- if (opt != Qnil) Check_Type(opt, T_HASH);
1022
+ if (TYPE(ht) != T_FIXNUM) {
1023
+ exc = create_argument_error("invalid height");
1024
+ break;
1025
+ }
1026
+ } while (0);
609
1027
 
610
1028
  /*
611
1029
  * set context
612
1030
  */
613
- set_encoder_context(ptr, FIX2INT(wd), FIX2INT(ht), opt);
1031
+ if (!RTEST(exc)) {
1032
+ exc = set_encoder_context(ptr, FIX2INT(wd), FIX2INT(ht), opt);
1033
+ }
1034
+
1035
+ /*
1036
+ * post process
1037
+ */
1038
+ if (RTEST(exc)) rb_exc_raise(exc);
614
1039
 
615
1040
  return Qtrue;
616
1041
  }
617
1042
 
618
- static int
619
- push_rows_yuv422(JSAMPROW rows, int wd, uint8_t* data, int nrow)
1043
+ static void
1044
+ push_rows_yuv422(JSAMPROW dst, int wd, int st, uint8_t* data, int nrow)
620
1045
  {
621
- int size;
1046
+ uint8_t* src;
622
1047
  int i;
1048
+ int j;
623
1049
 
624
- size = wd * nrow;
1050
+ for (i = 0; i < nrow; i++) {
1051
+ src = data;
625
1052
 
626
- for (i = 0; i < size; i += 2) {
627
- rows[0] = data[0];
628
- rows[1] = data[1];
629
- rows[2] = data[3];
630
- rows[3] = data[2];
631
- rows[4] = data[1];
632
- rows[5] = data[3];
1053
+ for (j = 0; j < wd; j += 2) {
1054
+ dst[0] = src[0];
1055
+ dst[1] = src[1];
1056
+ dst[2] = src[3];
1057
+ dst[3] = src[2];
1058
+ dst[4] = src[1];
1059
+ dst[5] = src[3];
633
1060
 
634
- rows += 6;
635
- data += 4;
636
- }
1061
+ dst += 6;
1062
+ src += 4;
1063
+ }
637
1064
 
638
- return (size * 2);
1065
+ data += st;
1066
+ }
639
1067
  }
640
1068
 
641
- static int
642
- push_rows_rgb565(JSAMPROW rows, int wd, uint8_t* data, int nrow)
1069
+ static void
1070
+ push_rows_rgb565(JSAMPROW dst, int wd, int st, uint8_t* data, int nrow)
643
1071
  {
644
- int size;
1072
+ uint8_t* src;
645
1073
  int i;
1074
+ int j;
646
1075
 
647
- size = wd * nrow;
1076
+ for (i = 0; i < nrow; i++) {
1077
+ src = data;
648
1078
 
649
- for (i = 0; i < size; i += 1) {
650
- rows[0] = data[1] & 0xf8;
651
- rows[1] = ((data[1] << 5) & 0xe0) | ((data[0] >> 3) & 0x1c);
652
- rows[2] = (data[0] << 3) & 0xf8;
1079
+ for (j = 0; j < wd; j++) {
1080
+ dst[0] = src[1] & 0xf8;
1081
+ dst[1] = ((src[1] << 5) & 0xe0) | ((src[0] >> 3) & 0x1c);
1082
+ dst[2] = (src[0] << 3) & 0xf8;
653
1083
 
654
- rows += 3;
655
- data += 2;
656
- }
1084
+ dst += 3;
1085
+ src += 2;
1086
+ }
657
1087
 
658
- return (size * 2);
1088
+ data += st;
1089
+ }
659
1090
  }
660
1091
 
661
- static int
662
- push_rows_comp3(JSAMPROW rows, int wd, uint8_t* data, int nrow)
1092
+ static void
1093
+ push_rows_comp3(JSAMPROW rows, int wd, int st, uint8_t* data, int nrow)
663
1094
  {
664
1095
  int size;
1096
+ int i;
1097
+
1098
+ size = wd * 3;
665
1099
 
666
- size = wd * nrow * 3;
667
- memcpy(rows, data, size);
1100
+ for (i = 0; i < nrow; i++) {
1101
+ memcpy(rows, data, size);
668
1102
 
669
- return size;
1103
+ rows += size;
1104
+ data += st;
1105
+ }
670
1106
  }
671
1107
 
672
- static int
673
- push_rows_comp4(JSAMPROW rows, int wd, uint8_t* data, int nrow)
1108
+ static void
1109
+ push_rows_comp4(JSAMPROW rows, int wd, int st, uint8_t* data, int nrow)
674
1110
  {
675
1111
  int size;
1112
+ int i;
676
1113
 
677
- size = wd * nrow * 4;
678
- memcpy(rows, data, size);
679
-
680
- return size;
681
- }
1114
+ size = wd * 4;
682
1115
 
1116
+ for (i = 0; i < nrow; i++) {
1117
+ memcpy(rows, data, size);
683
1118
 
1119
+ rows += size;
1120
+ data += st;
1121
+ }
1122
+ }
684
1123
 
685
- static int
686
- push_rows_grayscale(JSAMPROW rows, int wd, uint8_t* data, int nrow)
1124
+ static void
1125
+ push_rows_grayscale(JSAMPROW rows, int wd, int st, uint8_t* data, int nrow)
687
1126
  {
688
- int size;
1127
+ int i;
689
1128
 
690
- size = wd * nrow;
691
- memcpy(rows, data, size);
1129
+ for (i = 0; i < nrow; i++) {
1130
+ memcpy(rows, data, wd);
692
1131
 
693
- return size;
1132
+ rows += wd;
1133
+ data += st;
1134
+ }
694
1135
  }
695
1136
 
696
- static int
1137
+ static void
697
1138
  push_rows(jpeg_encode_t* ptr, uint8_t* data, int nrow)
698
1139
  {
699
- int ret;
700
-
701
1140
  switch (ptr->format) {
702
1141
  case FMT_YUV422:
703
- ret = push_rows_yuv422(ptr->rows, ptr->width, data, nrow);
1142
+ push_rows_yuv422(ptr->rows, ptr->width, ptr->stride, data, nrow);
704
1143
  break;
705
1144
 
706
1145
  case FMT_RGB565:
707
- ret = push_rows_rgb565(ptr->rows, ptr->width, data, nrow);
1146
+ push_rows_rgb565(ptr->rows, ptr->width, ptr->stride, data, nrow);
708
1147
  break;
709
1148
 
710
1149
  case FMT_YUV:
711
1150
  case FMT_RGB:
712
1151
  case FMT_BGR:
713
- ret = push_rows_comp3(ptr->rows, ptr->width, data, nrow);
1152
+ push_rows_comp3(ptr->rows, ptr->width, ptr->stride, data, nrow);
714
1153
  break;
715
1154
 
716
1155
  case FMT_RGB32:
717
1156
  case FMT_BGR32:
718
- ret = push_rows_comp4(ptr->rows, ptr->width, data, nrow);
1157
+ push_rows_comp4(ptr->rows, ptr->width, ptr->stride, data, nrow);
719
1158
  break;
720
1159
 
721
1160
  case FMT_GRAYSCALE:
722
- ret = push_rows_grayscale(ptr->rows, ptr->width, data, nrow);
1161
+ push_rows_grayscale(ptr->rows, ptr->width, ptr->stride, data, nrow);
723
1162
  break;
724
1163
 
725
1164
  default:
726
1165
  RUNTIME_ERROR("Really?");
727
1166
  }
1167
+ }
728
1168
 
729
- return ret;
1169
+ static void
1170
+ put_exif_tags(jpeg_encode_t* ptr)
1171
+ {
1172
+ uint8_t data[] = {
1173
+ /* Exif header */
1174
+ 'E', 'x', 'i', 'f', 0x00, 0x00,
1175
+ 'M', 'M',
1176
+ 0x00, 0x2a,
1177
+ 0x00, 0x00, 0x00, 0x08,
1178
+ 0x00, 0x01,
1179
+
1180
+ /* orieatation */
1181
+ 0x01, 0x12,
1182
+ 0x00, 0x03,
1183
+ 0x00, 0x00, 0x00, 0x01,
1184
+ 0x00, 0x00,
1185
+ 0x00, 0x00,
1186
+
1187
+ /* end mark */
1188
+ 0x00, 0x00, 0x00, 0x00,
1189
+ };
1190
+
1191
+ data[24] = (ptr->orientation >> 8) & 0xff;
1192
+ data[25] = (ptr->orientation >> 0) & 0xff;
1193
+
1194
+ jpeg_write_marker(&ptr->cinfo, JPEG_APP1, data, sizeof(data));
730
1195
  }
731
1196
 
732
1197
  static VALUE
733
- do_encode(jpeg_encode_t* ptr, uint8_t* data)
1198
+ do_encode(VALUE _ptr)
734
1199
  {
735
1200
  VALUE ret;
736
-
737
- unsigned char* buf;
738
- unsigned long buf_size;
1201
+ jpeg_encode_t* ptr;
1202
+ uint8_t* data;
739
1203
  int nrow;
740
1204
 
741
- buf = NULL;
1205
+ /*
1206
+ * initialize
1207
+ */
1208
+ ret = Qnil;
1209
+ ptr = (jpeg_encode_t*)_ptr;
1210
+ data = (uint8_t*)RSTRING_PTR(ptr->data);
742
1211
 
1212
+ /*
1213
+ * do encode
1214
+ */
1215
+ if (setjmp(ptr->err_mgr.jmpbuf)) {
1216
+ /*
1217
+ * when error occurred
1218
+ */
1219
+ jpeg_abort_compress(&ptr->cinfo);
1220
+ rb_raise(encerr_klass, "%s", ptr->err_mgr.msg);
743
1221
 
744
- jpeg_mem_dest(&ptr->cinfo, &buf, &buf_size);
1222
+ } else {
1223
+ /*
1224
+ * normal path
1225
+ */
1226
+ jpeg_start_compress(&ptr->cinfo, TRUE);
745
1227
 
746
- jpeg_start_compress(&ptr->cinfo, TRUE);
1228
+ if (ptr->orientation != 0) {
1229
+ put_exif_tags(ptr);
1230
+ }
747
1231
 
748
- while (ptr->cinfo.next_scanline < ptr->cinfo.image_height) {
749
- nrow = ptr->cinfo.image_height - ptr->cinfo.next_scanline;
750
- if (nrow > UNIT_LINES) nrow = UNIT_LINES;
1232
+ while (ptr->cinfo.next_scanline < ptr->cinfo.image_height) {
1233
+ nrow = ptr->cinfo.image_height - ptr->cinfo.next_scanline;
1234
+ if (nrow > UNIT_LINES) nrow = UNIT_LINES;
751
1235
 
752
- data += push_rows(ptr, data, nrow);
753
- jpeg_write_scanlines(&ptr->cinfo, ptr->array, nrow);
754
- }
1236
+ push_rows(ptr, data, nrow);
755
1237
 
756
- jpeg_finish_compress(&ptr->cinfo);
1238
+ jpeg_write_scanlines(&ptr->cinfo, ptr->array, nrow);
1239
+ data += (ptr->stride * nrow);
1240
+ }
757
1241
 
758
- /*
759
- * create return data
760
- */
761
- ret = rb_str_buf_new(buf_size);
762
- rb_str_set_len(ret, buf_size);
1242
+ jpeg_finish_compress(&ptr->cinfo);
763
1243
 
764
- memcpy(RSTRING_PTR(ret), buf, buf_size);
1244
+ /*
1245
+ * build return data
1246
+ */
1247
+ ret = rb_str_buf_new(ptr->buf.size);
1248
+ rb_str_set_len(ret, ptr->buf.size);
765
1249
 
766
- /*
767
- * post process
768
- */
769
- free(buf);
1250
+ memcpy(RSTRING_PTR(ret), ptr->buf.mem, ptr->buf.size);
1251
+ }
770
1252
 
771
1253
  return ret;
772
1254
  }
773
1255
 
1256
+ /**
1257
+ * encode data
1258
+ *
1259
+ * @overload encode(raw)
1260
+ *
1261
+ * @param raw [String] raw image data to encode.
1262
+ *
1263
+ * @return [String] encoded JPEG data.
1264
+ */
774
1265
  static VALUE
775
1266
  rb_encoder_encode(VALUE self, VALUE data)
776
1267
  {
777
1268
  VALUE ret;
1269
+ int state;
778
1270
  jpeg_encode_t* ptr;
779
1271
 
780
1272
  /*
781
1273
  * initialize
782
1274
  */
783
- Data_Get_Struct(self, jpeg_encode_t, ptr);
1275
+ ret = Qnil;
1276
+ state = 0;
1277
+
1278
+ TypedData_Get_Struct(self, jpeg_encode_t, &jpeg_encoder_data_type, ptr);
784
1279
 
785
1280
  /*
786
1281
  * argument check
@@ -788,70 +1283,123 @@ rb_encoder_encode(VALUE self, VALUE data)
788
1283
  Check_Type(data, T_STRING);
789
1284
 
790
1285
  if (RSTRING_LEN(data) < ptr->data_size) {
791
- ARGUMENT_ERROR("raw image data is too short.");
1286
+ ARGUMENT_ERROR("image data is too short");
792
1287
  }
793
1288
 
794
1289
  if (RSTRING_LEN(data) > ptr->data_size) {
795
- ARGUMENT_ERROR("raw image data is too large.");
1290
+ ARGUMENT_ERROR("image data is too large");
1291
+ }
1292
+
1293
+ /*
1294
+ * alloc memory
1295
+ */
1296
+ jpeg_mem_dest(&ptr->cinfo, &ptr->buf.mem, &ptr->buf.size);
1297
+ if (ptr->buf.mem == NULL) {
1298
+ RUNTIME_ERROR("jpeg_mem_dest() failed");
796
1299
  }
797
1300
 
1301
+ /*
1302
+ * prepare
1303
+ */
1304
+ SET_DATA(ptr, data);
1305
+
798
1306
  /*
799
1307
  * do encode
800
1308
  */
801
- ret = do_encode(ptr, (uint8_t*)RSTRING_PTR(data));
1309
+ ret = rb_protect(do_encode, (VALUE)ptr, &state);
1310
+
1311
+ /*
1312
+ * post process
1313
+ */
1314
+ CLR_DATA(ptr);
1315
+
1316
+ if (ptr->buf.mem != NULL) {
1317
+ free(ptr->buf.mem);
1318
+
1319
+ ptr->buf.mem = NULL;
1320
+ ptr->buf.size = 0;
1321
+ }
1322
+
1323
+ if (state != 0) rb_jump_tag(state);
802
1324
 
803
1325
  return ret;
804
1326
  }
805
1327
 
806
1328
  static void
807
- rb_decoder_free(void* ptr)
1329
+ rb_decoder_mark(void* _ptr)
808
1330
  {
809
- //jpeg_destroy_decompress(&(((jpeg_decode_t*)ptr)->cinfo));
1331
+ jpeg_decode_t* ptr;
810
1332
 
811
- free(ptr);
1333
+ ptr = (jpeg_decode_t*)_ptr;
1334
+
1335
+ if (ptr->orientation.buf != Qnil) {
1336
+ rb_gc_mark(ptr->orientation.buf);
1337
+ }
1338
+
1339
+ if (ptr->data != Qnil) {
1340
+ rb_gc_mark(ptr->data);
1341
+ }
812
1342
  }
813
1343
 
814
1344
  static void
815
- decode_output_message(j_common_ptr cinfo)
1345
+ rb_decoder_free(void* _ptr)
816
1346
  {
817
- ext_error_t* err;
1347
+ jpeg_decode_t* ptr;
818
1348
 
819
- err = (ext_error_t*)cinfo->err;
1349
+ ptr = (jpeg_decode_t*)_ptr;
820
1350
 
821
- (*err->jerr.format_message)(cinfo, err->msg);
822
- }
1351
+ if (ptr->array != NULL) {
1352
+ free(ptr->array);
1353
+ }
823
1354
 
824
- static void
825
- decode_emit_message(j_common_ptr cinfo, int msg_level)
826
- {
827
- ext_error_t* err;
1355
+ ptr->orientation.buf = Qnil;
1356
+ ptr->data = Qnil;
828
1357
 
829
- if (msg_level < 0) {
830
- err = (ext_error_t*)cinfo->err;
831
- (*err->jerr.format_message)(cinfo, err->msg);
832
- /*
833
- * 以前はemit_messageが呼ばれるとエラー扱いしていたが、
834
- * Logicoolの一部のモデルなどで問題が出るので無視する
835
- * ようにした。
836
- * また本来であれば、警告表示を行うべきでもあるが一部
837
- * のモデルで大量にemitされる場合があるので表示しない
838
- * ようにしている。
839
- * 問題が発生した際は、最後のメッセージをオブジェクト
840
- * のインスタンスとして持たすべき。
841
- */
842
- // longjmp(err->jmpbuf, 1);
1358
+ if (TEST_FLAG(ptr, F_CREAT)) {
1359
+ jpeg_destroy_decompress(&ptr->cinfo);
843
1360
  }
1361
+
1362
+ free(ptr);
844
1363
  }
845
1364
 
846
- static void
847
- decode_error_exit(j_common_ptr cinfo)
1365
+ static size_t
1366
+ rb_decoder_size(const void* ptr)
848
1367
  {
849
- ext_error_t* err;
1368
+ size_t ret;
850
1369
 
851
- err = (ext_error_t*)cinfo->err;
852
- (*err->jerr.format_message)(cinfo, err->msg);
853
- longjmp(err->jmpbuf, 1);
854
- }
1370
+ ret = sizeof(jpeg_decode_t);
1371
+
1372
+ return ret;
1373
+ }
1374
+
1375
+ #if RUBY_API_VERSION_CODE > 20600
1376
+ static const rb_data_type_t jpeg_decoder_data_type = {
1377
+ "libjpeg-ruby decoder object", // wrap_struct_name
1378
+ {
1379
+ rb_decoder_mark, // function.dmark
1380
+ rb_decoder_free, // function.dfree
1381
+ rb_decoder_size, // function.dsize
1382
+ NULL, // function.dcompact
1383
+ {NULL}, // function.reserved
1384
+ },
1385
+ NULL, // parent
1386
+ NULL, // data
1387
+ (VALUE)RUBY_TYPED_FREE_IMMEDIATELY // flags
1388
+ };
1389
+ #else /* RUBY_API_VERSION_CODE > 20600 */
1390
+ static const rb_data_type_t jpeg_decoder_data_type = {
1391
+ "libjpeg-ruby decoder object", // wrap_struct_name
1392
+ {
1393
+ rb_decoder_mark, // function.dmark
1394
+ rb_decoder_free, // function.dfree
1395
+ rb_decoder_size, // function.dsize
1396
+ {NULL, NULL}, // function.reserved
1397
+ },
1398
+ NULL, // parent
1399
+ NULL, // data
1400
+ (VALUE)RUBY_TYPED_FREE_IMMEDIATELY // flags
1401
+ };
1402
+ #endif /* RUBY_API_VERSION_CODE > 20600 */
855
1403
 
856
1404
  static VALUE
857
1405
  rb_decoder_alloc(VALUE self)
@@ -861,54 +1409,40 @@ rb_decoder_alloc(VALUE self)
861
1409
  ptr = ALLOC(jpeg_decode_t);
862
1410
  memset(ptr, 0, sizeof(*ptr));
863
1411
 
864
- ptr->format = FMT_RGB;
865
- ptr->need_meta = !0;
866
- ptr->expand_colormap = 0;
867
- ptr->parse_exif = 0;
868
-
869
- ptr->out_color_space = JCS_RGB;
870
-
871
- ptr->scale_num = 1;
872
- ptr->scale_denom = 1;
1412
+ ptr->flags = DEFAULT_DECODE_FLAGS;
873
1413
 
874
- ptr->out_color_components = 3;
875
- ptr->output_gamma = 0.0;
876
- ptr->buffered_image = FALSE;
877
- ptr->do_fancy_upsampling = FALSE;
878
- ptr->do_block_smoothing = FALSE;
879
- ptr->quantize_colors = FALSE;
880
- ptr->dither_mode = JDITHER_NONE;
881
- ptr->dct_method = JDCT_FASTEST;
882
- ptr->desired_number_of_colors = 0;
883
- ptr->enable_1pass_quant = FALSE;
884
- ptr->enable_external_quant = FALSE;
885
- ptr->enable_2pass_quant = FALSE;
886
-
887
- return Data_Wrap_Struct(decoder_klass, 0, rb_decoder_free, ptr);
1414
+ return TypedData_Wrap_Struct(decoder_klass, &jpeg_decoder_data_type, ptr);
888
1415
  }
889
1416
 
890
- static void
891
- eval_decoder_opt_pixel_format(jpeg_decode_t* ptr, VALUE opt)
1417
+ static VALUE
1418
+ eval_decoder_pixel_format_opt(jpeg_decode_t* ptr, VALUE opt)
892
1419
  {
1420
+ VALUE ret;
893
1421
  int format;
894
1422
  int color_space;
895
1423
  int components;
896
1424
 
897
- if (opt != Qundef) {
1425
+ ret = Qnil;
1426
+
1427
+ switch (TYPE(opt)) {
1428
+ case T_UNDEF:
1429
+ format = FMT_RGB;
1430
+ color_space = JCS_RGB;
1431
+ components = 3;
1432
+ break;
1433
+
1434
+ case T_STRING:
1435
+ case T_SYMBOL:
898
1436
  if(EQ_STR(opt, "RGB") || EQ_STR(opt, "RGB24")) {
899
1437
  format = FMT_RGB;
900
1438
  color_space = JCS_RGB;
901
1439
  components = 3;
902
1440
 
903
1441
  } else if (EQ_STR(opt, "YUV422") || EQ_STR(opt, "YUYV")) {
904
- format = FMT_YUV422;
905
- color_space = JCS_YCbCr;
906
- components = 3;
1442
+ ret = create_not_implement_error( "not implemented colorspace");
907
1443
 
908
1444
  } else if (EQ_STR(opt, "RGB565")) {
909
- format = FMT_RGB565;
910
- color_space = JCS_RGB;
911
- components = 3;
1445
+ ret = create_not_implement_error( "not implemented colorspace");
912
1446
 
913
1447
  } else if (EQ_STR(opt, "GRAYSCALE")) {
914
1448
  format = FMT_GRAYSCALE;
@@ -941,254 +1475,555 @@ eval_decoder_opt_pixel_format(jpeg_decode_t* ptr, VALUE opt)
941
1475
  components = 4;
942
1476
 
943
1477
  } else {
944
- ARGUMENT_ERROR("Unsupportd :pixel_format option value.");
1478
+ ret = create_argument_error("unsupportd :pixel_format option value");
945
1479
  }
1480
+ break;
1481
+
1482
+ default:
1483
+ ret = create_type_error("unsupportd :pixel_format option type");
1484
+ break;
1485
+ }
946
1486
 
947
- ptr->format = format;
948
- ptr->out_color_space = color_space;
949
- ptr->out_color_components = components;
1487
+ if (!RTEST(ret)) {
1488
+ ptr->format = format;
1489
+ ptr->out_color_space = color_space;
1490
+ ptr->out_color_components = components;
950
1491
  }
1492
+
1493
+ return ret;
951
1494
  }
952
1495
 
953
- static void
954
- eval_decoder_opt_output_gamma(jpeg_decode_t* ptr, VALUE opt)
1496
+ static VALUE
1497
+ eval_decoder_output_gamma_opt(jpeg_decode_t* ptr, VALUE opt)
955
1498
  {
1499
+ VALUE ret;
1500
+ double gamma;
1501
+
1502
+ ret = Qnil;
1503
+
956
1504
  switch (TYPE(opt)) {
957
1505
  case T_UNDEF:
958
- // Nothing
1506
+ gamma = 0.0;
959
1507
  break;
960
1508
 
1509
+ case T_FIXNUM:
961
1510
  case T_FLOAT:
962
- ptr->output_gamma = NUM2DBL(opt);
1511
+ if (isnan(NUM2DBL(opt)) || isinf(NUM2DBL(opt))) {
1512
+ ret = create_argument_error("unsupported :output_gamma value");
1513
+ } else {
1514
+ gamma = NUM2DBL(opt);
1515
+ }
963
1516
  break;
964
1517
 
965
1518
  default:
966
- ARGUMENT_ERROR("Unsupportd :output_gamma option value.");
1519
+ ret = create_type_error("unsupported :output_gamma type");
967
1520
  break;
968
1521
  }
1522
+
1523
+ if (!RTEST(ret)) ptr->output_gamma = gamma;
1524
+
1525
+ return ret;
969
1526
  }
970
1527
 
971
- static void
972
- eval_decoder_opt_do_fancy_upsampling(jpeg_decode_t* ptr, VALUE opt)
1528
+ static VALUE
1529
+ eval_decoder_do_fancy_upsampling_opt(jpeg_decode_t* ptr, VALUE opt)
973
1530
  {
974
- if (opt != Qundef) {
975
- ptr->do_fancy_upsampling = (RTEST(opt))? TRUE: FALSE;
1531
+ if (opt != Qundef && RTEST(opt)) {
1532
+ ptr->do_fancy_upsampling = TRUE;
1533
+ } else {
1534
+ ptr->do_fancy_upsampling = FALSE;
976
1535
  }
1536
+
1537
+ return Qnil;
977
1538
  }
978
1539
 
979
- static void
980
- eval_decoder_opt_do_smoothing(jpeg_decode_t* ptr, VALUE opt)
1540
+ static VALUE
1541
+ eval_decoder_do_smoothing_opt(jpeg_decode_t* ptr, VALUE opt)
981
1542
  {
982
- if (opt != Qundef) {
983
- ptr->do_block_smoothing = (RTEST(opt))? TRUE: FALSE;
1543
+ if (opt != Qundef && RTEST(opt)) {
1544
+ ptr->do_block_smoothing = TRUE;
1545
+ } else {
1546
+ ptr->do_block_smoothing = FALSE;
984
1547
  }
1548
+
1549
+ return Qnil;
985
1550
  }
986
1551
 
987
- static void
988
- eval_decoder_opt_dither( jpeg_decode_t* ptr, VALUE opt)
1552
+ static VALUE
1553
+ eval_decoder_dither_opt( jpeg_decode_t* ptr, VALUE opt)
989
1554
  {
990
- VALUE dmode;
991
- VALUE pass2;
992
- VALUE n_col;
1555
+ VALUE ret;
1556
+ VALUE tmp;
1557
+ int mode;
1558
+ int quant;
1559
+ int pass2;
1560
+ int ncol;
993
1561
 
994
- if (opt != Qundef) {
995
- if (TYPE(opt) != T_ARRAY) {
996
- ARGUMENT_ERROR("Unsupportd :dither option value.");
997
- }
1562
+ ret = Qnil;
1563
+
1564
+ switch (TYPE(opt)) {
1565
+ case T_UNDEF:
1566
+ mode = JDITHER_NONE;
1567
+ quant = FALSE;
1568
+ ncol = 0;
1569
+ pass2 = FALSE;
1570
+ break;
998
1571
 
1572
+ case T_ARRAY:
999
1573
  if (RARRAY_LEN(opt) != 3) {
1000
- ARGUMENT_ERROR(":dither is illeagal length (shall be 3 entries).");
1001
- }
1574
+ ret = create_argument_error(":dither invalid size");
1575
+
1576
+ } else do {
1577
+ /*
1578
+ * dither mode
1579
+ */
1580
+ tmp = rb_ary_entry(opt, 0);
1581
+ if (TYPE(tmp) != T_STRING && TYPE(tmp) != T_SYMBOL) {
1582
+ ret = create_type_error("unsupported dither mode type");
1583
+ break;
1002
1584
 
1003
- dmode = RARRAY_AREF(opt, 0);
1004
- pass2 = RARRAY_AREF(opt, 1);
1005
- n_col = RARRAY_AREF(opt, 2);
1006
-
1007
- if (EQ_STR(dmode, "NONE")) {
1008
- ptr->dither_mode = JDITHER_NONE;
1009
- ptr->quantize_colors = FALSE;
1585
+ } else if (EQ_STR(tmp, "NONE")) {
1586
+ mode = JDITHER_NONE;
1587
+ quant = FALSE;
1010
1588
 
1011
- } else if(EQ_STR(dmode, "ORDERED")) {
1012
- ptr->dither_mode = JDITHER_ORDERED;
1013
- ptr->quantize_colors = TRUE;
1589
+ } else if(EQ_STR(tmp, "ORDERED")) {
1590
+ mode = JDITHER_ORDERED;
1591
+ quant = TRUE;
1014
1592
 
1015
- } else if(EQ_STR(dmode, "FS")) {
1016
- ptr->dither_mode = JDITHER_FS;
1017
- ptr->quantize_colors = TRUE;
1593
+ } else if(EQ_STR(tmp, "FS")) {
1594
+ mode = JDITHER_FS;
1595
+ quant = TRUE;
1018
1596
 
1019
- } else {
1020
- ARGUMENT_ERROR("dither mode is illeagal value.");
1021
- }
1597
+ } else {
1598
+ ret = create_argument_error("dither mode is illeagal value.");
1599
+ break;
1600
+ }
1022
1601
 
1023
- ptr->two_pass_quantize = (RTEST(pass2))? TRUE: FALSE;
1602
+ /*
1603
+ * 2 pass flag
1604
+ */
1605
+ pass2 = (RTEST(rb_ary_entry(opt, 1)))? TRUE: FALSE;
1606
+
1607
+ /*
1608
+ * number of color
1609
+ */
1610
+ tmp = rb_ary_entry(opt, 2);
1611
+ if (TYPE(tmp) != T_FIXNUM) {
1612
+ ret = create_type_error("unsupported number of colors type");
1613
+ break;
1024
1614
 
1025
- if (TYPE(n_col) == T_FIXNUM) {
1026
- ptr->desired_number_of_colors = FIX2INT(n_col);
1027
- } else {
1028
- ARGUMENT_ERROR( "number of dithered colors is illeagal value.");
1029
- }
1615
+ } else if (FIX2LONG(tmp) < 8) {
1616
+ ret = create_range_error("number of colors less than 0");
1617
+ break;
1618
+
1619
+ } else if (FIX2LONG(tmp) > 256) {
1620
+ ret = create_range_error("number of colors greater than 256");
1621
+ break;
1622
+
1623
+ } else {
1624
+ ncol = FIX2INT(tmp);
1625
+ }
1626
+ } while (0);
1627
+ break;
1628
+
1629
+ default:
1630
+ ret = create_type_error("unsupported :dither type");
1631
+ break;
1632
+ }
1633
+
1634
+ if (!RTEST(ret)) {
1635
+ ptr->dither_mode = mode;
1636
+ ptr->quantize_colors = quant;
1637
+ ptr->two_pass_quantize = pass2;
1638
+ ptr->desired_number_of_colors = ncol;
1639
+
1640
+ if (mode != JDITHER_NONE) SET_FLAG(ptr, F_DITHER);
1030
1641
  }
1642
+
1643
+ return ret;
1031
1644
  }
1032
1645
 
1646
+ #if 0
1033
1647
  static void
1034
- eval_decoder_opt_use_1pass_quantizer(jpeg_decode_t* ptr, VALUE opt)
1648
+ eval_decoder_use_1pass_quantizer_opt(jpeg_decode_t* ptr, VALUE opt)
1035
1649
  {
1036
- if (opt != Qundef) {
1037
- if (RTEST(opt)) {
1038
- ptr->enable_1pass_quant = TRUE;
1039
- ptr->buffered_image = TRUE;
1040
- } else {
1041
- ptr->enable_1pass_quant = FALSE;
1042
- }
1650
+ if (opt != Qundef && RTEST(opt)) {
1651
+ ptr->enable_1pass_quant = TRUE;
1652
+ } else {
1653
+ ptr->enable_1pass_quant = FALSE;
1043
1654
  }
1655
+
1656
+ return Qnil;
1044
1657
  }
1045
1658
 
1046
- static void
1047
- eval_decoder_opt_use_external_colormap(jpeg_decode_t* ptr, VALUE opt)
1659
+ static VALUE
1660
+ eval_decoder_use_external_colormap_opt(jpeg_decode_t* ptr, VALUE opt)
1048
1661
  {
1049
- if (opt != Qundef) {
1050
- if (RTEST(opt)) {
1662
+ if (opt != Qundef && RTEST(opt)) {
1051
1663
  ptr->enable_external_quant = TRUE;
1052
- ptr->buffered_image = TRUE;
1053
1664
  } else {
1054
1665
  ptr->enable_external_quant = FALSE;
1055
1666
  }
1056
1667
  }
1668
+
1669
+ return Qnil;
1057
1670
  }
1058
1671
 
1059
- static void
1060
- eval_decoder_opt_use_2pass_quantizer(jpeg_decode_t* ptr, VALUE opt)
1672
+ static VALUE
1673
+ eval_decoder_use_2pass_quantizer_opt(jpeg_decode_t* ptr, VALUE opt)
1061
1674
  {
1062
- if (opt != Qundef) {
1063
- if (RTEST(opt)) {
1675
+ if (opt != Qundef && RTEST(opt)) {
1064
1676
  ptr->enable_2pass_quant = TRUE;
1065
- ptr->buffered_image = TRUE;
1066
1677
  } else {
1067
1678
  ptr->enable_2pass_quant = FALSE;
1068
1679
  }
1069
1680
  }
1681
+
1682
+ return Qnil;
1070
1683
  }
1684
+ #endif
1071
1685
 
1072
- static void
1073
- eval_decoder_opt_without_meta(jpeg_decode_t* ptr, VALUE opt)
1686
+ static VALUE
1687
+ eval_decoder_without_meta_opt(jpeg_decode_t* ptr, VALUE opt)
1074
1688
  {
1075
- if (opt != Qundef) {
1076
- ptr->need_meta = RTEST(opt);
1689
+ if (opt != Qundef && RTEST(opt)) {
1690
+ CLR_FLAG(ptr, F_NEED_META);
1691
+ } else {
1692
+ SET_FLAG(ptr, F_NEED_META);
1077
1693
  }
1694
+
1695
+ return Qnil;
1078
1696
  }
1079
1697
 
1080
- static void
1081
- eval_decoder_opt_expand_colormap(jpeg_decode_t* ptr, VALUE opt)
1698
+ static VALUE
1699
+ eval_decoder_expand_colormap_opt(jpeg_decode_t* ptr, VALUE opt)
1082
1700
  {
1083
- if (opt != Qundef) {
1084
- ptr->expand_colormap = RTEST(opt);
1701
+ if (opt != Qundef && RTEST(opt)) {
1702
+ SET_FLAG(ptr, F_EXPAND_COLORMAP);
1703
+ } else {
1704
+ CLR_FLAG(ptr, F_EXPAND_COLORMAP);
1085
1705
  }
1706
+
1707
+ return Qnil;
1086
1708
  }
1087
1709
 
1088
- static void
1089
- eval_decoder_opt_scale(jpeg_decode_t* ptr, VALUE opt)
1710
+
1711
+ static VALUE
1712
+ eval_decoder_scale_opt(jpeg_decode_t* ptr, VALUE opt)
1090
1713
  {
1714
+ VALUE ret;
1715
+ int scale_num;
1716
+ int scale_denom;
1717
+
1718
+ ret = Qnil;
1719
+
1091
1720
  switch (TYPE(opt)) {
1092
1721
  case T_UNDEF:
1093
- // Nothing
1722
+ scale_num = 1;
1723
+ scale_denom = 1;
1724
+ break;
1725
+
1726
+ case T_FIXNUM:
1727
+ if (FIX2LONG(opt) <= 0) {
1728
+ ret = create_range_error(":scale less equal 0");
1729
+
1730
+ } else {
1731
+ scale_num = FIX2INT(opt) * 1000;
1732
+ scale_denom = 1000;
1733
+ }
1094
1734
  break;
1095
1735
 
1096
1736
  case T_FLOAT:
1097
- ptr->scale_num = 1000;
1098
- ptr->scale_denom = (int)(NUM2DBL(opt) * 1000.0);
1737
+ if (isnan(NUM2DBL(opt)) || isinf(NUM2DBL(opt))) {
1738
+ ret = create_argument_error("unsupportd :quality option value");
1739
+
1740
+ } else if (NUM2DBL(opt) <= 0.0) {
1741
+ ret = create_range_error(":scale less equal 0");
1742
+
1743
+ } else {
1744
+ scale_num = (int)(NUM2DBL(opt) * 1000.0);
1745
+ scale_denom = 1000;
1746
+ }
1099
1747
  break;
1100
1748
 
1101
1749
  case T_RATIONAL:
1102
- ptr->scale_num = FIX2INT(rb_rational_num(opt));
1103
- ptr->scale_denom = FIX2INT(rb_rational_den(opt));
1750
+ scale_num = (int)FIX2LONG(rb_rational_num(opt));
1751
+ scale_denom = (int)FIX2LONG(rb_rational_den(opt));
1104
1752
  break;
1105
1753
 
1106
1754
  default:
1107
- ARGUMENT_ERROR("Unsupportd :exapnd_colormap option value.");
1755
+ ret = create_type_error("unsupportd :scale option type");
1108
1756
  break;
1109
1757
  }
1758
+
1759
+ if (!RTEST(ret)) {
1760
+ ptr->scale_num = scale_num;
1761
+ ptr->scale_denom = scale_denom;
1762
+ }
1763
+
1764
+ return ret;
1110
1765
  }
1111
1766
 
1112
- static void
1113
- eval_decoder_opt_dct_method(jpeg_decode_t* ptr, VALUE opt)
1767
+ static VALUE
1768
+ eval_decoder_dct_method_opt(jpeg_decode_t* ptr, VALUE opt)
1114
1769
  {
1115
- if (opt != Qundef) {
1116
- if (EQ_STR(opt, "ISLOW")) {
1117
- ptr->dct_method = JDCT_ISLOW;
1770
+ VALUE ret;
1771
+ int dct_method;
1772
+
1773
+ ret = Qnil;
1774
+
1775
+ switch (TYPE(opt)) {
1776
+ case T_UNDEF:
1777
+ dct_method = JDCT_FASTEST;
1778
+ break;
1779
+
1780
+ case T_STRING:
1781
+ case T_SYMBOL:
1782
+ if (EQ_STR(opt, "FASTEST")) {
1783
+ dct_method = JDCT_FASTEST;
1784
+
1785
+ } else if (EQ_STR(opt, "ISLOW")) {
1786
+ dct_method = JDCT_ISLOW;
1118
1787
 
1119
1788
  } else if (EQ_STR(opt, "IFAST")) {
1120
- ptr->dct_method = JDCT_IFAST;
1789
+ dct_method = JDCT_IFAST;
1121
1790
 
1122
1791
  } else if (EQ_STR(opt, "FLOAT")) {
1123
- ptr->dct_method = JDCT_FLOAT;
1124
-
1125
- } else if (EQ_STR(opt, "FASTEST")) {
1126
- ptr->dct_method = JDCT_FASTEST;
1792
+ dct_method = JDCT_FLOAT;
1127
1793
 
1128
1794
  } else {
1129
- ARGUMENT_ERROR("Unsupportd :dct_method option value.");
1795
+ ret = create_argument_error("unsupportd :dct_method option value");
1130
1796
  }
1797
+ break;
1798
+
1799
+ default:
1800
+ ret = create_type_error("unsupportd :dct_method option type");
1801
+ break;
1131
1802
  }
1803
+
1804
+ if (!RTEST(ret)) ptr->dct_method = dct_method;
1805
+
1806
+ return ret;
1132
1807
  }
1133
1808
 
1134
- static void
1135
- eval_decoder_opt_with_exif(jpeg_decode_t* ptr, VALUE opt)
1809
+ static VALUE
1810
+ eval_decoder_with_exif_tags_opt(jpeg_decode_t* ptr, VALUE opt)
1811
+ {
1812
+ if (opt != Qundef && RTEST(opt)) {
1813
+ SET_FLAG(ptr, F_PARSE_EXIF);
1814
+ } else {
1815
+ CLR_FLAG(ptr, F_PARSE_EXIF);
1816
+ }
1817
+
1818
+ return Qnil;
1819
+ }
1820
+
1821
+ static VALUE
1822
+ eval_decoder_orientation_opt(jpeg_decode_t* ptr, VALUE opt)
1136
1823
  {
1137
- if (opt != Qundef) {
1138
- ptr->parse_exif = RTEST(opt);
1824
+ if (opt != Qundef && RTEST(opt)) {
1825
+ SET_FLAG(ptr, F_APPLY_ORIENTATION);
1826
+ } else {
1827
+ CLR_FLAG(ptr, F_APPLY_ORIENTATION);
1139
1828
  }
1829
+
1830
+ return Qnil;
1140
1831
  }
1141
1832
 
1142
- static void
1833
+ static VALUE
1143
1834
  set_decoder_context( jpeg_decode_t* ptr, VALUE opt)
1144
1835
  {
1836
+ VALUE ret;
1145
1837
  VALUE opts[N(decoder_opts_ids)];
1838
+ JSAMPARRAY ary;
1839
+
1840
+ /*
1841
+ * initialize
1842
+ */
1843
+ ret = Qnil;
1844
+ ary = NULL;
1146
1845
 
1147
1846
  /*
1148
1847
  * parse options
1149
1848
  */
1150
- rb_get_kwargs(opt, decoder_opts_ids, 0, N(decoder_opts_ids), opts);
1849
+ do {
1850
+ rb_get_kwargs(opt, decoder_opts_ids, 0, N(decoder_opts_ids), opts);
1851
+
1852
+ /*
1853
+ * set context
1854
+ */
1855
+ ret = eval_decoder_pixel_format_opt(ptr, opts[0]);
1856
+ if (RTEST(ret)) break;
1857
+
1858
+ ret = eval_decoder_output_gamma_opt(ptr, opts[1]);
1859
+ if (RTEST(ret)) break;
1860
+
1861
+ ret = eval_decoder_do_fancy_upsampling_opt(ptr, opts[2]);
1862
+ if (RTEST(ret)) break;
1863
+
1864
+ ret = eval_decoder_do_smoothing_opt(ptr, opts[3]);
1865
+ if (RTEST(ret)) break;
1866
+
1867
+ ret = eval_decoder_dither_opt(ptr, opts[4]);
1868
+ if (RTEST(ret)) break;
1869
+
1870
+ #if 0
1871
+ ret = eval_decoder_use_1pass_quantizer_opt(ptr, opts[5]);
1872
+ if (RTEST(ret)) break;
1873
+
1874
+ ret = eval_decoder_use_external_colormap_opt(ptr, opts[6]);
1875
+ if (RTEST(ret)) break;
1876
+
1877
+ ret = eval_decoder_use_2pass_quantizer_opt(ptr, opts[7]);
1878
+ if (RTEST(ret)) break;
1879
+ #endif
1880
+
1881
+ ret = eval_decoder_without_meta_opt(ptr, opts[5]);
1882
+ if (RTEST(ret)) break;
1883
+
1884
+ ret = eval_decoder_expand_colormap_opt(ptr, opts[6]);
1885
+ if (RTEST(ret)) break;
1886
+
1887
+ ret = eval_decoder_scale_opt(ptr, opts[7]);
1888
+ if (RTEST(ret)) break;
1889
+
1890
+ ret = eval_decoder_dct_method_opt(ptr, opts[8]);
1891
+ if (RTEST(ret)) break;
1892
+
1893
+ ret = eval_decoder_with_exif_tags_opt(ptr, opts[9]);
1894
+ if (RTEST(ret)) break;
1895
+
1896
+ ret = eval_decoder_orientation_opt(ptr, opts[10]);
1897
+ if (RTEST(ret)) break;
1898
+ } while (0);
1899
+
1900
+ /*
1901
+ * alloc memory
1902
+ */
1903
+ if (!RTEST(ret)) {
1904
+ ary = ALLOC_ARRAY();
1905
+ if (ary == NULL) ret = create_memory_error();
1906
+ }
1151
1907
 
1152
1908
  /*
1153
- * set context
1909
+ * set the rest context parameter
1910
+ */
1911
+ if (!RTEST(ret)) {
1912
+ ptr->err_mgr.jerr.output_message = output_message;
1913
+ ptr->err_mgr.jerr.emit_message = emit_message;
1914
+ ptr->err_mgr.jerr.error_exit = error_exit;
1915
+
1916
+ // 現時点でオプションでの対応をおこなっていないので
1917
+ // ここで値を設定
1918
+ ptr->enable_1pass_quant = FALSE;
1919
+ ptr->enable_external_quant = FALSE;
1920
+ ptr->enable_2pass_quant = FALSE;
1921
+ ptr->buffered_image = FALSE;
1922
+
1923
+ #if 0
1924
+ if (ptr->enable_1pass_quant == TRUE ||
1925
+ ptr->enable_external_quant == TRUE ||
1926
+ ptr->enable_2pass_quant == TRUE) {
1927
+ ptr->buffered_image = TRUE;
1928
+
1929
+ } else {
1930
+ ptr->buffered_image = FALSE;
1931
+ }
1932
+ #endif
1933
+
1934
+ ptr->array = ary;
1935
+ ptr->data = Qnil;
1936
+ ptr->orientation.value = 0;
1937
+ ptr->orientation.buf = Qnil;
1938
+ }
1939
+
1940
+ /*
1941
+ * setup libjpeg
1154
1942
  */
1155
- eval_decoder_opt_pixel_format(ptr, opts[0]);
1156
- eval_decoder_opt_output_gamma(ptr, opts[1]);
1157
- eval_decoder_opt_do_fancy_upsampling(ptr, opts[2]);
1158
- eval_decoder_opt_do_smoothing(ptr, opts[3]);
1159
- eval_decoder_opt_dither(ptr, opts[4]);
1160
- eval_decoder_opt_use_1pass_quantizer(ptr, opts[5]);
1161
- eval_decoder_opt_use_external_colormap(ptr, opts[6]);
1162
- eval_decoder_opt_use_2pass_quantizer(ptr, opts[7]);
1163
- eval_decoder_opt_without_meta(ptr, opts[8]);
1164
- eval_decoder_opt_expand_colormap(ptr, opts[9]);
1165
- eval_decoder_opt_scale(ptr, opts[10]);
1166
- eval_decoder_opt_dct_method(ptr, opts[11]);
1167
- eval_decoder_opt_with_exif(ptr, opts[12]);
1943
+ if (!RTEST(ret)) {
1944
+ jpeg_create_decompress(&ptr->cinfo);
1945
+ SET_FLAG(ptr, F_CREAT);
1946
+
1947
+ ptr->cinfo.err = jpeg_std_error(&ptr->err_mgr.jerr);
1948
+ }
1949
+
1950
+ return ret;
1168
1951
  }
1169
1952
 
1953
+ /**
1954
+ * initialize decoder object
1955
+ *
1956
+ * @overload initialize(opts)
1957
+ *
1958
+ * @param opts [Hash] options to initialize object
1959
+ *
1960
+ * @option opts [Symbol] :pixel_format
1961
+ * specifies the format of the output image. possible values are:
1962
+ * YUV422 YUYV RGB565 RGB RGB24 BGR BGR24 YUV444 YCbCr
1963
+ * RGBX RGB32 BGRX BGR32 GRAYSCALE
1964
+ *
1965
+ * @option opts [Float] :output_gamma
1966
+ *
1967
+ * @option opts [Boolean] :fancy_upsampling
1968
+ *
1969
+ * @option opts [Boolean] :do_smoothing
1970
+ *
1971
+ * @option opts [Array] :dither
1972
+ * specifies dithering parameters. A 3-elements array.
1973
+ * specify the dither type as a string for the 1st element,
1974
+ * whether to use 2-pass quantize for the 2nd element as a boolean,
1975
+ * and the number of colors used for the 3rd element as an integer
1976
+ * from 16 to 256.
1977
+ *
1978
+ * @option opts [Boolean] :without_meta
1979
+ * specifies whether or not to include meta information in the
1980
+ * output data. If true, no meta-information is output.
1981
+ *
1982
+ * @option opts [Boolean] :expand_colormap
1983
+ * specifies whether to expand the color map. If dither is specified,
1984
+ * the output will be a color number of 1 byte per pixel, but if this
1985
+ * option is set to true, the output will be expanded to color information.
1986
+ *
1987
+ * @option opts [Ratioanl] :scale
1988
+ *
1989
+ * @option opts [Symbol] :dct_method
1990
+ * specifies how decoding is handled. possible values are:
1991
+ * FASTEST ISLOW IFAST FLOAT
1992
+ *
1993
+ * @option opts [Boolean] :with_exif_tags
1994
+ * specifies whether to include Exif tag information in the output data.
1995
+ * set this option to true to parse the Exif tag information and include
1996
+ * it in the meta information output.
1997
+ *
1998
+ * @option opts [Boolean] :with_exif
1999
+ * alias to :with_exif_tags option.
2000
+ */
1170
2001
  static VALUE
1171
2002
  rb_decoder_initialize( int argc, VALUE *argv, VALUE self)
1172
2003
  {
1173
2004
  jpeg_decode_t* ptr;
2005
+ VALUE exc;
1174
2006
  VALUE opt;
1175
2007
 
1176
2008
  /*
1177
2009
  * initialize
1178
2010
  */
1179
- Data_Get_Struct(self, jpeg_decode_t, ptr);
2011
+ TypedData_Get_Struct(self, jpeg_decode_t, &jpeg_decoder_data_type, ptr);
1180
2012
 
1181
2013
  /*
1182
2014
  * parse arguments
1183
2015
  */
1184
- rb_scan_args( argc, argv, "01", &opt);
1185
-
1186
- if (opt != Qnil) Check_Type(opt, T_HASH);
2016
+ rb_scan_args( argc, argv, "0:", &opt);
1187
2017
 
1188
2018
  /*
1189
2019
  * set context
1190
2020
  */
1191
- set_decoder_context(ptr, opt);
2021
+ exc = set_decoder_context(ptr, opt);
2022
+
2023
+ /*
2024
+ * post process
2025
+ */
2026
+ if (RTEST(exc)) rb_exc_raise(exc);
1192
2027
 
1193
2028
  return Qtrue;
1194
2029
  }
@@ -1197,11 +2032,12 @@ static VALUE
1197
2032
  rb_decoder_set(VALUE self, VALUE opt)
1198
2033
  {
1199
2034
  jpeg_decode_t* ptr;
2035
+ VALUE exc;
1200
2036
 
1201
2037
  /*
1202
2038
  * initialize
1203
2039
  */
1204
- Data_Get_Struct(self, jpeg_decode_t, ptr);
2040
+ TypedData_Get_Struct(self, jpeg_decode_t, &jpeg_decoder_data_type, ptr);
1205
2041
 
1206
2042
  /*
1207
2043
  * check argument
@@ -1211,13 +2047,16 @@ rb_decoder_set(VALUE self, VALUE opt)
1211
2047
  /*
1212
2048
  * set context
1213
2049
  */
1214
- set_decoder_context(ptr, opt);
2050
+ exc = set_decoder_context(ptr, opt);
2051
+
2052
+ /*
2053
+ * post process
2054
+ */
2055
+ if (RTEST(exc)) rb_exc_raise(exc);
1215
2056
 
1216
2057
  return Qtrue;
1217
2058
  }
1218
2059
 
1219
-
1220
-
1221
2060
  static VALUE
1222
2061
  get_colorspace_str( J_COLOR_SPACE cs)
1223
2062
  {
@@ -1396,7 +2235,7 @@ exif_init(exif_t* ptr, uint8_t* src, size_t size)
1396
2235
  uint32_t off;
1397
2236
 
1398
2237
  /*
1399
- * Check Exif idntifier
2238
+ * Check Exif identifier
1400
2239
  */
1401
2240
  if (memcmp(src, "Exif\0\0", 6)) {
1402
2241
  rb_raise(decerr_klass, "invalid exif identifier");
@@ -1847,7 +2686,7 @@ exif_read(exif_t* ptr, VALUE dst)
1847
2686
  #define THUMBNAIL_SIZE ID2SYM(rb_intern("jpeg_interchange_format_length"))
1848
2687
 
1849
2688
  static VALUE
1850
- create_exif_hash(jpeg_decode_t* ptr)
2689
+ create_exif_tags_hash(jpeg_decode_t* ptr)
1851
2690
  {
1852
2691
  VALUE ret;
1853
2692
  jpeg_saved_marker_ptr marker;
@@ -1895,99 +2734,291 @@ create_exif_hash(jpeg_decode_t* ptr)
1895
2734
  return ret;
1896
2735
  }
1897
2736
 
1898
- static VALUE
1899
- create_meta(jpeg_decode_t* ptr)
2737
+ static void
2738
+ pick_exif_orientation(jpeg_decode_t* ptr)
1900
2739
  {
1901
- VALUE ret;
1902
- struct jpeg_decompress_struct* cinfo;
2740
+ jpeg_saved_marker_ptr marker;
2741
+ int o9n;
2742
+ uint8_t* p;
2743
+ int be;
2744
+ uint32_t off;
2745
+ int i;
2746
+ int n;
1903
2747
 
1904
- /* TODO: そのうちディザをかけた場合のカラーマップをメタで返すようにする */
2748
+ o9n = 0;
1905
2749
 
1906
- ret = rb_obj_alloc(meta_klass);
1907
- cinfo = &ptr->cinfo;
2750
+ for (marker = ptr->cinfo.marker_list;
2751
+ marker != NULL; marker = marker->next) {
1908
2752
 
1909
- rb_ivar_set(ret, id_width, INT2FIX(cinfo->output_width));
1910
- rb_ivar_set(ret, id_height, INT2FIX(cinfo->output_height));
1911
- rb_ivar_set(ret, id_orig_cs, get_colorspace_str(cinfo->jpeg_color_space));
2753
+ if (marker->data_length < 14) continue;
1912
2754
 
1913
- if (ptr->format == FMT_YVU) {
1914
- rb_ivar_set(ret, id_out_cs, rb_str_new_cstr("YCrCb"));
1915
- } else {
1916
- rb_ivar_set(ret, id_out_cs, get_colorspace_str(cinfo->out_color_space));
1917
- }
2755
+ p = marker->data;
1918
2756
 
1919
- rb_ivar_set(ret, id_ncompo, INT2FIX(cinfo->output_components));
2757
+ /*
2758
+ * check Exif identifier
2759
+ */
2760
+ if (memcmp(p, "Exif\0\0", 6)) continue;
1920
2761
 
1921
- if (ptr->parse_exif) {
1922
- rb_ivar_set(ret, id_exif, create_exif_hash(ptr));
1923
- }
2762
+ /*
2763
+ * check endian marker
2764
+ */
2765
+ if (!memcmp(p + 6, "MM", 2)) {
2766
+ be = !0;
1924
2767
 
1925
- return ret;
1926
- }
2768
+ } else if (!memcmp(p + 6, "II", 2)) {
2769
+ be = 0;
1927
2770
 
1928
- static VALUE
1929
- do_read_header(jpeg_decode_t* ptr, uint8_t* jpg, size_t jpg_sz)
1930
- {
1931
- VALUE ret;
2771
+ } else {
2772
+ continue;
2773
+ }
1932
2774
 
1933
- switch (ptr->format) {
1934
- case FMT_YUV422:
1935
- case FMT_RGB565:
1936
- RUNTIME_ERROR( "Not implement");
2775
+ /*
2776
+ * check TIFF identifier
2777
+ */
2778
+ if (get_u16(p + 8, be) != 0x002a) continue;
2779
+
2780
+ /*
2781
+ * set 0th IFD address
2782
+ */
2783
+ off = get_u32(p + 10, be);
2784
+ if (off < 8 || off >= marker->data_length - 6) continue;
2785
+
2786
+ p += (6 + off);
2787
+
2788
+ /* ここまでくればAPP1がExifタグなので
2789
+ * 0th IFDをなめてOrientationタグを探す */
2790
+
2791
+ n = get_u16(p, be);
2792
+ p += 2;
2793
+
2794
+ for (i = 0; i < n; i++) {
2795
+ int tag;
2796
+ int type;
2797
+ int num;
2798
+
2799
+ tag = get_u16(p + 0, be);
2800
+ type = get_u16(p + 2, be);
2801
+ num = get_u32(p + 4, be);
2802
+
2803
+ if (tag == 0x0112) {
2804
+ if (type == 3 && num == 1) {
2805
+ o9n = get_u16(p + 8, be);
2806
+ goto loop_out;
2807
+
2808
+ } else {
2809
+ fprintf(stderr,
2810
+ "Illeagal orientation tag found [type:%d, num:%d]\n",
2811
+ type,
2812
+ num);
2813
+ }
2814
+ }
2815
+
2816
+ p += 12;
2817
+ }
2818
+ }
2819
+ loop_out:
2820
+
2821
+ ptr->orientation.value = (o9n >= 1 && o9n <= 8)? (o9n - 1): 0;
2822
+ }
2823
+
2824
+ static VALUE
2825
+ create_colormap(jpeg_decode_t* ptr)
2826
+ {
2827
+ VALUE ret;
2828
+ struct jpeg_decompress_struct* cinfo;
2829
+ JSAMPARRAY map;
2830
+ int i; // volatileを外すとaarch64のgcc6でクラッシュする場合がある
2831
+ uint32_t c;
2832
+
2833
+ cinfo = &ptr->cinfo;
2834
+ ret = rb_ary_new_capa(cinfo->actual_number_of_colors);
2835
+ map = cinfo->colormap;
2836
+
2837
+ switch (cinfo->out_color_components) {
2838
+ case 1:
2839
+ for (i = 0; i < cinfo->actual_number_of_colors; i++) {
2840
+ c = map[0][i];
2841
+ rb_ary_push(ret, INT2FIX(c));
2842
+ }
2843
+ break;
2844
+
2845
+ case 2:
2846
+ for (i = 0; i < cinfo->actual_number_of_colors; i++) {
2847
+ c = (map[0][i] << 8) | (map[1][i] << 0);
2848
+ rb_ary_push(ret, INT2FIX(c));
2849
+ }
2850
+ break;
2851
+
2852
+ case 3:
2853
+ for (i = 0; i < cinfo->actual_number_of_colors; i++) {
2854
+ c = (map[0][i] << 16) | (map[1][i] << 8) | (map[2][i] << 0);
2855
+
2856
+ rb_ary_push(ret, INT2FIX(c));
2857
+ }
1937
2858
  break;
2859
+
2860
+ default:
2861
+ RUNTIME_ERROR("this number of components is not implemented yet");
1938
2862
  }
1939
2863
 
1940
- jpeg_create_decompress(&ptr->cinfo);
2864
+ return ret;
2865
+ }
1941
2866
 
1942
- ptr->cinfo.err = jpeg_std_error(&ptr->err_mgr.jerr);
1943
- ptr->err_mgr.jerr.output_message = decode_output_message;
1944
- ptr->err_mgr.jerr.emit_message = decode_emit_message;
1945
- ptr->err_mgr.jerr.error_exit = decode_error_exit;
2867
+ static VALUE
2868
+ rb_meta_exif_tags(VALUE self)
2869
+ {
2870
+ return rb_ivar_get(self, id_exif_tags);
2871
+ }
1946
2872
 
1947
- ptr->cinfo.raw_data_out = FALSE;
1948
- ptr->cinfo.dct_method = JDCT_FLOAT;
2873
+ static VALUE
2874
+ create_meta(jpeg_decode_t* ptr)
2875
+ {
2876
+ VALUE ret;
2877
+ struct jpeg_decompress_struct* cinfo;
2878
+ int width;
2879
+ int height;
2880
+ int stride;
2881
+
2882
+ ret = rb_obj_alloc(meta_klass);
2883
+ cinfo = &ptr->cinfo;
2884
+
2885
+ if (TEST_FLAG(ptr, F_APPLY_ORIENTATION) && (ptr->orientation.value & 4)) {
2886
+ width = cinfo->output_height;
2887
+ height = cinfo->output_width;
2888
+ } else {
2889
+ width = cinfo->output_width;
2890
+ height = cinfo->output_height;
2891
+ }
2892
+
2893
+ stride = cinfo->output_width * cinfo->output_components;
2894
+
2895
+ rb_ivar_set(ret, id_width, INT2FIX(width));
2896
+ rb_ivar_set(ret, id_stride, INT2FIX(stride));
2897
+ rb_ivar_set(ret, id_height, INT2FIX(height));
2898
+
2899
+ rb_ivar_set(ret, id_orig_cs, get_colorspace_str(cinfo->jpeg_color_space));
2900
+
2901
+ if (ptr->format == FMT_YVU) {
2902
+ rb_ivar_set(ret, id_out_cs, rb_str_new_cstr("YCrCb"));
2903
+ } else {
2904
+ rb_ivar_set(ret, id_out_cs, get_colorspace_str(cinfo->out_color_space));
2905
+ }
2906
+
2907
+ if (TEST_FLAG_ALL(ptr, F_DITHER | F_EXPAND_COLORMAP)) {
2908
+ rb_ivar_set(ret, id_ncompo, INT2FIX(cinfo->out_color_components));
2909
+ } else {
2910
+ rb_ivar_set(ret, id_ncompo, INT2FIX(cinfo->output_components));
2911
+ }
2912
+
2913
+ if (TEST_FLAG(ptr, F_PARSE_EXIF)) {
2914
+ rb_ivar_set(ret, id_exif_tags, create_exif_tags_hash(ptr));
2915
+ rb_define_singleton_method(ret, "exif_tags", rb_meta_exif_tags, 0);
2916
+ rb_define_singleton_method(ret, "exif", rb_meta_exif_tags, 0);
2917
+ }
2918
+
2919
+ if (TEST_FLAG(ptr, F_DITHER)) {
2920
+ rb_ivar_set(ret, id_colormap, create_colormap(ptr));
2921
+ }
2922
+
2923
+ return ret;
2924
+ }
2925
+
2926
+ static VALUE
2927
+ do_read_header(VALUE _ptr)
2928
+ {
2929
+ VALUE ret;
2930
+ jpeg_decode_t* ptr;
2931
+ uint8_t* data;
2932
+ size_t size;
2933
+
2934
+ /*
2935
+ * initialize
2936
+ */
2937
+ ret = Qnil;
2938
+ ptr = (jpeg_decode_t*)_ptr;
2939
+ data = (uint8_t*)RSTRING_PTR(ptr->data);
2940
+ size = RSTRING_LEN(ptr->data);
2941
+
2942
+ /*
2943
+ * process body
2944
+ */
2945
+ ptr->cinfo.raw_data_out = FALSE;
2946
+ ptr->cinfo.dct_method = JDCT_FLOAT;
1949
2947
 
1950
2948
  if (setjmp(ptr->err_mgr.jmpbuf)) {
1951
- jpeg_destroy_decompress(&ptr->cinfo);
2949
+ /*
2950
+ * when error occurred
2951
+ */
1952
2952
  rb_raise(decerr_klass, "%s", ptr->err_mgr.msg);
2953
+
1953
2954
  } else {
1954
- jpeg_mem_src(&ptr->cinfo, jpg, jpg_sz);
2955
+ /*
2956
+ * normal path
2957
+ */
2958
+ jpeg_mem_src(&ptr->cinfo, data, size);
1955
2959
 
1956
- if (ptr->parse_exif) {
2960
+ if (TEST_FLAG(ptr, F_PARSE_EXIF | F_APPLY_ORIENTATION)) {
1957
2961
  jpeg_save_markers(&ptr->cinfo, JPEG_APP1, 0xFFFF);
1958
2962
  }
1959
2963
 
1960
2964
  jpeg_read_header(&ptr->cinfo, TRUE);
1961
2965
  jpeg_calc_output_dimensions(&ptr->cinfo);
1962
2966
 
1963
- ret = create_meta(ptr);
2967
+ if (TEST_FLAG(ptr, F_APPLY_ORIENTATION)) {
2968
+ pick_exif_orientation(ptr);
2969
+ }
1964
2970
 
1965
- jpeg_destroy_decompress(&ptr->cinfo);
2971
+ ret = create_meta(ptr);
1966
2972
  }
1967
2973
 
1968
2974
  return ret;
1969
2975
  }
1970
2976
 
2977
+ /**
2978
+ * read meta data
2979
+ *
2980
+ * @overload read_header(jpeg)
2981
+ *
2982
+ * @param jpeg [String] input data.
2983
+ *
2984
+ * @return [JPEG::Meta] metadata.
2985
+ */
1971
2986
  static VALUE
1972
2987
  rb_decoder_read_header(VALUE self, VALUE data)
1973
2988
  {
1974
2989
  VALUE ret;
1975
2990
  jpeg_decode_t* ptr;
2991
+ int state;
1976
2992
 
1977
2993
  /*
1978
2994
  * initialize
1979
2995
  */
1980
- Data_Get_Struct(self, jpeg_decode_t, ptr);
2996
+ ret = Qnil;
2997
+ state = 0;
2998
+
2999
+ TypedData_Get_Struct(self, jpeg_decode_t, &jpeg_decoder_data_type, ptr);
1981
3000
 
1982
3001
  /*
1983
3002
  * argument check
1984
3003
  */
1985
3004
  Check_Type(data, T_STRING);
1986
3005
 
3006
+ /*
3007
+ * prepare
3008
+ */
3009
+ SET_DATA(ptr, data);
3010
+
1987
3011
  /*
1988
3012
  * do encode
1989
3013
  */
1990
- ret = do_read_header(ptr, (uint8_t*)RSTRING_PTR(data), RSTRING_LEN(data));
3014
+ ret = rb_protect(do_read_header, (VALUE)ptr, &state);
3015
+
3016
+ /*
3017
+ * post process
3018
+ */
3019
+ CLR_DATA(ptr);
3020
+
3021
+ if (state != 0) rb_jump_tag(state);
1991
3022
 
1992
3023
  return ret;
1993
3024
  }
@@ -2018,7 +3049,7 @@ expand_colormap(struct jpeg_decompress_struct* cinfo, uint8_t* src)
2018
3049
  */
2019
3050
 
2020
3051
  VALUE ret;
2021
- int i;
3052
+ volatile int i; // volatileを外すとaarch64のgcc6でクラッシュする場合がある
2022
3053
  int n;
2023
3054
  uint8_t* dst;
2024
3055
  JSAMPARRAY map;
@@ -2035,6 +3066,15 @@ expand_colormap(struct jpeg_decompress_struct* cinfo, uint8_t* src)
2035
3066
  }
2036
3067
  break;
2037
3068
 
3069
+ case 2:
3070
+ for (i = 0; i < n; i++) {
3071
+ dst[0] = map[0][src[i]];
3072
+ dst[1] = map[1][src[i]];
3073
+
3074
+ dst += 2;
3075
+ }
3076
+ break;
3077
+
2038
3078
  case 3:
2039
3079
  for (i = 0; i < n; i++) {
2040
3080
  dst[0] = map[0][src[i]];
@@ -2046,7 +3086,7 @@ expand_colormap(struct jpeg_decompress_struct* cinfo, uint8_t* src)
2046
3086
  break;
2047
3087
 
2048
3088
  default:
2049
- RUNTIME_ERROR("Really?");
3089
+ RUNTIME_ERROR("this number of components is not implemented yet");
2050
3090
  }
2051
3091
 
2052
3092
  rb_str_set_len(ret, n * cinfo->out_color_components);
@@ -2067,10 +3107,417 @@ swap_cbcr(uint8_t* p, size_t size)
2067
3107
  }
2068
3108
  }
2069
3109
 
3110
+ static void
3111
+ do_transpose8(uint8_t* img, int wd, int ht, void* dst)
3112
+ {
3113
+ int x;
3114
+ int y;
3115
+
3116
+ uint8_t* sp;
3117
+ uint8_t* dp;
3118
+
3119
+ sp = (uint8_t*)img;
3120
+
3121
+ for (y = 0; y < ht; y++) {
3122
+ dp = (uint8_t*)dst + y;
3123
+
3124
+ for (x = 0; x < wd; x++) {
3125
+ *dp = *sp;
3126
+
3127
+ sp++;
3128
+ dp += ht;
3129
+ }
3130
+ }
3131
+ }
3132
+
3133
+ static void
3134
+ do_transpose16(void* img, int wd, int ht, void* dst)
3135
+ {
3136
+ int x;
3137
+ int y;
3138
+
3139
+ uint16_t* sp;
3140
+ uint16_t* dp;
3141
+
3142
+ sp = (uint16_t*)img;
3143
+
3144
+ for (y = 0; y < ht; y++) {
3145
+ dp = (uint16_t*)dst + y;
3146
+
3147
+ for (x = 0; x < wd; x++) {
3148
+ *dp = *sp;
3149
+
3150
+ sp++;
3151
+ dp += ht;
3152
+ }
3153
+ }
3154
+ }
3155
+
3156
+ static void
3157
+ do_transpose24(void* img, int wd, int ht, void* dst)
3158
+ {
3159
+ int x;
3160
+ int y;
3161
+ int st;
3162
+
3163
+ uint8_t* sp;
3164
+ uint8_t* dp;
3165
+
3166
+ sp = (uint8_t*)img;
3167
+ st = ht * 3;
3168
+
3169
+ for (y = 0; y < ht; y++) {
3170
+ dp = (uint8_t*)dst + (y * 3);
3171
+
3172
+ for (x = 0; x < wd; x++) {
3173
+ dp[0] = sp[0];
3174
+ dp[1] = sp[1];
3175
+ dp[2] = sp[2];
3176
+
3177
+ sp += 3;
3178
+ dp += st;
3179
+ }
3180
+ }
3181
+ }
3182
+
3183
+ static void
3184
+ do_transpose32(void* img, int wd, int ht, void* dst)
3185
+ {
3186
+ int x;
3187
+ int y;
3188
+
3189
+ uint32_t* sp;
3190
+ uint32_t* dp;
3191
+
3192
+ sp = (uint32_t*)img;
3193
+
3194
+ for (y = 0; y < ht; y++) {
3195
+ dp = (uint32_t*)dst + y;
3196
+
3197
+ for (x = 0; x < wd; x++) {
3198
+ *dp = *sp;
3199
+
3200
+ sp++;
3201
+ dp += ht;
3202
+ }
3203
+ }
3204
+ }
3205
+
3206
+ static void
3207
+ do_transpose(void* img, int wd, int ht, int nc, void* dst)
3208
+ {
3209
+ switch (nc) {
3210
+ case 1:
3211
+ do_transpose8(img, wd, ht, dst);
3212
+ break;
3213
+
3214
+ case 2:
3215
+ do_transpose16(img, wd, ht, dst);
3216
+ break;
3217
+
3218
+ case 3:
3219
+ do_transpose24(img, wd, ht, dst);
3220
+ break;
3221
+
3222
+ case 4:
3223
+ do_transpose32(img, wd, ht, dst);
3224
+ break;
3225
+ }
3226
+ }
3227
+
3228
+ static void
3229
+ do_upside_down8(void* img, int wd, int ht)
3230
+ {
3231
+ uint8_t* sp;
3232
+ uint8_t* dp;
3233
+
3234
+ sp = (uint8_t*)img;
3235
+ dp = (uint8_t*)img + ((wd * ht) - 1);
3236
+
3237
+ while (sp < dp) {
3238
+ SWAP(*sp, *dp, uint8_t);
3239
+
3240
+ sp++;
3241
+ dp--;
3242
+ }
3243
+ }
3244
+
3245
+ static void
3246
+ do_upside_down16(void* img, int wd, int ht)
3247
+ {
3248
+ uint16_t* sp;
3249
+ uint16_t* dp;
3250
+
3251
+ sp = (uint16_t*)img;
3252
+ dp = (uint16_t*)img + ((wd * ht) - 1);
3253
+
3254
+ while (sp < dp) {
3255
+ SWAP(*sp, *dp, uint8_t);
3256
+
3257
+ sp++;
3258
+ dp--;
3259
+ }
3260
+ }
3261
+
3262
+ static void
3263
+ do_upside_down24(void* img, int wd, int ht)
3264
+ {
3265
+ uint8_t* sp;
3266
+ uint8_t* dp;
3267
+
3268
+ sp = (uint8_t*)img;
3269
+ dp = (uint8_t*)img + ((wd * ht * 3) - 3);
3270
+
3271
+ while (sp < dp) {
3272
+ SWAP(sp[0], dp[0], uint8_t);
3273
+ SWAP(sp[1], dp[1], uint8_t);
3274
+ SWAP(sp[2], dp[2], uint8_t);
3275
+
3276
+ sp += 3;
3277
+ dp -= 3;
3278
+ }
3279
+ }
3280
+
3281
+ static void
3282
+ do_upside_down32(void* img, int wd, int ht)
3283
+ {
3284
+ uint32_t* sp;
3285
+ uint32_t* dp;
3286
+
3287
+ sp = (uint32_t*)img;
3288
+ dp = (uint32_t*)img + ((wd * ht) - 1);
3289
+
3290
+ ht /= 2;
3291
+
3292
+ while (sp < dp) {
3293
+ SWAP(*sp, *dp, uint32_t);
3294
+
3295
+ sp++;
3296
+ dp--;
3297
+ }
3298
+ }
3299
+
3300
+ static void
3301
+ do_upside_down(void* img, int wd, int ht, int nc)
3302
+ {
3303
+ switch (nc) {
3304
+ case 1:
3305
+ do_upside_down8(img, wd, ht);
3306
+ break;
3307
+
3308
+ case 2:
3309
+ do_upside_down16(img, wd, ht);
3310
+ break;
3311
+
3312
+ case 3:
3313
+ do_upside_down24(img, wd, ht);
3314
+ break;
3315
+
3316
+ case 4:
3317
+ do_upside_down32(img, wd, ht);
3318
+ break;
3319
+ }
3320
+ }
3321
+
3322
+ static void
3323
+ do_flip_horizon8(void* img, int wd, int ht)
3324
+ {
3325
+ int y;
3326
+ int st;
3327
+
3328
+ uint8_t* sp;
3329
+ uint8_t* dp;
3330
+
3331
+ st = wd;
3332
+ wd /= 2;
3333
+
3334
+ sp = (uint8_t*)img;
3335
+ dp = (uint8_t*)img + (st - 1);
3336
+
3337
+ for (y = 0; y < ht; y++) {
3338
+ while (sp < dp) {
3339
+ SWAP(*sp, *dp, uint8_t);
3340
+
3341
+ sp++;
3342
+ dp--;
3343
+ }
3344
+
3345
+ sp = sp - wd + st;
3346
+ dp = sp + (st - 1);
3347
+ }
3348
+ }
3349
+
3350
+ static void
3351
+ do_flip_horizon16(void* img, int wd, int ht)
3352
+ {
3353
+ int y;
3354
+ int st;
3355
+
3356
+ uint16_t* sp;
3357
+ uint16_t* dp;
3358
+
3359
+ st = wd;
3360
+ wd /= 2;
3361
+
3362
+ sp = (uint16_t*)img;
3363
+ dp = (uint16_t*)img + (st - 1);
3364
+
3365
+ for (y = 0; y < ht; y++) {
3366
+ while (sp < dp) {
3367
+ SWAP(*sp, *dp, uint16_t);
3368
+
3369
+ sp++;
3370
+ dp--;
3371
+ }
3372
+
3373
+ sp = sp - wd + st;
3374
+ dp = sp + (st - 1);
3375
+ }
3376
+ }
3377
+
3378
+ static void
3379
+ do_flip_horizon24(void* img, int wd, int ht)
3380
+ {
3381
+ int y;
3382
+ int st;
3383
+
3384
+ uint8_t* sp;
3385
+ uint8_t* dp;
3386
+
3387
+ st = wd * 3;
3388
+ wd /= 2;
3389
+
3390
+ sp = (uint8_t*)img;
3391
+ dp = (uint8_t*)img + (st - 3);
3392
+
3393
+ for (y = 0; y < ht; y++) {
3394
+ while (sp < dp) {
3395
+ SWAP(sp[0], dp[0], uint8_t);
3396
+ SWAP(sp[1], dp[1], uint8_t);
3397
+ SWAP(sp[2], dp[2], uint8_t);
3398
+
3399
+ sp += 3;
3400
+ dp -= 3;
3401
+ }
3402
+
3403
+ sp = (sp - (wd * 3)) + st;
3404
+ dp = sp + (st - 3);
3405
+ }
3406
+ }
3407
+
3408
+ static void
3409
+ do_flip_horizon32(void* img, int wd, int ht)
3410
+ {
3411
+ int y;
3412
+ int st;
3413
+
3414
+ uint32_t* sp;
3415
+ uint32_t* dp;
3416
+
3417
+ st = wd;
3418
+ wd /= 2;
3419
+
3420
+ sp = (uint32_t*)img;
3421
+ dp = (uint32_t*)img + (st - 1);
3422
+
3423
+ for (y = 0; y < ht; y++) {
3424
+ while (sp < dp) {
3425
+ SWAP(*sp, *dp, uint32_t);
3426
+
3427
+ sp++;
3428
+ dp--;
3429
+ }
3430
+
3431
+ sp = sp - wd + st;
3432
+ dp = sp + (st - 1);
3433
+ }
3434
+ }
3435
+
3436
+ static void
3437
+ do_flip_horizon(void* img, int wd, int ht, int nc)
3438
+ {
3439
+ switch (nc) {
3440
+ case 1:
3441
+ do_flip_horizon8(img, wd, ht);
3442
+ break;
3443
+
3444
+ case 2:
3445
+ do_flip_horizon16(img, wd, ht);
3446
+ break;
3447
+
3448
+ case 3:
3449
+ do_flip_horizon24(img, wd, ht);
3450
+ break;
3451
+
3452
+ case 4:
3453
+ do_flip_horizon32(img, wd, ht);
3454
+ break;
3455
+ }
3456
+ }
3457
+
3458
+ static VALUE
3459
+ shift_orientation_buffer(jpeg_decode_t* ptr, VALUE img)
3460
+ {
3461
+ VALUE ret;
3462
+ int len;
3463
+
3464
+ ret = ptr->orientation.buf;
3465
+ len = RSTRING_LEN(img);
3466
+
3467
+ if (ret == Qnil || RSTRING_LEN(ret) != len) {
3468
+ ret = rb_str_buf_new(len);
3469
+ rb_str_set_len(ret, len);
3470
+ }
3471
+
3472
+ ptr->orientation.buf = img;
3473
+
3474
+ return ret;
3475
+ }
3476
+
3477
+ static VALUE
3478
+ apply_orientation(jpeg_decode_t* ptr, VALUE img)
3479
+ {
3480
+ struct jpeg_decompress_struct* cinfo;
3481
+ int wd;
3482
+ int ht;
3483
+ int nc;
3484
+ VALUE tmp;
3485
+
3486
+ cinfo = &ptr->cinfo;
3487
+ wd = cinfo->output_width;
3488
+ ht = cinfo->output_height;
3489
+ nc = cinfo->output_components;
3490
+
3491
+ if (ptr->orientation.value & 4) {
3492
+ /* 転置は交換アルゴリズムでは実装できないので新規バッファを
3493
+ 用意する */
3494
+ tmp = img;
3495
+ img = shift_orientation_buffer(ptr, tmp);
3496
+ SWAP(wd, ht, int);
3497
+
3498
+ do_transpose(RSTRING_PTR(tmp), ht, wd, nc, RSTRING_PTR(img));
3499
+ }
3500
+
3501
+ if (ptr->orientation.value & 2) {
3502
+ do_upside_down(RSTRING_PTR(img), wd, ht, nc);
3503
+ }
3504
+
3505
+ if (ptr->orientation.value & 1) {
3506
+ do_flip_horizon(RSTRING_PTR(img), wd, ht, nc);
3507
+ }
3508
+
3509
+ return img;
3510
+ }
3511
+
2070
3512
  static VALUE
2071
- do_decode(jpeg_decode_t* ptr, uint8_t* jpg, size_t jpg_sz)
3513
+ do_decode(VALUE _ptr)
2072
3514
  {
2073
3515
  VALUE ret;
3516
+
3517
+ jpeg_decode_t* ptr;
3518
+ uint8_t* data;
3519
+ size_t size;
3520
+
2074
3521
  struct jpeg_decompress_struct* cinfo;
2075
3522
  JSAMPARRAY array;
2076
3523
 
@@ -2080,118 +3527,149 @@ do_decode(jpeg_decode_t* ptr, uint8_t* jpg, size_t jpg_sz)
2080
3527
  int i;
2081
3528
  int j;
2082
3529
 
2083
- ret = Qundef; // warning対策
3530
+ /*
3531
+ * initialize
3532
+ */
3533
+ ret = Qnil; // warning対策
3534
+ ptr = (jpeg_decode_t*)_ptr;
3535
+ data = (uint8_t*)RSTRING_PTR(ptr->data);
3536
+ size = RSTRING_LEN(ptr->data);
2084
3537
  cinfo = &ptr->cinfo;
2085
- array = ALLOC_ARRAY();
2086
-
2087
- switch (ptr->format) {
2088
- case FMT_YUV422:
2089
- case FMT_RGB565:
2090
- RUNTIME_ERROR( "Not implement");
2091
- break;
3538
+ array = ptr->array;
2092
3539
 
2093
- case FMT_GRAYSCALE:
2094
- case FMT_YUV:
2095
- case FMT_RGB:
2096
- case FMT_BGR:
2097
- case FMT_YVU:
2098
- case FMT_RGB32:
2099
- case FMT_BGR32:
2100
- jpeg_create_decompress(cinfo);
3540
+ /*
3541
+ * do decode
3542
+ */
3543
+ if (setjmp(ptr->err_mgr.jmpbuf)) {
3544
+ /*
3545
+ * when error occurred
3546
+ */
3547
+ jpeg_abort_decompress(&ptr->cinfo);
3548
+ rb_raise(decerr_klass, "%s", ptr->err_mgr.msg);
2101
3549
 
2102
- cinfo->err = jpeg_std_error(&ptr->err_mgr.jerr);
2103
- ptr->err_mgr.jerr.output_message = decode_output_message;
2104
- ptr->err_mgr.jerr.emit_message = decode_emit_message;
2105
- ptr->err_mgr.jerr.error_exit = decode_error_exit;
3550
+ } else {
3551
+ /*
3552
+ * initialize
3553
+ */
3554
+ jpeg_mem_src(cinfo, data, size);
2106
3555
 
2107
- if (setjmp(ptr->err_mgr.jmpbuf)) {
2108
- jpeg_abort_decompress(cinfo);
2109
- jpeg_destroy_decompress(&ptr->cinfo);
3556
+ if (TEST_FLAG(ptr, F_PARSE_EXIF | F_APPLY_ORIENTATION)) {
3557
+ jpeg_save_markers(&ptr->cinfo, JPEG_APP1, 0xFFFF);
3558
+ }
2110
3559
 
2111
- free(array);
2112
- rb_raise(decerr_klass, "%s", ptr->err_mgr.msg);
3560
+ jpeg_read_header(cinfo, TRUE);
3561
+ jpeg_calc_output_dimensions(cinfo);
2113
3562
 
2114
- } else {
2115
- jpeg_mem_src(cinfo, jpg, jpg_sz);
2116
- jpeg_read_header(cinfo, TRUE);
2117
-
2118
- cinfo->raw_data_out = FALSE;
2119
- cinfo->dct_method = ptr->dct_method;
2120
-
2121
- cinfo->out_color_space = ptr->out_color_space;
2122
- cinfo->out_color_components = ptr->out_color_components;
2123
- cinfo->scale_num = ptr->scale_num;
2124
- cinfo->scale_denom = ptr->scale_denom;
2125
- cinfo->output_gamma = ptr->output_gamma;
2126
- cinfo->do_fancy_upsampling = ptr->do_fancy_upsampling;
2127
- cinfo->do_block_smoothing = ptr->do_block_smoothing;
2128
- cinfo->quantize_colors = ptr->quantize_colors;
2129
- cinfo->dither_mode = ptr->dither_mode;
2130
- cinfo->two_pass_quantize = ptr->two_pass_quantize;
2131
- cinfo->desired_number_of_colors = ptr->desired_number_of_colors;
2132
- cinfo->enable_1pass_quant = ptr->enable_1pass_quant;
2133
- cinfo->enable_external_quant = ptr->enable_external_quant;
2134
- cinfo->enable_2pass_quant = ptr->enable_2pass_quant;
2135
-
2136
- jpeg_calc_output_dimensions(cinfo);
2137
- jpeg_start_decompress(cinfo);
2138
-
2139
- stride = cinfo->output_components * cinfo->output_width;
2140
- raw_sz = stride * cinfo->output_height;
2141
- ret = rb_str_buf_new(raw_sz);
2142
- raw = (uint8_t*)RSTRING_PTR(ret);
2143
-
2144
-
2145
- while (cinfo->output_scanline < cinfo->output_height) {
2146
- for (i = 0, j = cinfo->output_scanline; i < UNIT_LINES; i++, j++) {
2147
- array[i] = raw + (j * stride);
2148
- }
3563
+ /*
3564
+ * configuration
3565
+ */
3566
+ cinfo->raw_data_out = FALSE;
3567
+ cinfo->dct_method = ptr->dct_method;
3568
+
3569
+ cinfo->out_color_space = ptr->out_color_space;
3570
+ cinfo->out_color_components = ptr->out_color_components;
3571
+ cinfo->scale_num = ptr->scale_num;
3572
+ cinfo->scale_denom = ptr->scale_denom;
3573
+ cinfo->output_gamma = ptr->output_gamma;
3574
+ cinfo->do_fancy_upsampling = ptr->do_fancy_upsampling;
3575
+ cinfo->do_block_smoothing = ptr->do_block_smoothing;
3576
+ cinfo->quantize_colors = ptr->quantize_colors;
3577
+ cinfo->dither_mode = ptr->dither_mode;
3578
+ cinfo->two_pass_quantize = ptr->two_pass_quantize;
3579
+ cinfo->desired_number_of_colors = ptr->desired_number_of_colors;
3580
+ cinfo->enable_1pass_quant = ptr->enable_1pass_quant;
3581
+ cinfo->enable_external_quant = ptr->enable_external_quant;
3582
+ cinfo->enable_2pass_quant = ptr->enable_2pass_quant;
2149
3583
 
2150
- jpeg_read_scanlines(cinfo, array, UNIT_LINES);
2151
- }
3584
+ /*
3585
+ * decode process
3586
+ */
3587
+ jpeg_start_decompress(cinfo);
2152
3588
 
2153
- if (ptr->expand_colormap && IS_COLORMAPPED( cinfo)) {
2154
- ret = expand_colormap(cinfo, raw);
2155
- } else {
2156
- rb_str_set_len( ret, raw_sz);
3589
+ stride = cinfo->output_components * cinfo->output_width;
3590
+ raw_sz = stride * cinfo->output_height;
3591
+ ret = rb_str_buf_new(raw_sz);
3592
+ raw = (uint8_t*)RSTRING_PTR(ret);
3593
+
3594
+ while (cinfo->output_scanline < cinfo->output_height) {
3595
+ for (i = 0, j = cinfo->output_scanline; i < UNIT_LINES; i++, j++) {
3596
+ array[i] = raw + (j * stride);
2157
3597
  }
2158
3598
 
2159
- if (ptr->need_meta) add_meta(ret, ptr);
2160
- if (ptr->format == FMT_YVU) swap_cbcr(raw, raw_sz);
3599
+ jpeg_read_scanlines(cinfo, array, UNIT_LINES);
3600
+ }
3601
+
3602
+ jpeg_finish_decompress(&ptr->cinfo);
2161
3603
 
2162
- jpeg_finish_decompress(cinfo);
2163
- jpeg_destroy_decompress(&ptr->cinfo);
3604
+ /*
3605
+ * build return data
3606
+ */
3607
+ if (TEST_FLAG(ptr, F_EXPAND_COLORMAP) && IS_COLORMAPPED(cinfo)) {
3608
+ ret = expand_colormap(cinfo, raw);
3609
+ } else {
3610
+ rb_str_set_len(ret, raw_sz);
2164
3611
  }
2165
- break;
2166
- }
2167
3612
 
2168
- free(array);
3613
+ if (ptr->format == FMT_YVU) swap_cbcr(raw, raw_sz);
3614
+
3615
+ if (TEST_FLAG(ptr, F_APPLY_ORIENTATION)) {
3616
+ pick_exif_orientation(ptr);
3617
+ ret = apply_orientation(ptr, ret);
3618
+ }
3619
+
3620
+ if (TEST_FLAG(ptr, F_NEED_META)) add_meta(ret, ptr);
3621
+ }
2169
3622
 
2170
3623
  return ret;
2171
3624
  }
2172
3625
 
3626
+ /**
3627
+ * decode JPEG data
3628
+ *
3629
+ * @overload decode(jpeg)
3630
+ *
3631
+ * @param jpeg [String] JPEG data to decode.
3632
+ *
3633
+ * @return [String] decoded raw image data.
3634
+ */
2173
3635
  static VALUE
2174
3636
  rb_decoder_decode(VALUE self, VALUE data)
2175
3637
  {
2176
- VALUE ret;
2177
- jpeg_decode_t* ptr;
3638
+ VALUE ret;
3639
+ jpeg_decode_t* ptr;
3640
+ int state;
2178
3641
 
2179
- /*
2180
- * initialize
2181
- */
2182
- Data_Get_Struct(self, jpeg_decode_t, ptr);
3642
+ /*
3643
+ * initialize
3644
+ */
3645
+ ret = Qnil;
3646
+ state = 0;
2183
3647
 
2184
- /*
2185
- * argument check
2186
- */
2187
- Check_Type(data, T_STRING);
3648
+ TypedData_Get_Struct(self, jpeg_decode_t, &jpeg_decoder_data_type, ptr);
2188
3649
 
2189
- /*
2190
- * do encode
2191
- */
2192
- ret = do_decode(ptr, (uint8_t*)RSTRING_PTR(data), RSTRING_LEN(data));
3650
+ /*
3651
+ * argument check
3652
+ */
3653
+ Check_Type(data, T_STRING);
3654
+
3655
+ /*
3656
+ * prepare
3657
+ */
3658
+ SET_DATA(ptr, data);
2193
3659
 
2194
- return ret;
3660
+ /*
3661
+ * do decode
3662
+ */
3663
+ ret = rb_protect(do_decode, (VALUE)ptr, &state);
3664
+
3665
+ /*
3666
+ * post process
3667
+ */
3668
+ CLR_DATA(ptr);
3669
+
3670
+ if (state != 0) rb_jump_tag(state);
3671
+
3672
+ return ret;
2195
3673
  }
2196
3674
 
2197
3675
  static VALUE
@@ -2201,28 +3679,19 @@ rb_test_image(VALUE self, VALUE data)
2201
3679
  struct jpeg_decompress_struct cinfo;
2202
3680
  ext_error_t err_mgr;
2203
3681
 
2204
- jpeg_create_decompress(&cinfo);
2205
-
2206
- cinfo.err = jpeg_std_error(&err_mgr.jerr);
2207
-
2208
- err_mgr.jerr.output_message = decode_output_message;
2209
- err_mgr.jerr.emit_message = decode_emit_message;
2210
- err_mgr.jerr.error_exit = decode_error_exit;
2211
-
2212
- cinfo.raw_data_out = FALSE;
2213
- cinfo.dct_method = JDCT_FLOAT;
3682
+ cinfo.raw_data_out = FALSE;
3683
+ cinfo.dct_method = JDCT_FLOAT;
2214
3684
 
2215
3685
  if (setjmp(err_mgr.jmpbuf)) {
2216
- ret = Qtrue;
2217
- } else {
2218
3686
  ret = Qfalse;
2219
3687
 
3688
+ } else {
2220
3689
  jpeg_mem_src(&cinfo, (uint8_t*)RSTRING_PTR(data), RSTRING_LEN(data));
2221
3690
  jpeg_read_header(&cinfo, TRUE);
2222
3691
  jpeg_calc_output_dimensions(&cinfo);
2223
- }
2224
3692
 
2225
- jpeg_destroy_decompress(&cinfo);
3693
+ ret = Qtrue;
3694
+ }
2226
3695
 
2227
3696
  return ret;
2228
3697
  }
@@ -2256,11 +3725,12 @@ Init_jpeg()
2256
3725
 
2257
3726
  meta_klass = rb_define_class_under(module, "Meta", rb_cObject);
2258
3727
  rb_define_attr(meta_klass, "width", 1, 0);
3728
+ rb_define_attr(meta_klass, "stride", 1, 0);
2259
3729
  rb_define_attr(meta_klass, "height", 1, 0);
2260
3730
  rb_define_attr(meta_klass, "original_colorspace", 1, 0);
2261
3731
  rb_define_attr(meta_klass, "output_colorspace", 1, 0);
2262
3732
  rb_define_attr(meta_klass, "num_components", 1, 0);
2263
- rb_define_attr(meta_klass, "exif", 1, 0);
3733
+ rb_define_attr(meta_klass, "colormap", 1, 0);
2264
3734
 
2265
3735
  decerr_klass = rb_define_class_under(module,
2266
3736
  "DecodeError", rb_eRuntimeError);
@@ -2278,11 +3748,13 @@ Init_jpeg()
2278
3748
  decoder_opts_ids[i] = rb_intern_const(decoder_opts_keys[i]);
2279
3749
  }
2280
3750
 
2281
- id_meta = rb_intern_const("@meta");
2282
- id_width = rb_intern_const("@width");
2283
- id_height = rb_intern_const("@height");
2284
- id_orig_cs = rb_intern_const("@original_colorspace");
2285
- id_out_cs = rb_intern_const("@output_colorspace");
2286
- id_ncompo = rb_intern_const("@num_components");
2287
- id_exif = rb_intern_const("@exif");
3751
+ id_meta = rb_intern_const("@meta");
3752
+ id_width = rb_intern_const("@width");
3753
+ id_stride = rb_intern_const("@stride");
3754
+ id_height = rb_intern_const("@height");
3755
+ id_orig_cs = rb_intern_const("@original_colorspace");
3756
+ id_out_cs = rb_intern_const("@output_colorspace");
3757
+ id_ncompo = rb_intern_const("@num_components");
3758
+ id_exif_tags = rb_intern_const("@exif_tags");
3759
+ id_colormap = rb_intern_const("@colormap");
2288
3760
  }