libpng-ruby 0.5.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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);