libpng-ruby 0.5.4 → 0.6.0

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