libpng-ruby 0.5.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/png/png.c CHANGED
@@ -4,9 +4,9 @@
4
4
  * Copyright (C) 2016 Hiroshi Kuwagata <kgt9221@gmail.com>
5
5
  */
6
6
 
7
- /*
8
- * $Id: png.c 67 2016-06-07 06:10:47Z pi $
9
- */
7
+ #ifdef __GNUC__
8
+ #pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
9
+ #endif /* defined(__GNUC__) */
10
10
 
11
11
  #include <stdio.h>
12
12
  #include <stdint.h>
@@ -19,11 +19,13 @@
19
19
  #include <zlib.h>
20
20
 
21
21
  #include "ruby.h"
22
+ #include "ruby/version.h"
22
23
 
23
24
  #define N(x) (sizeof(x)/sizeof(*x))
24
25
 
25
26
  #define RUNTIME_ERROR(msg) rb_raise(rb_eRuntimeError, (msg))
26
27
  #define ARGUMENT_ERROR(msg) rb_raise(rb_eArgError, (msg))
28
+ #define RANGE_ERROR(msg) rb_raise(rb_eRangeError, (msg))
27
29
  #define TYPE_ERROR(msg) rb_raise(rb_eTypeError, (msg))
28
30
  #define NOMEMORY_ERROR(msg) rb_raise(rb_eNoMemError, (msg))
29
31
 
@@ -33,6 +35,22 @@
33
35
  #define EQ_STR(val,str) (rb_to_id(val) == rb_intern(str))
34
36
  #define EQ_INT(val,n) (FIX2INT(val) == n)
35
37
 
38
+ #define SET_DATA(ptr, idat, odat) \
39
+ do { \
40
+ (ptr)->ibuf = (idat);\
41
+ (ptr)->obuf = (odat);\
42
+ (ptr)->error = Qnil;\
43
+ (ptr)->warn_msg = Qnil;\
44
+ } while (0)
45
+
46
+ #define CLR_DATA(ptr) \
47
+ do { \
48
+ (ptr)->ibuf = Qnil;\
49
+ (ptr)->obuf = Qnil;\
50
+ (ptr)->error = Qnil;\
51
+ (ptr)->warn_msg = Qnil;\
52
+ } while (0)
53
+
36
54
  static VALUE module;
37
55
  static VALUE encoder_klass;
38
56
  static VALUE decoder_klass;
@@ -58,13 +76,10 @@ typedef struct {
58
76
  png_infop info;
59
77
 
60
78
  png_uint_32 width;
79
+ png_uint_32 stride;
61
80
  png_uint_32 height;
62
-
63
- png_byte** rows;
64
-
65
- int stride;
81
+ png_uint_32 data_size;
66
82
  int num_comp;
67
- int pixels;
68
83
  int with_time;
69
84
 
70
85
  int c_type; // as 'color type'
@@ -72,10 +87,16 @@ typedef struct {
72
87
  int c_level; // as 'compression level'
73
88
  int f_type; // as 'filter type'
74
89
 
90
+ png_byte** rows;
75
91
  png_text* text;
76
92
  int num_text;
77
-
78
93
  double gamma;
94
+
95
+ VALUE ibuf;
96
+ VALUE obuf;
97
+
98
+ VALUE error;
99
+ VALUE warn_msg;
79
100
  } png_encoder_t;
80
101
 
81
102
  typedef union {
@@ -84,6 +105,9 @@ typedef union {
84
105
  int format;
85
106
  int need_meta;
86
107
  double display_gamma;
108
+
109
+ VALUE error;
110
+ VALUE warn_msg;
87
111
  } common;
88
112
 
89
113
  struct {
@@ -95,6 +119,9 @@ typedef union {
95
119
  int need_meta;
96
120
  double display_gamma;
97
121
 
122
+ VALUE error;
123
+ VALUE warn_msg;
124
+
98
125
  /*
99
126
  * classic api context
100
127
  */
@@ -130,6 +157,9 @@ typedef union {
130
157
  int need_meta;
131
158
  double display_gamma;
132
159
 
160
+ VALUE error;
161
+ VALUE warn_msg;
162
+
133
163
  /*
134
164
  * simplified api context
135
165
  */
@@ -153,16 +183,180 @@ static const char* encoder_opt_keys[] ={
153
183
  "text", // hash<String,String>
154
184
  "time", // bool (default: true)
155
185
  "gamma", // float
186
+ "stride", // int >0
156
187
  };
157
188
 
158
189
  static ID encoder_opt_ids[N(encoder_opt_keys)];
159
190
 
191
+ static void
192
+ mem_io_write_data(png_structp ctx, png_bytep src, png_size_t size)
193
+ {
194
+ VALUE buf;
195
+
196
+ buf = (VALUE)png_get_io_ptr(ctx);
197
+ rb_str_buf_cat(buf, (const char *)src, size);
198
+ }
199
+
200
+ static void
201
+ mem_io_read_data(png_structp ctx, png_bytep dst, png_size_t rq_size)
202
+ {
203
+ mem_io_t* io;
204
+
205
+ io = (mem_io_t*)png_get_io_ptr(ctx);
206
+
207
+ if (io->pos + rq_size <= io->size) {
208
+ memcpy(dst, io->ptr + io->pos, rq_size);
209
+ io->pos += rq_size;
210
+
211
+ } else {
212
+ png_error(ctx, "data not enough.");
213
+ }
214
+ }
215
+
216
+ static void
217
+ mem_io_flush(png_structp ctx)
218
+ {
219
+ // ignore
220
+ }
221
+
222
+ static char*
223
+ clone_cstr(VALUE s)
224
+ {
225
+ char* ret;
226
+ size_t sz;
227
+
228
+ sz = RSTRING_LEN(s);
229
+
230
+ ret = (char*)malloc(sz + 1);
231
+ if (ret == NULL) NOMEMORY_ERROR("no memory");
232
+
233
+ memcpy(ret, RSTRING_PTR(s), sz);
234
+ ret[sz] = '\0';
235
+
236
+ return ret;
237
+ }
238
+
239
+ static VALUE
240
+ create_runtime_error(const char* fmt, ...)
241
+ {
242
+ VALUE ret;
243
+ va_list ap;
244
+
245
+ va_start(ap, fmt);
246
+ ret = rb_exc_new_str(rb_eRuntimeError, rb_vsprintf(fmt, ap));
247
+ va_end(ap);
248
+
249
+ return ret;
250
+ }
251
+
252
+ static VALUE
253
+ create_argument_error(const char* fmt, ...)
254
+ {
255
+ VALUE ret;
256
+ va_list ap;
257
+
258
+ va_start(ap, fmt);
259
+ ret = rb_exc_new_str(rb_eArgError, rb_vsprintf(fmt, ap));
260
+ va_end(ap);
261
+
262
+ return ret;
263
+ }
264
+
265
+ static VALUE
266
+ create_type_error(const char* fmt, ...)
267
+ {
268
+ VALUE ret;
269
+ va_list ap;
270
+
271
+ va_start(ap, fmt);
272
+ ret = rb_exc_new_str(rb_eTypeError, rb_vsprintf(fmt, ap));
273
+ va_end(ap);
274
+
275
+ return ret;
276
+ }
277
+
278
+ static VALUE
279
+ create_range_error(const char* fmt, ...)
280
+ {
281
+ VALUE ret;
282
+ va_list ap;
283
+
284
+ va_start(ap, fmt);
285
+ ret = rb_exc_new_str(rb_eRangeError, rb_vsprintf(fmt, ap));
286
+ va_end(ap);
287
+
288
+ return ret;
289
+ }
290
+
291
+ #if 0
292
+ static VALUE
293
+ create_not_implement_error(const char* fmt, ...)
294
+ {
295
+ VALUE ret;
296
+ va_list ap;
297
+
298
+ va_start(ap, fmt);
299
+ ret = rb_exc_new_str(rb_eNotImpError, rb_vsprintf(fmt, ap));
300
+ va_end(ap);
301
+
302
+ return ret;
303
+ }
304
+ #endif
305
+
306
+ static VALUE
307
+ create_memory_error()
308
+ {
309
+ return rb_exc_new_str(rb_eRangeError, rb_str_new_cstr("no memory"));
310
+ }
311
+
312
+ void
313
+ text_info_free(png_text* text, int n)
314
+ {
315
+ int i;
316
+
317
+ if (text != NULL) {
318
+ for (i = 0; i < n; i++) {
319
+ if (text[i].key != NULL) {
320
+ free(text[i].key);
321
+ }
322
+
323
+ if (text[i].text != NULL) {
324
+ free(text[i].text);
325
+ }
326
+ }
327
+
328
+ free(text);
329
+ }
330
+ }
331
+
332
+ static void
333
+ rb_encoder_mark(void* _ptr)
334
+ {
335
+ png_encoder_t* ptr;
336
+
337
+ ptr = (png_encoder_t*)_ptr;
338
+
339
+ if (ptr->ibuf != Qnil) {
340
+ rb_gc_mark(ptr->ibuf);
341
+ }
342
+
343
+ if (ptr->obuf != Qnil) {
344
+ rb_gc_mark(ptr->obuf);
345
+ }
346
+
347
+ if (ptr->error != Qnil) {
348
+ rb_gc_mark(ptr->error);
349
+ }
350
+
351
+ if (ptr->warn_msg != Qnil) {
352
+ rb_gc_mark(ptr->warn_msg);
353
+ }
354
+ }
160
355
 
161
356
  static void
162
357
  rb_encoder_free(void* _ptr)
163
358
  {
164
359
  png_encoder_t* ptr;
165
- int i;
166
360
 
167
361
  ptr = (png_encoder_t*)_ptr;
168
362
 
@@ -175,35 +369,70 @@ rb_encoder_free(void* _ptr)
175
369
  }
176
370
 
177
371
  if (ptr->text != NULL) {
178
- for (i = 0; i < ptr->num_text; i++) {
179
- if (ptr->text[i].key != NULL) {
180
- xfree(ptr->text[i].key);
181
- }
182
-
183
- if (ptr->text[i].text != NULL) {
184
- xfree(ptr->text[i].text);
185
- }
186
- }
372
+ text_info_free(ptr->text, ptr->num_text);
187
373
  }
188
374
 
375
+ ptr->ibuf = Qnil;
376
+ ptr->obuf = Qnil;
377
+ ptr->error = Qnil;
378
+ ptr->warn_msg = Qnil;
379
+
189
380
  free(ptr);
190
381
  }
191
382
 
192
- static void
193
- mem_io_write_data(png_structp ctx, png_bytep src, png_size_t size)
383
+ static size_t
384
+ rb_encoder_size(const void* _ptr)
194
385
  {
195
- VALUE buf;
386
+ size_t ret;
387
+ png_encoder_t* ptr;
388
+ int i;
196
389
 
197
- buf = (VALUE)png_get_io_ptr(ctx);
198
- rb_str_buf_cat(buf, (const char *)src, size);
199
- }
390
+ ptr = (png_encoder_t*)_ptr;
200
391
 
201
- static void
202
- mem_io_flush(png_structp ctx)
203
- {
204
- // ignore
392
+ ret = sizeof(png_encoder_t);
393
+ // ret += sizeof(png_struct);
394
+ // ret += sizeof(png_info);
395
+ ret += (sizeof(png_byte*) * ptr->width);
396
+
397
+ ret += sizeof(png_text) * ptr->num_text;
398
+
399
+ for (i = 0; i < ptr->num_text; i++) {
400
+ ret += (strlen(ptr->text[i].key) + ptr->text[i].text_length);
401
+ }
402
+
403
+ return ret;
205
404
  }
206
405
 
406
+ #if RUBY_API_VERSION_CODE > 20600
407
+ static const rb_data_type_t png_encoder_data_type = {
408
+ "libpng-ruby encoder object", // wrap_struct_name
409
+ {
410
+ rb_encoder_mark, // function.dmark
411
+ rb_encoder_free, // function.dfree
412
+ rb_encoder_size, // function.dsize
413
+ NULL, // function.dcompact
414
+ {NULL}, // function.reserved
415
+ },
416
+ NULL, // parent
417
+ NULL, // data
418
+ (VALUE)RUBY_TYPED_FREE_IMMEDIATELY // flags
419
+ };
420
+ #else /* RUBY_API_VERSION_CODE > 20600 */
421
+ static const rb_data_type_t png_encoder_data_type = {
422
+ "libpng-ruby encoder object", // wrap_struct_name
423
+ {
424
+ rb_encoder_mark, // function.dmark
425
+ rb_encoder_free, // function.dfree
426
+ rb_encoder_size, // function.dsize
427
+ {NULL, NULL}, // function.reserved
428
+ },
429
+ NULL, // parent
430
+ NULL, // data
431
+ (VALUE)RUBY_TYPED_FREE_IMMEDIATELY // flags
432
+ };
433
+ #endif /* RUBY_API_VERSION_CODE > 20600 */
434
+
435
+
207
436
  static VALUE
208
437
  rb_encoder_alloc(VALUE self)
209
438
  {
@@ -220,82 +449,125 @@ rb_encoder_alloc(VALUE self)
220
449
  ptr->with_time = !0;
221
450
  ptr->gamma = NAN;
222
451
 
223
- return Data_Wrap_Struct(encoder_klass, 0, rb_encoder_free, ptr);
452
+ return TypedData_Wrap_Struct(encoder_klass, &png_encoder_data_type, ptr);
224
453
  }
225
454
 
226
- static void
227
- eval_encoder_opt_color_type(png_encoder_t* ptr, VALUE opt)
455
+ static VALUE
456
+ eval_encoder_opt_pixel_format(png_encoder_t* ptr, VALUE opt)
228
457
  {
229
- if (opt != Qundef) {
458
+ VALUE ret;
459
+ int type;
460
+ int comp;
461
+
462
+ ret = Qnil;
463
+
464
+ switch (TYPE(opt)) {
465
+ case T_UNDEF:
466
+ type = PNG_COLOR_TYPE_RGB;
467
+ comp = 3;
468
+ break;
469
+
470
+ case T_STRING:
471
+ case T_SYMBOL:
230
472
  if (EQ_STR(opt, "GRAY") || EQ_STR(opt, "GRAYSCALE")) {
231
- ptr->c_type = PNG_COLOR_TYPE_GRAY;
232
- ptr->num_comp = 1;
473
+ type = PNG_COLOR_TYPE_GRAY;
474
+ comp = 1;
233
475
 
234
476
  } else if (EQ_STR(opt, "GA")) {
235
- ptr->c_type = PNG_COLOR_TYPE_GA;
236
- ptr->num_comp = 2;
477
+ type = PNG_COLOR_TYPE_GA;
478
+ comp = 2;
237
479
 
238
480
  } else if (EQ_STR(opt, "RGB")) {
239
- ptr->c_type = PNG_COLOR_TYPE_RGB;
240
- ptr->num_comp = 3;
481
+ type = PNG_COLOR_TYPE_RGB;
482
+ comp = 3;
241
483
 
242
484
  } else if (EQ_STR(opt, "RGBA")) {
243
- ptr->c_type = PNG_COLOR_TYPE_RGBA;
244
- ptr->num_comp = 4;
485
+ type = PNG_COLOR_TYPE_RGBA;
486
+ comp = 4;
245
487
 
246
488
  } else {
247
- ARGUMENT_ERROR(":color_type is invalid value");
489
+ ret = create_argument_error(":pixel_format invalid value");
248
490
  }
491
+ break;
492
+
493
+ default:
494
+ ret = create_type_error(":pixel_format invalid type");
495
+ break;
249
496
  }
497
+
498
+ if (!RTEST(ret)) {
499
+ ptr->c_type = type;
500
+ ptr->num_comp = comp;
501
+ }
502
+
503
+ return ret;
250
504
  }
251
505
 
252
- static void
506
+ static VALUE
253
507
  eval_encoder_opt_interlace(png_encoder_t* ptr, VALUE opt)
254
508
  {
255
- if (opt != Qundef) {
509
+ switch (TYPE(opt)) {
510
+ case T_UNDEF:
511
+ ptr->i_meth = PNG_INTERLACE_NONE;
512
+ break;
513
+
514
+ default:
256
515
  ptr->i_meth = (RTEST(opt))? PNG_INTERLACE_ADAM7: PNG_INTERLACE_NONE;
516
+ break;
257
517
  }
518
+
519
+ return Qnil;
258
520
  }
259
521
 
260
- static void
522
+ static VALUE
261
523
  eval_encoder_opt_compression(png_encoder_t* ptr, VALUE opt)
262
524
  {
263
- int val;
264
-
265
- if (opt != Qundef) {
266
- switch (TYPE(opt)) {
267
- case T_FIXNUM:
268
- val = FIX2INT(opt);
269
- if (val < 0 || val > 9) {
270
- ARGUMENT_ERROR(":compress is out of range");
271
- }
272
- break;
525
+ VALUE ret;
526
+ int lv;
273
527
 
274
- case T_STRING:
275
- case T_SYMBOL:
276
- if (EQ_STR(opt, "NO_COMPRESSION")) {
277
- val = Z_NO_COMPRESSION;
528
+ ret = Qnil;
278
529
 
279
- } else if (EQ_STR(opt, "BEST_SPEED")) {
280
- val = Z_BEST_SPEED;
530
+ switch (TYPE(opt)) {
531
+ case T_UNDEF:
532
+ lv = Z_DEFAULT_COMPRESSION;
533
+ break;
281
534
 
282
- } else if (EQ_STR(opt, "BEST_COMPRESSION")) {
283
- val = Z_BEST_COMPRESSION;
535
+ case T_STRING:
536
+ case T_SYMBOL:
537
+ if (EQ_STR(opt, "NO_COMPRESSION")) {
538
+ lv = Z_NO_COMPRESSION;
284
539
 
285
- } else if (EQ_STR(opt, "DEFAULT")) {
286
- val = Z_DEFAULT_COMPRESSION;
540
+ } else if (EQ_STR(opt, "BEST_SPEED")) {
541
+ lv = Z_BEST_SPEED;
287
542
 
288
- } else {
289
- ARGUMENT_ERROR(":interlace is invalid value");
290
- }
291
- break;
543
+ } else if (EQ_STR(opt, "BEST_COMPRESSION")) {
544
+ lv = Z_BEST_COMPRESSION;
545
+
546
+ } else if (EQ_STR(opt, "DEFAULT")) {
547
+ lv = Z_DEFAULT_COMPRESSION;
292
548
 
293
- default:
294
- TYPE_ERROR(":interlace is not Symbol and String");
549
+ } else {
550
+ ret = create_argument_error(":compress is invalid value");
295
551
  }
552
+ break;
553
+
554
+ case T_FIXNUM:
555
+ lv = FIX2INT(opt);
556
+ if (lv < 0 || lv > 9) {
557
+ ret = create_range_error(":compress out of range");
558
+ }
559
+ break;
296
560
 
297
- ptr->c_level = val;
561
+ default:
562
+ ret = create_type_error(":compress invalid type");
563
+ break;
298
564
  }
565
+
566
+ if (!RTEST(ret)) {
567
+ ptr->c_level = lv;
568
+ }
569
+
570
+ return ret;
299
571
  }
300
572
 
301
573
  static VALUE
@@ -313,302 +585,522 @@ capitalize(VALUE str)
313
585
  return rb_ary_join(tmp, rb_str_new_cstr(" "));
314
586
  }
315
587
 
316
- static char*
317
- clone_cstr(VALUE s)
588
+ struct convert_text_rb2c_arg {
589
+ VALUE src;
590
+ png_text* dst;
591
+ };
592
+
593
+ static VALUE
594
+ convert_text_rb2c(VALUE _arg)
318
595
  {
319
- char* ret;
320
- size_t sz;
596
+ struct convert_text_rb2c_arg* arg;
597
+ VALUE src;
598
+ png_text* dst;
599
+ VALUE keys;
600
+ int i;
601
+ VALUE key;
602
+ VALUE val;
321
603
 
322
- sz = RSTRING_LEN(s);
323
- ret = (char*)malloc(sz + 1);
604
+ arg = (struct convert_text_rb2c_arg*)_arg;
605
+ src = arg->src;
606
+ dst = arg->dst;
324
607
 
325
- memcpy(ret, RSTRING_PTR(s), sz);
326
- ret[sz] = '\0';
608
+ /*
609
+ * 途中で例外が発生する可能性があるので資源回収できる様に
610
+ * 0クリアを最初に済ませておく。
611
+ */
612
+ keys = rb_funcall(src, rb_intern("keys"), 0);
327
613
 
328
- return ret;
329
- }
614
+ for (i = 0; i < RARRAY_LEN(keys); i++) {
615
+ key = rb_ary_entry(keys, i);
616
+ val = rb_hash_aref(src, key);
330
617
 
618
+ if (TYPE(key) != T_STRING && TYPE(key) != T_SYMBOL) {
619
+ ARGUMENT_ERROR(":type is invalid structure");
620
+ }
331
621
 
332
- static void
622
+ if (TYPE(val) != T_STRING && TYPE(val) != T_SYMBOL) {
623
+ ARGUMENT_ERROR(":type is invalid structure");
624
+ }
625
+
626
+ key = capitalize(key);
627
+ if (RSTRING_LEN(key) >= 0 && RSTRING_LEN(key) <= 79) {
628
+ dst[i].compression = PNG_TEXT_COMPRESSION_NONE;
629
+ dst[i].key = clone_cstr(key);
630
+ dst[i].text = clone_cstr(val);
631
+ dst[i].text_length = RSTRING_LEN(val);
632
+
633
+ } else {
634
+ ARGUMENT_ERROR("keyword in :text is too long");
635
+ }
636
+ }
637
+
638
+ return Qnil;
639
+ }
640
+
641
+ static VALUE
333
642
  eval_encoder_opt_text(png_encoder_t* ptr, VALUE opt)
334
643
  {
335
- VALUE keys;
336
- VALUE key;
337
- VALUE val;
338
- int i;
644
+ VALUE ret;
339
645
  png_text* text;
646
+ size_t size;
647
+ struct convert_text_rb2c_arg arg;
648
+ int state;
340
649
 
341
- text = NULL;
342
-
343
- if (opt != Qundef) {
344
- if (TYPE(opt) == T_HASH && RHASH_SIZE(opt) > 0) {
345
- keys = rb_funcall(opt, rb_intern("keys"), 0);
346
- text = (png_text*)xmalloc(sizeof(png_text) * RHASH_SIZE(opt));
347
-
348
- /*
349
- * 途中で例外が発生する可能性があるので rb_encoder_free()で
350
- * 資源回収できる様に 0クリアとコンテキスト登録を最初に済ま
351
- * せておく。
352
- *
353
- * なお、この処理はinitialize()の過程で呼び出される。このた
354
- * め例外が発生しコンテキストの状態が中途半端な状態でユーザ
355
- * から参照されることはない。
356
- */
357
- memset(text, 0, sizeof(*text));
358
- ptr->text = text;
359
- ptr->num_text = RARRAY_LEN(keys);
360
-
361
- for (i = 0; i < RARRAY_LEN(keys); i++) {
362
- key = RARRAY_AREF(keys, i);
363
- val = rb_hash_aref(opt, key);
364
-
365
- if (TYPE(key) != T_STRING && TYPE(key) != T_SYMBOL) {
366
- ARGUMENT_ERROR(":type is invalid structure");
367
- }
368
-
369
- if (TYPE(val) != T_STRING && TYPE(val) != T_SYMBOL) {
370
- ARGUMENT_ERROR(":type is invalid structure");
371
- }
372
-
373
- key = capitalize(key);
374
- if (RSTRING_LEN(key) >= 0 && RSTRING_LEN(key) <= 79) {
375
- text[i].compression = PNG_TEXT_COMPRESSION_NONE;
376
- text[i].key = clone_cstr(key);
377
- text[i].text = clone_cstr(val);
378
- text[i].text_length = RSTRING_LEN(val);
379
-
380
- } else {
381
- ARGUMENT_ERROR("keyword in :text is too long");
382
- }
383
- }
384
- } else {
385
- ARGUMENT_ERROR(":text is invalid value");
650
+ ret = Qnil;
651
+ text = NULL;
652
+ size = 0;
653
+ state = 0;
654
+
655
+ switch (TYPE(opt)) {
656
+ case T_UNDEF:
657
+ // ignore
658
+ break;
659
+
660
+ case T_HASH:
661
+ size = RHASH_SIZE(opt);
662
+ if (size == 0) break;
663
+
664
+ text = (png_text*)malloc(sizeof(png_text) * size);
665
+ if (text == NULL) {
666
+ ret = create_memory_error();
667
+ break;
668
+ }
669
+
670
+ arg.src = opt;
671
+ arg.dst = text;
672
+
673
+ rb_protect(convert_text_rb2c, (VALUE)&arg, &state);
674
+
675
+ if (state != 0) {
676
+ ret = rb_errinfo();
677
+ rb_set_errinfo(Qnil);
678
+ text_info_free(text, size);
386
679
  }
680
+ break;
681
+
682
+ default:
683
+ ret = create_type_error(":text invalid type");
684
+ break;
685
+ }
686
+
687
+ if (!RTEST(ret)) {
688
+ ptr->text = text;
689
+ ptr->num_text = size;
387
690
  }
691
+
692
+ return ret;
388
693
  }
389
694
 
390
- static void
695
+ static VALUE
391
696
  eval_encoder_opt_time(png_encoder_t* ptr, VALUE opt)
392
697
  {
393
- if (opt != Qundef) {
698
+ switch (TYPE(opt)) {
699
+ case T_UNDEF:
700
+ ptr->with_time = !0;
701
+ break;
702
+
703
+ default:
394
704
  ptr->with_time = RTEST(opt);
705
+ break;
395
706
  }
707
+
708
+ return Qnil;
396
709
  }
397
710
 
398
- static void
711
+ static VALUE
399
712
  eval_encoder_opt_gamma(png_encoder_t* ptr, VALUE opt)
400
713
  {
401
- if (opt != Qundef) {
402
- ptr->gamma = NUM2DBL(opt);
714
+ VALUE ret;
715
+ double gamma;
716
+
717
+ ret = Qnil;
718
+
719
+ switch (TYPE(opt)) {
720
+ case T_UNDEF:
721
+ gamma = NAN;
722
+ break;
723
+
724
+ case T_FIXNUM:
725
+ case T_FLOAT:
726
+ case T_RATIONAL:
727
+ gamma = NUM2DBL(opt);
728
+ break;
729
+
730
+ default:
731
+ ret = create_type_error(":gamma invalid type");
732
+ break;
403
733
  }
734
+
735
+ if (!RTEST(ret)) ptr->gamma = gamma;
736
+
737
+ return ret;
738
+ }
739
+
740
+ static VALUE
741
+ eval_encoder_opt_stride(png_encoder_t* ptr, VALUE opt)
742
+ {
743
+ VALUE ret;
744
+ png_uint_32 stride;
745
+
746
+ ret = Qnil;
747
+
748
+ switch (TYPE(opt)) {
749
+ case T_UNDEF:
750
+ stride = ptr->width * ptr->num_comp;
751
+ break;
752
+
753
+ case T_FIXNUM:
754
+ if (FIX2LONG(opt) >= (ptr->width * ptr->num_comp)) {
755
+ stride = FIX2LONG(opt);
756
+
757
+ } else {
758
+ ret = create_argument_error(":stride too little");
759
+ }
760
+ break;
761
+
762
+ default:
763
+ ret = create_type_error(":stride invalid type");
764
+ break;
765
+ }
766
+
767
+ if (!RTEST(ret)) ptr->stride = stride;
768
+
769
+ return ret;
404
770
  }
405
771
 
406
772
  static void
773
+ encode_error(png_structp ctx, png_const_charp msg)
774
+ {
775
+ png_encoder_t* ptr;
776
+
777
+ ptr = (png_encoder_t*)png_get_error_ptr(ctx);
778
+
779
+ ptr->error = create_runtime_error("encode error:%s", msg);
780
+
781
+ longjmp(png_jmpbuf(ptr->ctx), 1);
782
+ }
783
+
784
+ static void
785
+ encode_warn(png_structp ctx, png_const_charp msg)
786
+ {
787
+ png_encoder_t* ptr;
788
+
789
+ ptr = (png_encoder_t*)png_get_error_ptr(ctx);
790
+
791
+ if (ptr->warn_msg != Qnil) {
792
+ ptr->warn_msg = rb_ary_new();
793
+ }
794
+
795
+ rb_ary_push(ptr->warn_msg, rb_str_new_cstr(msg));
796
+ }
797
+
798
+ static VALUE
407
799
  set_encoder_context(png_encoder_t* ptr, int wd, int ht, VALUE opt)
408
800
  {
801
+ VALUE ret;
802
+
409
803
  png_structp ctx;
410
804
  png_infop info;
805
+ png_byte** rows;
806
+
411
807
  VALUE opts[N(encoder_opt_ids)];
412
808
 
413
- const char* err;
414
- png_byte** rows;
809
+ /*
810
+ * initialize
811
+ */
812
+ ret = Qnil;
813
+ ctx = NULL;
814
+ info = NULL;
815
+ rows = NULL;
415
816
 
416
817
  /*
417
- * parse options
818
+ * argument check
418
819
  */
419
- rb_get_kwargs(opt, encoder_opt_ids, 0, N(encoder_opt_ids), opts);
820
+ do {
821
+ if (wd <= 0) {
822
+ ret = create_range_error("image width less equal zero");
823
+ break;
824
+ }
825
+
826
+ if (ht <= 0) {
827
+ ret = create_range_error("image height less equal zero");
828
+ break;
829
+ }
830
+ } while (0);
420
831
 
421
832
  /*
422
- * set context
833
+ * parse options
423
834
  */
424
- eval_encoder_opt_color_type(ptr, opts[0]);
425
- eval_encoder_opt_interlace(ptr, opts[1]);
426
- eval_encoder_opt_compression(ptr, opts[2]);
427
- eval_encoder_opt_text(ptr, opts[3]);
428
- eval_encoder_opt_time(ptr, opts[4]);
429
- eval_encoder_opt_gamma(ptr, opts[5]);
835
+ if (!RTEST(ret)) do {
836
+ rb_get_kwargs(opt, encoder_opt_ids, 0, N(encoder_opt_ids), opts);
837
+
838
+ // オプション評価で使用するので前もって設定しておく
839
+ ptr->width = wd;
840
+ ptr->height = ht;
841
+
842
+ ret = eval_encoder_opt_pixel_format(ptr, opts[0]);
843
+ if (RTEST(ret)) break;
844
+
845
+ ret = eval_encoder_opt_interlace(ptr, opts[1]);
846
+ if (RTEST(ret)) break;
847
+
848
+ ret = eval_encoder_opt_compression(ptr, opts[2]);
849
+ if (RTEST(ret)) break;
850
+
851
+ ret = eval_encoder_opt_text(ptr, opts[3]);
852
+ if (RTEST(ret)) break;
853
+
854
+ ret = eval_encoder_opt_time(ptr, opts[4]);
855
+ if (RTEST(ret)) break;
856
+
857
+ ret = eval_encoder_opt_gamma(ptr, opts[5]);
858
+ if (RTEST(ret)) break;
859
+
860
+ ret = eval_encoder_opt_stride(ptr, opts[6]);
861
+ if (RTEST(ret)) break;
862
+ } while (0);
430
863
 
431
864
  /*
432
865
  * create PNG context
433
866
  */
434
- do {
435
- err = NULL;
436
- rows = NULL;
437
- info = NULL;
438
-
439
- ctx = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
867
+ if (!RTEST(ret)) do {
868
+ ctx = png_create_write_struct(PNG_LIBPNG_VER_STRING,
869
+ ptr,
870
+ encode_error,
871
+ encode_warn);
440
872
  if (ctx == NULL) {
441
- err = "png_create_read_struct() failed.";
873
+ ret = create_runtime_error("png_create_read_struct() failed");
442
874
  break;
443
875
  }
444
876
 
445
877
  info = png_create_info_struct(ctx);
446
878
  if (info == NULL) {
447
- err = "png_create_info_structt() failed.";
879
+ ret = create_runtime_error("png_create_info_structt() failed");
448
880
  break;
449
881
  }
450
882
 
451
883
  rows = png_malloc(ctx, ht * sizeof(png_byte*));
452
884
  if (rows == NULL) {
453
- err = "png_malloc() failed.";
885
+ ret = create_memory_error("png_malloc() failed");
454
886
  break;
455
887
  }
456
888
 
457
889
  memset(rows, 0, ht * sizeof(png_byte*));
458
890
 
459
- ptr->ctx = ctx;
460
- ptr->info = info;
461
- ptr->width = wd;
462
- ptr->height = ht;
463
- ptr->stride = ptr->width * ptr->num_comp;
464
- ptr->pixels = ptr->stride * ptr->height;
465
- ptr->rows = rows;
466
-
891
+ ptr->data_size = ptr->stride * ptr->height;
892
+ ptr->ctx = ctx;
893
+ ptr->info = info;
894
+ ptr->rows = rows;
895
+ ptr->ibuf = Qnil;
896
+ ptr->obuf = Qnil;
897
+ ptr->error = Qnil;
898
+ ptr->warn_msg = Qnil;
467
899
  } while(0);
468
900
 
469
901
  /*
470
902
  * post process
471
903
  */
472
- if (err != NULL) {
473
- if (ctx != NULL) {
474
- png_destroy_write_struct(&ctx, &info);
475
- }
476
-
477
- if (rows != NULL) {
478
- png_free(ctx, rows);
479
- }
904
+ if (RTEST(ret)) {
905
+ if (ctx != NULL) png_destroy_write_struct(&ctx, &info);
906
+ if (rows != NULL) png_free(ctx, rows);
480
907
  }
908
+
909
+ return ret;
481
910
  }
482
911
 
483
912
  static VALUE
484
913
  rb_encoder_initialize(int argc, VALUE* argv, VALUE self)
485
914
  {
486
915
  png_encoder_t* ptr;
916
+ VALUE exc;
487
917
  VALUE wd;
488
918
  VALUE ht;
489
919
  VALUE opts;
490
920
 
921
+ /*
922
+ * initialize
923
+ */
924
+ exc = Qnil;
925
+
491
926
  /*
492
927
  * strip object
493
928
  */
494
- Data_Get_Struct(self, png_encoder_t, ptr);
929
+ TypedData_Get_Struct(self, png_encoder_t, &png_encoder_data_type, ptr);
495
930
 
496
931
  /*
497
932
  * parse argument
498
933
  */
499
- rb_scan_args(argc, argv, "21", &wd, &ht, &opts);
934
+ rb_scan_args(argc, argv, "2:", &wd, &ht, &opts);
500
935
 
501
- Check_Type(wd, T_FIXNUM);
502
- Check_Type(ht, T_FIXNUM);
503
- if (opts != Qnil) Check_Type(opts, T_HASH);
936
+ /*
937
+ * check argument
938
+ */
939
+ do {
940
+ if (TYPE(wd) != T_FIXNUM) {
941
+ exc = create_argument_error("invalid width");
942
+ break;
943
+ }
504
944
 
945
+ if (TYPE(ht) != T_FIXNUM) {
946
+ exc = create_argument_error("invalid height");
947
+ break;
948
+ }
949
+ } while (0);
505
950
 
506
951
  /*
507
952
  * set context
508
953
  */
509
- set_encoder_context(ptr, FIX2INT(wd), FIX2INT(ht), opts);
954
+ if (!RTEST(exc)) {
955
+ exc = set_encoder_context(ptr, FIX2INT(wd), FIX2INT(ht), opts);
956
+ }
957
+
958
+ /*
959
+ * post process
960
+ */
961
+ if (RTEST(exc)) rb_exc_raise(exc);
510
962
 
511
963
  return self;
512
964
  }
513
965
 
966
+ static VALUE
967
+ do_encode(VALUE arg)
968
+ {
969
+ png_encoder_t* ptr;
970
+ png_uint_32 i;
971
+ png_byte* bytes;
972
+
973
+ /*
974
+ * initialize
975
+ */
976
+ ptr = (png_encoder_t*)arg;
977
+
978
+ if (setjmp(png_jmpbuf(ptr->ctx))) {
979
+ rb_exc_raise(ptr->error);
980
+
981
+ } else {
982
+ png_set_IHDR(ptr->ctx,
983
+ ptr->info,
984
+ ptr->width,
985
+ ptr->height,
986
+ 8,
987
+ ptr->c_type,
988
+ ptr->i_meth,
989
+ PNG_COMPRESSION_TYPE_BASE,
990
+ PNG_FILTER_TYPE_BASE);
991
+
992
+ if (ptr->text) {
993
+ png_set_text(ptr->ctx, ptr->info, ptr->text, ptr->num_text);
994
+ }
995
+
996
+ if (ptr->with_time) {
997
+ time_t tm;
998
+ png_time png_time;
999
+
1000
+ time(&tm);
1001
+ png_convert_from_time_t(&png_time, tm);
1002
+ png_set_tIME(ptr->ctx, ptr->info, &png_time);
1003
+ }
1004
+
1005
+ if (!isnan(ptr->gamma)) {
1006
+ png_set_gAMA(ptr->ctx, ptr->info, ptr->gamma);
1007
+ }
1008
+
1009
+ png_set_compression_level(ptr->ctx, ptr->c_level);
1010
+
1011
+ png_set_write_fn(ptr->ctx,
1012
+ (png_voidp)ptr->obuf,
1013
+ (png_rw_ptr)mem_io_write_data,
1014
+ (png_flush_ptr)mem_io_flush);
1015
+
1016
+ bytes = (png_byte*)RSTRING_PTR(ptr->ibuf);
1017
+ for (i = 0; i < ptr->height; i++) {
1018
+ ptr->rows[i] = bytes;
1019
+ bytes += ptr->stride;
1020
+ }
1021
+
1022
+ png_set_rows(ptr->ctx, ptr->info, ptr->rows);
1023
+ png_write_png(ptr->ctx, ptr->info, PNG_TRANSFORM_IDENTITY, NULL);
1024
+ }
1025
+
1026
+ return Qnil;
1027
+ }
1028
+
514
1029
  static VALUE
515
1030
  rb_encoder_encode(VALUE self, VALUE data)
516
1031
  {
517
1032
  VALUE ret;
1033
+ VALUE exc;
518
1034
  png_encoder_t* ptr;
519
- png_uint_32 i;
520
- uint8_t* p;
1035
+ int state;
521
1036
 
522
1037
  /*
523
1038
  * initialize
524
1039
  */
525
- ret = rb_str_buf_new(0);
1040
+ ret = rb_str_buf_new(0);
1041
+ exc = Qnil;
1042
+ state = 0;
526
1043
 
527
1044
  /*
528
1045
  * strip object
529
1046
  */
530
- Data_Get_Struct(self, png_encoder_t, ptr);
1047
+ TypedData_Get_Struct(self, png_encoder_t, &png_encoder_data_type, ptr);
531
1048
 
532
1049
  /*
533
1050
  * argument check
534
1051
  */
535
1052
  Check_Type(data, T_STRING);
536
- if (RSTRING_LEN(data) != ptr->pixels) {
537
- ARGUMENT_ERROR("invalid data size");
538
- }
539
-
540
- /*
541
- * call libpng
542
- */
543
- png_set_IHDR(ptr->ctx,
544
- ptr->info,
545
- ptr->width,
546
- ptr->height,
547
- 8,
548
- ptr->c_type,
549
- ptr->i_meth,
550
- PNG_COMPRESSION_TYPE_BASE,
551
- PNG_FILTER_TYPE_BASE);
552
-
553
- if (ptr->text) {
554
- png_set_text(ptr->ctx, ptr->info, ptr->text, ptr->num_text);
555
- }
556
-
557
- if (ptr->with_time) {
558
- time_t tm;
559
- png_time png_time;
560
1053
 
561
- time(&tm);
562
- png_convert_from_time_t(&png_time, tm);
563
- png_set_tIME(ptr->ctx, ptr->info, &png_time);
1054
+ if (RSTRING_LEN(data) < ptr->data_size) {
1055
+ ARGUMENT_ERROR("image data too short");
564
1056
  }
565
1057
 
566
- if (!isnan(ptr->gamma)) {
567
- png_set_gAMA(ptr->ctx, ptr->info, ptr->gamma);
1058
+ if (RSTRING_LEN(data) > ptr->data_size) {
1059
+ ARGUMENT_ERROR("image data too large");
568
1060
  }
569
1061
 
570
- png_set_compression_level(ptr->ctx, ptr->c_level);
571
-
572
- png_set_write_fn(ptr->ctx,
573
- (png_voidp)ret,
574
- (png_rw_ptr)mem_io_write_data,
575
- (png_flush_ptr)mem_io_flush);
1062
+ /*
1063
+ * prepare
1064
+ */
1065
+ SET_DATA(ptr, data, ret);
576
1066
 
577
- p = (png_byte*)RSTRING_PTR(data);
578
- for (i = 0; i < ptr->height; i++) {
579
- ptr->rows[i] = p;
580
- p += ptr->stride;
1067
+ /*
1068
+ * do encode
1069
+ */
1070
+ if (!RTEST(exc)) {
1071
+ rb_protect(do_encode, (VALUE)ptr, &state);
581
1072
  }
582
1073
 
583
- png_set_rows(ptr->ctx, ptr->info, ptr->rows);
584
-
585
- if (setjmp(png_jmpbuf(ptr->ctx))) {
586
- RUNTIME_ERROR("encode error");
587
-
588
- } else {
589
- png_write_png(ptr->ctx, ptr->info, PNG_TRANSFORM_IDENTITY, NULL);
1074
+ /*
1075
+ * post process
1076
+ */
1077
+ if(state == 0) {
1078
+ rb_ivar_set(ret, rb_intern("warn"), ptr->warn_msg);
1079
+ }
1080
+
1081
+ CLR_DATA(ptr);
1082
+
1083
+ if (state != 0) {
1084
+ rb_jump_tag(state);
590
1085
  }
591
1086
 
1087
+ /*
1088
+ * post process
1089
+ */
1090
+ ptr->ibuf = Qnil;
1091
+ ptr->obuf = Qnil;
1092
+
1093
+ if (RTEST(exc)) rb_exc_raise(exc);
1094
+
592
1095
  return ret;
593
1096
  }
594
1097
 
595
1098
  static void
596
- mem_io_read_data(png_structp ctx, png_bytep dst, png_size_t rq_size)
1099
+ rb_decoder_mark(void* _ptr)
597
1100
  {
598
- mem_io_t* io;
599
-
600
- io = (mem_io_t*)png_get_io_ptr(ctx);
601
-
602
- if (io->pos + rq_size <= io->size) {
603
- memcpy(dst, io->ptr + io->pos, rq_size);
604
- io->pos += rq_size;
605
-
606
- } else {
607
- png_error(ctx, "data not enough.");
608
- }
1101
+ // nothing
609
1102
  }
610
1103
 
611
-
612
1104
  static void
613
1105
  rb_decoder_free(void* _ptr)
614
1106
  {
@@ -637,6 +1129,45 @@ rb_decoder_free(void* _ptr)
637
1129
  free(ptr);
638
1130
  }
639
1131
 
1132
+ static size_t
1133
+ rb_decoder_size(const void* _ptr)
1134
+ {
1135
+ size_t ret;
1136
+
1137
+ ret = sizeof(png_decoder_t);
1138
+
1139
+ return ret;
1140
+ }
1141
+
1142
+ #if RUBY_API_VERSION_CODE > 20600
1143
+ static const rb_data_type_t png_decoder_data_type = {
1144
+ "libpng-ruby decoder object", // wrap_struct_name
1145
+ {
1146
+ rb_decoder_mark, // function.dmark
1147
+ rb_decoder_free, // function.dfree
1148
+ rb_decoder_size, // function.dsize
1149
+ NULL, // function.dcompact
1150
+ {NULL}, // function.reserved
1151
+ },
1152
+ NULL, // parent
1153
+ NULL, // data
1154
+ (VALUE)RUBY_TYPED_FREE_IMMEDIATELY // flags
1155
+ };
1156
+ #else /* RUBY_API_VERSION_CODE > 20600 */
1157
+ static const rb_data_type_t png_decoder_data_type = {
1158
+ "libpng-ruby decoder object", // wrap_struct_name
1159
+ {
1160
+ rb_decoder_mark, // function.dmark
1161
+ rb_decoder_free, // function.dfree
1162
+ rb_decoder_size, // function.dsize
1163
+ {NULL, NULL}, // function.reserved
1164
+ },
1165
+ NULL, // parent
1166
+ NULL, // data
1167
+ (VALUE)RUBY_TYPED_FREE_IMMEDIATELY // flags
1168
+ };
1169
+ #endif /* RUBY_API_VERSION_CODE > 20600 */
1170
+
640
1171
  static VALUE
641
1172
  rb_decoder_alloc(VALUE self)
642
1173
  {
@@ -650,35 +1181,60 @@ rb_decoder_alloc(VALUE self)
650
1181
  ptr->common.need_meta = !0;
651
1182
  ptr->common.display_gamma = NAN;
652
1183
 
653
- return Data_Wrap_Struct(decoder_klass, 0, rb_decoder_free, ptr);
1184
+ return TypedData_Wrap_Struct(decoder_klass, &png_decoder_data_type, ptr);
654
1185
  }
655
1186
 
656
- static void
1187
+ static VALUE
657
1188
  eval_decoder_opt_api_type(png_decoder_t* ptr, VALUE opt)
658
1189
  {
659
- int api_type;
1190
+ VALUE ret;
1191
+ int type;
1192
+
1193
+ ret = Qnil;
1194
+
1195
+ switch (TYPE(opt)) {
1196
+ case T_UNDEF:
1197
+ type = API_SIMPLIFIED;
1198
+ break;
660
1199
 
661
- if (opt != Qundef) {
1200
+ case T_STRING:
1201
+ case T_SYMBOL:
662
1202
  if (EQ_STR(opt, "simplified")) {
663
- api_type = API_SIMPLIFIED;
1203
+ type = API_SIMPLIFIED;
664
1204
 
665
1205
  } else if (EQ_STR(opt, "classic")) {
666
- api_type = API_CLASSIC;
1206
+ type = API_CLASSIC;
667
1207
 
668
1208
  } else {
669
- ARGUMENT_ERROR(":api_type is invalid value");
1209
+ ret = create_argument_error(":api_type invalid value");
670
1210
  }
1211
+ break;
671
1212
 
672
- ptr->common.api_type = api_type;
1213
+ default:
1214
+ ret = create_type_error(":api_type invalid type");
1215
+ break;
673
1216
  }
1217
+
1218
+ if (!RTEST(ret)) ptr->common.api_type = type;
1219
+
1220
+ return ret;
674
1221
  }
675
1222
 
676
- static void
1223
+ static VALUE
677
1224
  eval_decoder_opt_pixel_format(png_decoder_t* ptr, VALUE opt)
678
1225
  {
1226
+ VALUE ret;
679
1227
  int format;
680
1228
 
681
- if (opt != Qundef) {
1229
+ ret = Qnil;
1230
+
1231
+ switch (TYPE(opt)) {
1232
+ case T_UNDEF:
1233
+ format = PNG_FORMAT_RGB;
1234
+ break;
1235
+
1236
+ case T_STRING:
1237
+ case T_SYMBOL:
682
1238
  if (EQ_STR(opt, "GRAY") || EQ_STR(opt, "GRAYSCALE")) {
683
1239
  format = PNG_FORMAT_GRAY;
684
1240
 
@@ -707,97 +1263,179 @@ eval_decoder_opt_pixel_format(png_decoder_t* ptr, VALUE opt)
707
1263
  format = PNG_FORMAT_ABGR;
708
1264
 
709
1265
  } else {
710
- ARGUMENT_ERROR(":color_type is invalid value");
1266
+ ret = create_argument_error(":pixel_format invalid value");
711
1267
  }
1268
+ break;
712
1269
 
713
- ptr->common.format = format;
1270
+ default:
1271
+ ret = create_type_error(":pixel_format invalid type");
1272
+ break;
714
1273
  }
1274
+
1275
+ if (!RTEST(ret)) ptr->common.format = format;
1276
+
1277
+ return ret;
715
1278
  }
716
1279
 
717
- static void
1280
+ static VALUE
718
1281
  eval_decoder_opt_without_meta(png_decoder_t* ptr, VALUE opt)
719
1282
  {
720
- if (opt != Qundef) {
1283
+ switch (TYPE(opt)) {
1284
+ case T_UNDEF:
1285
+ ptr->common.need_meta = !0;
1286
+ break;
1287
+
1288
+ default:
721
1289
  ptr->common.need_meta = !RTEST(opt);
1290
+ break;
722
1291
  }
1292
+
1293
+ return Qnil;
723
1294
  }
724
1295
 
725
- static void
1296
+ static VALUE
726
1297
  eval_decoder_opt_display_gamma(png_decoder_t* ptr, VALUE opt)
727
1298
  {
728
- if (opt != Qundef) {
1299
+ switch (TYPE(opt)) {
1300
+ case T_UNDEF:
1301
+ ptr->common.display_gamma = NAN;
1302
+ break;
1303
+
1304
+ default:
729
1305
  ptr->common.display_gamma = NUM2DBL(opt);
1306
+ break;
730
1307
  }
1308
+
1309
+ return Qnil;
731
1310
  }
732
1311
 
733
- static void
1312
+ static VALUE
734
1313
  set_decoder_context(png_decoder_t* ptr, VALUE opt)
735
1314
  {
1315
+ VALUE ret;
736
1316
  VALUE opts[N(decoder_opt_ids)];
737
1317
 
738
1318
  /*
739
- * parse options
1319
+ * initialize
740
1320
  */
741
- rb_get_kwargs(opt, decoder_opt_ids, 0, N(decoder_opt_ids), opts);
1321
+ ret = Qnil;
742
1322
 
743
1323
  /*
744
- * set context
1324
+ * parse options
745
1325
  */
746
- eval_decoder_opt_api_type(ptr, opts[2]);
747
- eval_decoder_opt_pixel_format(ptr, opts[0]);
748
- eval_decoder_opt_without_meta(ptr, opts[1]);
749
- eval_decoder_opt_display_gamma(ptr, opts[3]);
1326
+ do {
1327
+ rb_get_kwargs(opt, decoder_opt_ids, 0, N(decoder_opt_ids), opts);
1328
+
1329
+ ret = eval_decoder_opt_api_type(ptr, opts[2]);
1330
+ if (RTEST(ret)) break;
1331
+
1332
+ ret = eval_decoder_opt_pixel_format(ptr, opts[0]);
1333
+ if (RTEST(ret)) break;
1334
+
1335
+ ret = eval_decoder_opt_without_meta(ptr, opts[1]);
1336
+ if (RTEST(ret)) break;
1337
+
1338
+ ret = eval_decoder_opt_display_gamma(ptr, opts[3]);
1339
+ if (RTEST(ret)) break;
1340
+ } while (0);
1341
+
1342
+ return ret;
750
1343
  }
751
1344
 
752
1345
  static VALUE
753
1346
  rb_decoder_initialize(int argc, VALUE* argv, VALUE self)
754
1347
  {
755
1348
  png_decoder_t* ptr;
756
- VALUE opt;
1349
+ VALUE exc;
1350
+ VALUE opts;
1351
+
1352
+ /*
1353
+ * initialize
1354
+ */
1355
+ exc = Qnil;
757
1356
 
758
1357
  /*
759
1358
  * strip object
760
1359
  */
761
- Data_Get_Struct(self, png_decoder_t, ptr);
1360
+ TypedData_Get_Struct(self, png_decoder_t, &png_decoder_data_type, ptr);
762
1361
 
763
1362
  /*
764
1363
  * parse argument
765
1364
  */
766
- rb_scan_args(argc, argv, "01", &opt);
767
- if (opt != Qnil) Check_Type(opt, T_HASH);
1365
+ rb_scan_args(argc, argv, "0:", &opts);
768
1366
 
769
1367
  /*
770
1368
  * set context
771
1369
  */
772
- set_decoder_context(ptr, opt);
1370
+ exc = set_decoder_context(ptr, opts);
1371
+
1372
+ /*
1373
+ * post process
1374
+ */
1375
+ if (RTEST(exc)) rb_exc_raise(exc);
773
1376
 
774
1377
  return Qtrue;
775
1378
  }
776
1379
 
1380
+ static void
1381
+ decode_error(png_structp ctx, png_const_charp msg)
1382
+ {
1383
+ png_decoder_t* ptr;
1384
+
1385
+ ptr = (png_decoder_t*)png_get_error_ptr(ctx);
1386
+
1387
+ ptr->common.error = create_runtime_error("decode error:%s", msg);
1388
+
1389
+ longjmp(png_jmpbuf(ptr->classic.ctx), 1);
1390
+ }
1391
+
1392
+ static void
1393
+ decode_warn(png_structp ctx, png_const_charp msg)
1394
+ {
1395
+ png_decoder_t* ptr;
1396
+
1397
+ ptr = (png_decoder_t*)png_get_error_ptr(ctx);
1398
+
1399
+ if (ptr->common.warn_msg != Qnil) {
1400
+ ptr->common.warn_msg = rb_ary_new();
1401
+ }
1402
+
1403
+ rb_ary_push(ptr->common.warn_msg, rb_str_new_cstr(msg));
1404
+ }
1405
+
1406
+
777
1407
  static void
778
1408
  set_read_context(png_decoder_t* ptr, VALUE data)
779
1409
  {
1410
+ VALUE exc;
780
1411
  png_structp ctx;
781
1412
  png_infop fsi;
782
1413
  png_infop bsi;
783
1414
 
784
- const char* err;
785
-
786
1415
  do {
787
- err = NULL;
1416
+ exc = Qnil;
1417
+ ctx = NULL;
788
1418
  fsi = NULL;
789
1419
  bsi = NULL;
790
1420
 
791
- ctx = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1421
+ ctx = png_create_read_struct(PNG_LIBPNG_VER_STRING,
1422
+ ptr,
1423
+ decode_error,
1424
+ decode_warn);
792
1425
  if (ctx == NULL) {
793
- err = "png_create_read_struct() failed.";
1426
+ exc = create_runtime_error("png_create_read_struct() failed");
794
1427
  break;
795
1428
  }
796
1429
 
797
1430
  fsi = png_create_info_struct(ctx);
1431
+ if (fsi == NULL) {
1432
+ exc = create_runtime_error("png_create_info_struct() failed");
1433
+ break;
1434
+ }
1435
+
798
1436
  bsi = png_create_info_struct(ctx);
799
- if (fsi == NULL || bsi == NULL) {
800
- err = "png_create_info_struct() failed.";
1437
+ if (bsi == NULL) {
1438
+ exc = create_runtime_error("png_create_info_struct() failed");
801
1439
  break;
802
1440
  }
803
1441
 
@@ -809,22 +1447,17 @@ set_read_context(png_decoder_t* ptr, VALUE data)
809
1447
  ptr->classic.io.size = RSTRING_LEN(data);
810
1448
  ptr->classic.io.pos = 0;
811
1449
 
812
- if (setjmp(png_jmpbuf(ptr->classic.ctx))) {
813
- RUNTIME_ERROR("decode error");
814
-
815
- } else {
816
- png_set_read_fn(ptr->classic.ctx,
817
- (png_voidp)&ptr->classic.io,
818
- (png_rw_ptr)mem_io_read_data);
819
- }
1450
+ png_set_read_fn(ptr->classic.ctx,
1451
+ (png_voidp)&ptr->classic.io,
1452
+ (png_rw_ptr)mem_io_read_data);
820
1453
  } while(0);
821
1454
 
822
- if (err != NULL) {
1455
+ if (RTEST(exc)) {
823
1456
  if (ctx != NULL) {
824
1457
  png_destroy_read_struct(&ctx, &fsi, &bsi);
825
1458
  }
826
1459
 
827
- RUNTIME_ERROR(err);
1460
+ rb_exc_raise(exc);
828
1461
  }
829
1462
  }
830
1463
 
@@ -887,7 +1520,7 @@ get_color_type_str(png_decoder_t* ptr)
887
1520
  break;
888
1521
  }
889
1522
 
890
- return rb_str_new_cstr(cstr);
1523
+ return rb_str_freeze(rb_str_new_cstr(cstr));
891
1524
  }
892
1525
 
893
1526
  static VALUE
@@ -911,7 +1544,7 @@ get_interlace_method_str(png_decoder_t* ptr)
911
1544
  break;
912
1545
  }
913
1546
 
914
- return rb_str_new_cstr(cstr);
1547
+ return rb_str_freeze(rb_str_new_cstr(cstr));
915
1548
  }
916
1549
 
917
1550
  static VALUE
@@ -931,7 +1564,7 @@ get_compression_method_str(png_decoder_t* ptr)
931
1564
  break;
932
1565
  }
933
1566
 
934
- return rb_str_new_cstr(cstr);
1567
+ return rb_str_freeze(rb_str_new_cstr(cstr));
935
1568
  }
936
1569
 
937
1570
  static VALUE
@@ -955,7 +1588,7 @@ get_filter_method_str(png_decoder_t* ptr)
955
1588
  break;
956
1589
  }
957
1590
 
958
- return rb_str_new_cstr(cstr);
1591
+ return rb_str_freeze(rb_str_new_cstr(cstr));
959
1592
  }
960
1593
 
961
1594
  static VALUE
@@ -974,12 +1607,17 @@ create_text_meta(png_decoder_t* ptr)
974
1607
  val = rb_str_new(ptr->classic.text[i].text,
975
1608
  ptr->classic.text[i].text_length);
976
1609
 
1610
+ rb_str_freeze(key);
1611
+ rb_str_freeze(val);
1612
+
977
1613
  rb_funcall(key, rb_intern("downcase!"), 0);
978
1614
  rb_funcall(key, rb_intern("gsub!"), 2, rb_str_new2(" "), rb_str_new2("_"));
979
1615
 
980
1616
  rb_hash_aset(ret, rb_to_symbol(key), val);
981
1617
  }
982
1618
 
1619
+ rb_obj_freeze(ret);
1620
+
983
1621
  return ret;
984
1622
  }
985
1623
 
@@ -1000,6 +1638,8 @@ create_time_meta(png_decoder_t* ptr)
1000
1638
 
1001
1639
  rb_funcall(ret, rb_intern("localtime"), 0);
1002
1640
 
1641
+ rb_obj_freeze(ret);
1642
+
1003
1643
  return ret;
1004
1644
  }
1005
1645
 
@@ -1037,6 +1677,8 @@ create_meta(png_decoder_t* ptr)
1037
1677
  DBL2NUM(ptr->classic.file_gamma));
1038
1678
  }
1039
1679
 
1680
+ rb_obj_freeze(ret);
1681
+
1040
1682
  return ret;
1041
1683
  }
1042
1684
 
@@ -1110,9 +1752,11 @@ create_tiny_meta(png_decoder_t* ptr)
1110
1752
  break;
1111
1753
  }
1112
1754
 
1113
- rb_ivar_set(ret, id_pixfmt, rb_str_new_cstr(fmt));
1755
+ rb_ivar_set(ret, id_pixfmt, rb_str_freeze(rb_str_new_cstr(fmt)));
1114
1756
  rb_ivar_set(ret, id_ncompo, INT2FIX(nc));
1115
1757
 
1758
+ rb_obj_freeze(ret);
1759
+
1116
1760
  return ret;
1117
1761
  }
1118
1762
 
@@ -1164,8 +1808,13 @@ read_header_body(VALUE _arg)
1164
1808
  /*
1165
1809
  * read header
1166
1810
  */
1167
- png_read_info(ptr->classic.ctx, ptr->classic.fsi);
1168
- get_header_info(ptr);
1811
+ if (setjmp(png_jmpbuf(ptr->classic.ctx))) {
1812
+ rb_exc_raise(ptr->common.error);
1813
+
1814
+ } else {
1815
+ png_read_info(ptr->classic.ctx, ptr->classic.fsi);
1816
+ get_header_info(ptr);
1817
+ }
1169
1818
 
1170
1819
  return create_meta(ptr);
1171
1820
  }
@@ -1201,7 +1850,7 @@ rb_decoder_read_header(VALUE self, VALUE data)
1201
1850
  /*
1202
1851
  * strip object
1203
1852
  */
1204
- Data_Get_Struct(self, png_decoder_t, ptr);
1853
+ TypedData_Get_Struct(self, png_decoder_t, &png_decoder_data_type, ptr);
1205
1854
 
1206
1855
  /*
1207
1856
  * call read header funciton
@@ -1330,61 +1979,66 @@ decode_classic_api_body(VALUE _arg)
1330
1979
  /*
1331
1980
  * read basic info
1332
1981
  */
1333
- png_read_info(ptr->classic.ctx, ptr->classic.fsi);
1982
+ if (setjmp(png_jmpbuf(ptr->classic.ctx))) {
1983
+ rb_exc_raise(ptr->common.error);
1334
1984
 
1335
- /*
1336
- * gamma correction
1337
- */
1338
- if (!isnan(ptr->common.display_gamma)) {
1339
- if (!png_get_gAMA(ptr->classic.ctx, ptr->classic.fsi, &file_gamma)) {
1340
- file_gamma = 0.45;
1341
- }
1985
+ } else {
1986
+ png_read_info(ptr->classic.ctx, ptr->classic.fsi);
1342
1987
 
1343
- png_set_gamma(ptr->classic.ctx, ptr->common.display_gamma, file_gamma);
1344
- }
1988
+ /*
1989
+ * gamma correction
1990
+ */
1991
+ if (!isnan(ptr->common.display_gamma)) {
1992
+ if (!png_get_gAMA(ptr->classic.ctx, ptr->classic.fsi, &file_gamma)) {
1993
+ file_gamma = 0.45;
1994
+ }
1345
1995
 
1346
- png_read_update_info(ptr->classic.ctx, ptr->classic.fsi);
1996
+ png_set_gamma(ptr->classic.ctx, ptr->common.display_gamma, file_gamma);
1997
+ }
1347
1998
 
1348
- /*
1349
- * get image size
1350
- */
1999
+ png_read_update_info(ptr->classic.ctx, ptr->classic.fsi);
1351
2000
 
1352
- ptr->classic.width = \
1353
- png_get_image_width(ptr->classic.ctx, ptr->classic.fsi);
2001
+ /*
2002
+ * get image size
2003
+ */
1354
2004
 
1355
- ptr->classic.height = \
1356
- png_get_image_height(ptr->classic.ctx, ptr->classic.fsi);
2005
+ ptr->classic.width = \
2006
+ png_get_image_width(ptr->classic.ctx, ptr->classic.fsi);
1357
2007
 
1358
- stride = png_get_rowbytes(ptr->classic.ctx, ptr->classic.fsi);
2008
+ ptr->classic.height = \
2009
+ png_get_image_height(ptr->classic.ctx, ptr->classic.fsi);
1359
2010
 
1360
- /*
1361
- * alloc return memory
1362
- */
1363
- ret = rb_str_buf_new(stride * ptr->classic.height);
1364
- rb_str_set_len(ret, stride * ptr->classic.height);
2011
+ stride = png_get_rowbytes(ptr->classic.ctx, ptr->classic.fsi);
1365
2012
 
1366
- /*
1367
- * alloc rows
1368
- */
1369
- ptr->classic.rows = png_malloc(ptr->classic.ctx,
1370
- ptr->classic.height * sizeof(png_byte*));
1371
- if (ptr->classic.rows == NULL) {
1372
- NOMEMORY_ERROR("no memory");
1373
- }
2013
+ /*
2014
+ * alloc return memory
2015
+ */
2016
+ ret = rb_str_buf_new(stride * ptr->classic.height);
2017
+ rb_str_set_len(ret, stride * ptr->classic.height);
1374
2018
 
1375
- p = (png_byte*)RSTRING_PTR(ret);
1376
- for (i = 0; i < ptr->classic.height; i++) {
1377
- ptr->classic.rows[i] = p;
1378
- p += stride;
1379
- }
2019
+ /*
2020
+ * alloc rows
2021
+ */
2022
+ ptr->classic.rows = png_malloc(ptr->classic.ctx,
2023
+ ptr->classic.height * sizeof(png_byte*));
2024
+ if (ptr->classic.rows == NULL) {
2025
+ NOMEMORY_ERROR("no memory");
2026
+ }
1380
2027
 
1381
- png_read_image(ptr->classic.ctx, ptr->classic.rows);
1382
- png_read_end(ptr->classic.ctx, ptr->classic.fsi);
2028
+ p = (png_byte*)RSTRING_PTR(ret);
2029
+ for (i = 0; i < ptr->classic.height; i++) {
2030
+ ptr->classic.rows[i] = p;
2031
+ p += stride;
2032
+ }
1383
2033
 
1384
- if (ptr->classic.need_meta) {
1385
- get_header_info(ptr);
1386
- rb_ivar_set(ret, id_meta, create_meta(ptr));
1387
- rb_define_singleton_method(ret, "meta", rb_decode_result_meta, 0);
2034
+ png_read_image(ptr->classic.ctx, ptr->classic.rows);
2035
+ png_read_end(ptr->classic.ctx, ptr->classic.fsi);
2036
+
2037
+ if (ptr->classic.need_meta) {
2038
+ get_header_info(ptr);
2039
+ rb_ivar_set(ret, id_meta, create_meta(ptr));
2040
+ rb_define_singleton_method(ret, "meta", rb_decode_result_meta, 0);
2041
+ }
1388
2042
  }
1389
2043
 
1390
2044
  return ret;
@@ -1419,10 +2073,14 @@ rb_decoder_decode(VALUE self, VALUE data)
1419
2073
  */
1420
2074
  Check_Type(data, T_STRING);
1421
2075
 
2076
+ if (png_sig_cmp((png_const_bytep)RSTRING_PTR(data), 0, 8)) {
2077
+ RUNTIME_ERROR("Invalid PNG signature.");
2078
+ }
2079
+
1422
2080
  /*
1423
2081
  * strip object
1424
2082
  */
1425
- Data_Get_Struct(self, png_decoder_t, ptr);
2083
+ TypedData_Get_Struct(self, png_decoder_t, &png_decoder_data_type, ptr);
1426
2084
 
1427
2085
  /*
1428
2086
  * call decode funcs
@@ -1449,6 +2107,10 @@ Init_png()
1449
2107
  {
1450
2108
  int i;
1451
2109
 
2110
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
2111
+ rb_ext_ractor_safe(true);
2112
+ #endif /* defined(HAVE_RB_EXT_RACTOR_SAFE) */
2113
+
1452
2114
  module = rb_define_module("PNG");
1453
2115
 
1454
2116
  encoder_klass = rb_define_class_under(module, "Encoder", rb_cObject);