libpng-ruby 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 01ba3c06d3239eb5638f1a4b316988f36b64e571aa8d571b3e61e46dd6f296f2
4
+ data.tar.gz: 5d6cc04e434eb327aa111a388e4838e2bbc841a9f18b5703589d9c610ac79e9b
5
+ SHA512:
6
+ metadata.gz: 48977b718f74f3df9124a6e8cceba2bacd2d7cf2fbfdce2098585b9cf56ca59f0248ed1477ebe488d854b988408d86a134629c7b3466e4f5fa5f72fed7919ff7
7
+ data.tar.gz: 297bcc6fb934b6a42f7638c421ffe772246a9952670afd795b2e5f4ba4658256826c21650e3df22e177bf9ab82b9fc726c3ff9c7a5c3f22a665313c01ccb591b
data/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in libpng-ruby.gemspec
6
+ gemspec
7
+ gem "rake-compiler"
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Hiroshi Kuwagata
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # libpng-ruby
2
+ libpng interface for ruby.
3
+
4
+ ## Installation
5
+
6
+ ```ruby
7
+ gem 'libpng-ruby'
8
+ ```
9
+
10
+ And then execute:
11
+
12
+ $ bundle
13
+
14
+ Or install it yourself as:
15
+
16
+ $ gem install libpng-ruby
17
+
18
+ ## Usage
19
+
20
+ ### decode sample
21
+
22
+ ```ruby
23
+ require 'png'
24
+
25
+ dec = PNG::Decoder.new(:color_type => :BGR)
26
+
27
+ p dec.read_header(IO.binread("test.png"))
28
+
29
+ raw = dec << IO.binread("test.png")
30
+ p raw.meta
31
+
32
+ IO.binwrite("test.bgr", raw)
33
+ ```
34
+
35
+ #### decode options
36
+ | option | value type | description |
37
+ |---|---|---|
38
+ | :api_type | "simplified" or "classic" | |
39
+ | :pixel_format | String or Symbol | output format<br>(ignored when to use classic API) |
40
+ | :without_meta | Boolean | T.B.D |
41
+ | :display_gamma | Numeric | T.B.D<br>(ignored when to use simplified API) |
42
+
43
+ #### supported output color type
44
+ GRAY GRAYSCALE GA AG RGB BGR RGBA ARGB BGRA ABGR
45
+
46
+ ### encode sample
47
+
48
+ ```ruby
49
+ require 'png'
50
+
51
+ enc = PNG::Encoder.new(640, 480, :color_type => :YCbCr)
52
+
53
+ IO.binwrite("test.png", enc << IO.binread("test.raw"))
54
+ ```
55
+ #### encode options
56
+ | option | value type | description |
57
+ |---|---|---|
58
+ | :pixel_fromat | String or Symbol | input pixel format |
59
+ | :interlace | Boolean | use interlace mode |
60
+ | :compression | Integer or String or Symbol | compression level |
61
+ | :text | Hash | text information |
62
+ | :time | Boolean | with tIME chunk |
63
+ | :gamma | Numeric | file gamma value |
64
+
65
+ #### supported input color type
66
+ GRAY GRASCALE GA RGB RGBA
67
+
68
+ #### available compression level
69
+ ##### Integer
70
+ 0 to 9(0:no compression, 9:best compression).
71
+
72
+ #### String
73
+ NO_COMPRESSION BEST_SPEED BEST_COMPRESSION DEFAULT
74
+
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/test_*.rb']
7
+ end
8
+
9
+ task :default => :spec
data/bin/pnginfo ADDED
@@ -0,0 +1,7 @@
1
+ #! /usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require 'png'
5
+
6
+ ARGF.binmode
7
+ pp PNG.read_header(ARGF.read)
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+
3
+ have_library( "png16")
4
+ have_header( "png.h")
5
+ have_header( "zlib.h")
6
+
7
+ create_makefile( "png/png")
data/ext/png/png.c ADDED
@@ -0,0 +1,1497 @@
1
+ /*
2
+ * PNG encode/decode library for Ruby
3
+ *
4
+ * Copyright (C) 2016 Hiroshi Kuwagata <kgt9221@gmail.com>
5
+ */
6
+
7
+ /*
8
+ * $Id: png.c 67 2016-06-07 06:10:47Z pi $
9
+ */
10
+
11
+ #include <stdio.h>
12
+ #include <stdint.h>
13
+ #include <string.h>
14
+ #include <setjmp.h>
15
+ #include <time.h>
16
+ #include <math.h>
17
+
18
+ #include <png.h>
19
+ #include <zlib.h>
20
+
21
+ #include "ruby.h"
22
+
23
+ #define N(x) (sizeof(x)/sizeof(*x))
24
+
25
+ #define RUNTIME_ERROR(msg) rb_raise(rb_eRuntimeError, (msg))
26
+ #define ARGUMENT_ERROR(msg) rb_raise(rb_eArgError, (msg))
27
+ #define TYPE_ERROR(msg) rb_raise(rb_eTypeError, (msg))
28
+ #define NOMEMORY_ERROR(msg) rb_raise(rb_eNoMemError, (msg))
29
+
30
+ #define API_SIMPLIFIED 1
31
+ #define API_CLASSIC 2
32
+
33
+ #define EQ_STR(val,str) (rb_to_id(val) == rb_intern(str))
34
+ #define EQ_INT(val,n) (FIX2INT(val) == n)
35
+
36
+ static VALUE module;
37
+ static VALUE encoder_klass;
38
+ static VALUE decoder_klass;
39
+ static VALUE meta_klass;
40
+
41
+ static ID id_meta;
42
+ static ID id_stride;
43
+ static ID id_format;
44
+ static ID id_pixfmt;
45
+ static ID id_ncompo;
46
+
47
+ typedef struct {
48
+ uint8_t* ptr;
49
+ size_t size;
50
+ int pos;
51
+ } mem_io_t;
52
+
53
+ typedef struct {
54
+ /*
55
+ * for raw level chunk access
56
+ */
57
+ png_structp ctx; // as 'context'
58
+ png_infop info;
59
+
60
+ png_uint_32 width;
61
+ png_uint_32 height;
62
+
63
+ png_byte** rows;
64
+
65
+ int stride;
66
+ int num_comp;
67
+ int pixels;
68
+ int with_time;
69
+
70
+ int c_type; // as 'color type'
71
+ int i_meth; // as 'interlace method'
72
+ int c_level; // as 'compression level'
73
+ int f_type; // as 'filter type'
74
+
75
+ png_text* text;
76
+ int num_text;
77
+
78
+ double gamma;
79
+ } png_encoder_t;
80
+
81
+ typedef union {
82
+ struct {
83
+ int api_type;
84
+ int format;
85
+ int need_meta;
86
+ double display_gamma;
87
+ } common;
88
+
89
+ struct {
90
+ /*
91
+ * common field
92
+ */
93
+ int api_type;
94
+ int format;
95
+ int need_meta;
96
+ double display_gamma;
97
+
98
+ /*
99
+ * classic api context
100
+ */
101
+ png_structp ctx; // as 'context'
102
+ png_infop fsi; // as 'front side infomation'
103
+ png_infop bsi; // as 'back side infomation'
104
+
105
+ png_uint_32 width;
106
+ png_uint_32 height;
107
+
108
+ png_byte** rows;
109
+
110
+ int depth;
111
+ int c_type; // as 'color type'
112
+ int i_meth; // as 'interlace method'
113
+ int c_meth; // as 'compression method'
114
+ int f_meth; // as 'filter method'
115
+
116
+ mem_io_t io;
117
+
118
+ png_text* text;
119
+ int num_text;
120
+ png_time* time;
121
+ double file_gamma;
122
+ } classic;
123
+
124
+ struct {
125
+ /*
126
+ * common field
127
+ */
128
+ int api_type;
129
+ int format;
130
+ int need_meta;
131
+ double display_gamma;
132
+
133
+ /*
134
+ * simplified api context
135
+ */
136
+ png_image* ctx;
137
+ } simplified;
138
+ } png_decoder_t;
139
+
140
+ static const char* decoder_opt_keys[] ={
141
+ "pixel_format", // alias of "color_type"
142
+ "without_meta", // bool (default: false)
143
+ "api_type", // string ("simplified" or "classic")
144
+ "display_gamma", // float
145
+ };
146
+
147
+ static ID decoder_opt_ids[N(decoder_opt_keys)];
148
+
149
+ static const char* encoder_opt_keys[] ={
150
+ "pixel_format", // alias of "color_type"
151
+ "interlace", // bool (default: false)
152
+ "compression", // int 0~9
153
+ "text", // hash<String,String>
154
+ "time", // bool (default: true)
155
+ "gamma", // float
156
+ };
157
+
158
+ static ID encoder_opt_ids[N(encoder_opt_keys)];
159
+
160
+
161
+ static void
162
+ rb_encoder_free(void* _ptr)
163
+ {
164
+ png_encoder_t* ptr;
165
+ int i;
166
+
167
+ ptr = (png_encoder_t*)_ptr;
168
+
169
+ if (ptr->ctx != NULL) {
170
+ if (ptr->rows != NULL) {
171
+ png_free(ptr->ctx, ptr->rows);
172
+ }
173
+
174
+ png_destroy_write_struct(&ptr->ctx, &ptr->info);
175
+ }
176
+
177
+ 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
+ }
187
+ }
188
+
189
+ free(ptr);
190
+ }
191
+
192
+ static void
193
+ mem_io_write_data(png_structp ctx, png_bytep src, png_size_t size)
194
+ {
195
+ VALUE buf;
196
+
197
+ buf = (VALUE)png_get_io_ptr(ctx);
198
+ rb_str_buf_cat(buf, (const char *)src, size);
199
+ }
200
+
201
+ static void
202
+ mem_io_flush(png_structp ctx)
203
+ {
204
+ // ignore
205
+ }
206
+
207
+ static VALUE
208
+ rb_encoder_alloc(VALUE self)
209
+ {
210
+ png_encoder_t* ptr;
211
+
212
+ ptr = ALLOC(png_encoder_t);
213
+ memset(ptr, 0, sizeof(*ptr));
214
+
215
+ ptr->c_type = PNG_COLOR_TYPE_RGB;
216
+ ptr->i_meth = PNG_INTERLACE_NONE;
217
+ ptr->c_level = Z_DEFAULT_COMPRESSION;
218
+ ptr->f_type = PNG_FILTER_TYPE_BASE;
219
+ ptr->num_comp = 3;
220
+ ptr->with_time = !0;
221
+ ptr->gamma = NAN;
222
+
223
+ return Data_Wrap_Struct(encoder_klass, 0, rb_encoder_free, ptr);
224
+ }
225
+
226
+ static void
227
+ eval_encoder_opt_color_type(png_encoder_t* ptr, VALUE opt)
228
+ {
229
+ if (opt != Qundef) {
230
+ if (EQ_STR(opt, "GRAY") || EQ_STR(opt, "GRAYSCALE")) {
231
+ ptr->c_type = PNG_COLOR_TYPE_GRAY;
232
+ ptr->num_comp = 1;
233
+
234
+ } else if (EQ_STR(opt, "GA")) {
235
+ ptr->c_type = PNG_COLOR_TYPE_GA;
236
+ ptr->num_comp = 2;
237
+
238
+ } else if (EQ_STR(opt, "RGB")) {
239
+ ptr->c_type = PNG_COLOR_TYPE_RGB;
240
+ ptr->num_comp = 3;
241
+
242
+ } else if (EQ_STR(opt, "RGBA")) {
243
+ ptr->c_type = PNG_COLOR_TYPE_RGBA;
244
+ ptr->num_comp = 4;
245
+
246
+ } else {
247
+ ARGUMENT_ERROR(":color_type is invalid value");
248
+ }
249
+ }
250
+ }
251
+
252
+ static void
253
+ eval_encoder_opt_interlace(png_encoder_t* ptr, VALUE opt)
254
+ {
255
+ if (opt != Qundef) {
256
+ ptr->i_meth = (RTEST(opt))? PNG_INTERLACE_ADAM7: PNG_INTERLACE_NONE;
257
+ }
258
+ }
259
+
260
+ static void
261
+ eval_encoder_opt_compression(png_encoder_t* ptr, VALUE opt)
262
+ {
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;
273
+
274
+ case T_STRING:
275
+ case T_SYMBOL:
276
+ if (EQ_STR(opt, "NO_COMPRESSION")) {
277
+ val = Z_NO_COMPRESSION;
278
+
279
+ } else if (EQ_STR(opt, "BEST_SPEED")) {
280
+ val = Z_BEST_SPEED;
281
+
282
+ } else if (EQ_STR(opt, "BEST_COMPRESSION")) {
283
+ val = Z_BEST_COMPRESSION;
284
+
285
+ } else if (EQ_STR(opt, "DEFAULT")) {
286
+ val = Z_DEFAULT_COMPRESSION;
287
+
288
+ } else {
289
+ ARGUMENT_ERROR(":interlace is invalid value");
290
+ }
291
+ break;
292
+
293
+ default:
294
+ TYPE_ERROR(":interlace is not Symbol and String");
295
+ }
296
+
297
+ ptr->c_level = val;
298
+ }
299
+ }
300
+
301
+ static VALUE
302
+ capitalize(VALUE str)
303
+ {
304
+ VALUE tmp;
305
+ int i;
306
+
307
+ tmp = rb_str_split(rb_funcall(str, rb_intern("to_s"), 0), "_");
308
+
309
+ for (i = 0; i < RARRAY_LEN(tmp); i++) {
310
+ rb_funcall(RARRAY_AREF(tmp, i), rb_intern("capitalize!"), 0);
311
+ }
312
+
313
+ return rb_ary_join(tmp, rb_str_new_cstr(" "));
314
+ }
315
+
316
+ static char*
317
+ clone_cstr(VALUE s)
318
+ {
319
+ char* ret;
320
+ size_t sz;
321
+
322
+ sz = RSTRING_LEN(s);
323
+ ret = (char*)malloc(sz + 1);
324
+
325
+ memcpy(ret, RSTRING_PTR(s), sz);
326
+ ret[sz] = '\0';
327
+
328
+ return ret;
329
+ }
330
+
331
+
332
+ static void
333
+ eval_encoder_opt_text(png_encoder_t* ptr, VALUE opt)
334
+ {
335
+ VALUE keys;
336
+ VALUE key;
337
+ VALUE val;
338
+ int i;
339
+ png_text* text;
340
+
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");
386
+ }
387
+ }
388
+ }
389
+
390
+ static void
391
+ eval_encoder_opt_time(png_encoder_t* ptr, VALUE opt)
392
+ {
393
+ if (opt != Qundef) {
394
+ ptr->with_time = RTEST(opt);
395
+ }
396
+ }
397
+
398
+ static void
399
+ eval_encoder_opt_gamma(png_encoder_t* ptr, VALUE opt)
400
+ {
401
+ if (opt != Qundef) {
402
+ ptr->gamma = NUM2DBL(opt);
403
+ }
404
+ }
405
+
406
+ static void
407
+ set_encoder_context(png_encoder_t* ptr, int wd, int ht, VALUE opt)
408
+ {
409
+ png_structp ctx;
410
+ png_infop info;
411
+ VALUE opts[N(encoder_opt_ids)];
412
+
413
+ const char* err;
414
+ png_byte** rows;
415
+
416
+ /*
417
+ * parse options
418
+ */
419
+ rb_get_kwargs(opt, encoder_opt_ids, 0, N(encoder_opt_ids), opts);
420
+
421
+ /*
422
+ * set context
423
+ */
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]);
430
+
431
+ /*
432
+ * create PNG context
433
+ */
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);
440
+ if (ctx == NULL) {
441
+ err = "png_create_read_struct() failed.";
442
+ break;
443
+ }
444
+
445
+ info = png_create_info_struct(ctx);
446
+ if (info == NULL) {
447
+ err = "png_create_info_structt() failed.";
448
+ break;
449
+ }
450
+
451
+ rows = png_malloc(ctx, ht * sizeof(png_byte*));
452
+ if (rows == NULL) {
453
+ err = "png_malloc() failed.";
454
+ break;
455
+ }
456
+
457
+ memset(rows, 0, ht * sizeof(png_byte*));
458
+
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
+
467
+ } while(0);
468
+
469
+ /*
470
+ * post process
471
+ */
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
+ }
480
+ }
481
+ }
482
+
483
+ static VALUE
484
+ rb_encoder_initialize(int argc, VALUE* argv, VALUE self)
485
+ {
486
+ png_encoder_t* ptr;
487
+ VALUE wd;
488
+ VALUE ht;
489
+ VALUE opts;
490
+
491
+ /*
492
+ * strip object
493
+ */
494
+ Data_Get_Struct(self, png_encoder_t, ptr);
495
+
496
+ /*
497
+ * parse argument
498
+ */
499
+ rb_scan_args(argc, argv, "21", &wd, &ht, &opts);
500
+
501
+ Check_Type(wd, T_FIXNUM);
502
+ Check_Type(ht, T_FIXNUM);
503
+ if (opts != Qnil) Check_Type(opts, T_HASH);
504
+
505
+
506
+ /*
507
+ * set context
508
+ */
509
+ set_encoder_context(ptr, FIX2INT(wd), FIX2INT(ht), opts);
510
+
511
+ return self;
512
+ }
513
+
514
+ static VALUE
515
+ rb_encoder_encode(VALUE self, VALUE data)
516
+ {
517
+ VALUE ret;
518
+ png_encoder_t* ptr;
519
+ png_uint_32 i;
520
+ uint8_t* p;
521
+
522
+ /*
523
+ * initialize
524
+ */
525
+ ret = rb_str_buf_new(0);
526
+
527
+ /*
528
+ * strip object
529
+ */
530
+ Data_Get_Struct(self, png_encoder_t, ptr);
531
+
532
+ /*
533
+ * argument check
534
+ */
535
+ 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
+
561
+ time(&tm);
562
+ png_convert_from_time_t(&png_time, tm);
563
+ png_set_tIME(ptr->ctx, ptr->info, &png_time);
564
+ }
565
+
566
+ if (!isnan(ptr->gamma)) {
567
+ png_set_gAMA(ptr->ctx, ptr->info, ptr->gamma);
568
+ }
569
+
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);
576
+
577
+ p = (png_byte*)RSTRING_PTR(data);
578
+ for (i = 0; i < ptr->height; i++) {
579
+ ptr->rows[i] = p;
580
+ p += ptr->stride;
581
+ }
582
+
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);
590
+ }
591
+
592
+ return ret;
593
+ }
594
+
595
+ static void
596
+ mem_io_read_data(png_structp ctx, png_bytep dst, png_size_t rq_size)
597
+ {
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
+ }
609
+ }
610
+
611
+
612
+ static void
613
+ rb_decoder_free(void* _ptr)
614
+ {
615
+ png_decoder_t* ptr;
616
+
617
+ ptr = (png_decoder_t*)_ptr;
618
+
619
+ if (ptr->common.api_type == API_SIMPLIFIED) {
620
+ if (ptr->simplified.ctx) {
621
+ png_image_free(ptr->simplified.ctx);
622
+ xfree(ptr->simplified.ctx);
623
+ }
624
+
625
+ } else {
626
+ if (ptr->classic.ctx != NULL) {
627
+ if (ptr->classic.rows != NULL) {
628
+ png_free(ptr->classic.ctx, ptr->classic.rows);
629
+ }
630
+
631
+ png_destroy_read_struct(&ptr->classic.ctx,
632
+ &ptr->classic.fsi,
633
+ &ptr->classic.bsi);
634
+ }
635
+ }
636
+
637
+ free(ptr);
638
+ }
639
+
640
+ static VALUE
641
+ rb_decoder_alloc(VALUE self)
642
+ {
643
+ png_decoder_t* ptr;
644
+
645
+ ptr = ALLOC(png_decoder_t);
646
+ memset(ptr, 0, sizeof(*ptr));
647
+
648
+ ptr->common.api_type = API_SIMPLIFIED;
649
+ ptr->common.format = PNG_FORMAT_RGB;
650
+ ptr->common.need_meta = !0;
651
+ ptr->common.display_gamma = NAN;
652
+
653
+ return Data_Wrap_Struct(decoder_klass, 0, rb_decoder_free, ptr);
654
+ }
655
+
656
+ static void
657
+ eval_decoder_opt_api_type(png_decoder_t* ptr, VALUE opt)
658
+ {
659
+ int api_type;
660
+
661
+ if (opt != Qundef) {
662
+ if (EQ_STR(opt, "simplified")) {
663
+ api_type = API_SIMPLIFIED;
664
+
665
+ } else if (EQ_STR(opt, "classic")) {
666
+ api_type = API_CLASSIC;
667
+
668
+ } else {
669
+ ARGUMENT_ERROR(":api_type is invalid value");
670
+ }
671
+
672
+ ptr->common.api_type = api_type;
673
+ }
674
+ }
675
+
676
+ static void
677
+ eval_decoder_opt_pixel_format(png_decoder_t* ptr, VALUE opt)
678
+ {
679
+ int format;
680
+
681
+ if (opt != Qundef) {
682
+ if (EQ_STR(opt, "GRAY") || EQ_STR(opt, "GRAYSCALE")) {
683
+ format = PNG_FORMAT_GRAY;
684
+
685
+ } else if (EQ_STR(opt, "GA")) {
686
+ format = PNG_FORMAT_GA;
687
+
688
+ } else if (EQ_STR(opt, "AG")) {
689
+ format = PNG_FORMAT_AG;
690
+
691
+ } else if (EQ_STR(opt, "RGB")) {
692
+ format = PNG_FORMAT_RGB;
693
+
694
+ } else if (EQ_STR(opt, "BGR")) {
695
+ format = PNG_FORMAT_BGR;
696
+
697
+ } else if (EQ_STR(opt, "RGBA")) {
698
+ format = PNG_FORMAT_RGBA;
699
+
700
+ } else if (EQ_STR(opt, "ARGB")) {
701
+ format = PNG_FORMAT_ARGB;
702
+
703
+ } else if (EQ_STR(opt, "BGRA")) {
704
+ format = PNG_FORMAT_BGRA;
705
+
706
+ } else if (EQ_STR(opt, "ABGR")) {
707
+ format = PNG_FORMAT_ABGR;
708
+
709
+ } else {
710
+ ARGUMENT_ERROR(":color_type is invalid value");
711
+ }
712
+
713
+ ptr->common.format = format;
714
+ }
715
+ }
716
+
717
+ static void
718
+ eval_decoder_opt_without_meta(png_decoder_t* ptr, VALUE opt)
719
+ {
720
+ if (opt != Qundef) {
721
+ ptr->common.need_meta = !RTEST(opt);
722
+ }
723
+ }
724
+
725
+ static void
726
+ eval_decoder_opt_display_gamma(png_decoder_t* ptr, VALUE opt)
727
+ {
728
+ if (opt != Qundef) {
729
+ ptr->common.display_gamma = NUM2DBL(opt);
730
+ }
731
+ }
732
+
733
+ static void
734
+ set_decoder_context(png_decoder_t* ptr, VALUE opt)
735
+ {
736
+ VALUE opts[N(decoder_opt_ids)];
737
+
738
+ /*
739
+ * parse options
740
+ */
741
+ rb_get_kwargs(opt, decoder_opt_ids, 0, N(decoder_opt_ids), opts);
742
+
743
+ /*
744
+ * set context
745
+ */
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]);
750
+ }
751
+
752
+ static VALUE
753
+ rb_decoder_initialize(int argc, VALUE* argv, VALUE self)
754
+ {
755
+ png_decoder_t* ptr;
756
+ VALUE opt;
757
+
758
+ /*
759
+ * strip object
760
+ */
761
+ Data_Get_Struct(self, png_decoder_t, ptr);
762
+
763
+ /*
764
+ * parse argument
765
+ */
766
+ rb_scan_args(argc, argv, "01", &opt);
767
+ if (opt != Qnil) Check_Type(opt, T_HASH);
768
+
769
+ /*
770
+ * set context
771
+ */
772
+ set_decoder_context(ptr, opt);
773
+
774
+ return Qtrue;
775
+ }
776
+
777
+ static void
778
+ set_read_context(png_decoder_t* ptr, VALUE data)
779
+ {
780
+ png_structp ctx;
781
+ png_infop fsi;
782
+ png_infop bsi;
783
+
784
+ const char* err;
785
+
786
+ do {
787
+ err = NULL;
788
+ fsi = NULL;
789
+ bsi = NULL;
790
+
791
+ ctx = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
792
+ if (ctx == NULL) {
793
+ err = "png_create_read_struct() failed.";
794
+ break;
795
+ }
796
+
797
+ fsi = png_create_info_struct(ctx);
798
+ bsi = png_create_info_struct(ctx);
799
+ if (fsi == NULL || bsi == NULL) {
800
+ err = "png_create_info_struct() failed.";
801
+ break;
802
+ }
803
+
804
+ ptr->classic.ctx = ctx;
805
+ ptr->classic.fsi = fsi;
806
+ ptr->classic.bsi = bsi;
807
+
808
+ ptr->classic.io.ptr = (uint8_t*)RSTRING_PTR(data);
809
+ ptr->classic.io.size = RSTRING_LEN(data);
810
+ ptr->classic.io.pos = 0;
811
+
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
+ }
820
+ } while(0);
821
+
822
+ if (err != NULL) {
823
+ if (ctx != NULL) {
824
+ png_destroy_read_struct(&ctx, &fsi, &bsi);
825
+ }
826
+
827
+ RUNTIME_ERROR(err);
828
+ }
829
+ }
830
+
831
+ static void
832
+ get_header_info(png_decoder_t* ptr)
833
+ {
834
+ png_get_IHDR(ptr->classic.ctx,
835
+ ptr->classic.fsi,
836
+ &ptr->classic.width,
837
+ &ptr->classic.height,
838
+ &ptr->classic.depth,
839
+ &ptr->classic.c_type,
840
+ &ptr->classic.i_meth,
841
+ &ptr->classic.c_meth,
842
+ &ptr->classic.f_meth);
843
+
844
+ png_get_tIME(ptr->classic.ctx, ptr->classic.fsi, &ptr->classic.time);
845
+
846
+ png_get_text(ptr->classic.ctx,
847
+ ptr->classic.fsi,
848
+ &ptr->classic.text,
849
+ &ptr->classic.num_text);
850
+
851
+ if (!png_get_gAMA(ptr->classic.ctx,
852
+ ptr->classic.fsi, &ptr->classic.file_gamma)) {
853
+ ptr->classic.file_gamma = NAN;
854
+ }
855
+ }
856
+
857
+ static VALUE
858
+ get_color_type_str(png_decoder_t* ptr)
859
+ {
860
+ const char* cstr;
861
+ char tmp[32];
862
+
863
+ switch (ptr->classic.c_type) {
864
+ case PNG_COLOR_TYPE_GRAY:
865
+ cstr = "GRAY";
866
+ break;
867
+
868
+ case PNG_COLOR_TYPE_PALETTE:
869
+ cstr = "PALETTE";
870
+ break;
871
+
872
+ case PNG_COLOR_TYPE_RGB:
873
+ cstr = "RGB";
874
+ break;
875
+
876
+ case PNG_COLOR_TYPE_RGBA:
877
+ cstr = "RGBA";
878
+ break;
879
+
880
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
881
+ cstr = "GA";
882
+ break;
883
+
884
+ default:
885
+ sprintf(tmp, "UNKNOWN(%d)", ptr->classic.c_type);
886
+ cstr = tmp;
887
+ break;
888
+ }
889
+
890
+ return rb_str_new_cstr(cstr);
891
+ }
892
+
893
+ static VALUE
894
+ get_interlace_method_str(png_decoder_t* ptr)
895
+ {
896
+ const char* cstr;
897
+ char tmp[32];
898
+
899
+ switch (ptr->classic.i_meth) {
900
+ case PNG_INTERLACE_NONE:
901
+ cstr = "NONE";
902
+ break;
903
+
904
+ case PNG_INTERLACE_ADAM7:
905
+ cstr = "ADAM7";
906
+ break;
907
+
908
+ default:
909
+ sprintf(tmp, "UNKNOWN(%d)", ptr->classic.i_meth);
910
+ cstr = tmp;
911
+ break;
912
+ }
913
+
914
+ return rb_str_new_cstr(cstr);
915
+ }
916
+
917
+ static VALUE
918
+ get_compression_method_str(png_decoder_t* ptr)
919
+ {
920
+ const char* cstr;
921
+ char tmp[32];
922
+
923
+ switch (ptr->classic.c_meth) {
924
+ case PNG_COMPRESSION_TYPE_BASE:
925
+ cstr = "BASE";
926
+ break;
927
+
928
+ default:
929
+ sprintf(tmp, "UNKNOWN(%d)", ptr->classic.c_meth);
930
+ cstr = tmp;
931
+ break;
932
+ }
933
+
934
+ return rb_str_new_cstr(cstr);
935
+ }
936
+
937
+ static VALUE
938
+ get_filter_method_str(png_decoder_t* ptr)
939
+ {
940
+ const char* cstr;
941
+ char tmp[32];
942
+
943
+ switch (ptr->classic.f_meth) {
944
+ case PNG_FILTER_TYPE_BASE:
945
+ cstr = "BASE";
946
+ break;
947
+
948
+ case PNG_INTRAPIXEL_DIFFERENCING:
949
+ cstr = "INTRAPIXEL_DIFFERENCING";
950
+ break;
951
+
952
+ default:
953
+ sprintf(tmp, "UNKNOWN(%d)", ptr->classic.f_meth);
954
+ cstr = tmp;
955
+ break;
956
+ }
957
+
958
+ return rb_str_new_cstr(cstr);
959
+ }
960
+
961
+ static VALUE
962
+ create_text_meta(png_decoder_t* ptr)
963
+ {
964
+ VALUE ret;
965
+ VALUE key;
966
+ VALUE val;
967
+
968
+ int i;
969
+
970
+ ret = rb_hash_new();
971
+
972
+ for (i = 0; i < ptr->classic.num_text; i++) {
973
+ key = rb_str_new2(ptr->classic.text[i].key);
974
+ val = rb_str_new(ptr->classic.text[i].text,
975
+ ptr->classic.text[i].text_length);
976
+
977
+ rb_funcall(key, rb_intern("downcase!"), 0);
978
+ rb_funcall(key, rb_intern("gsub!"), 2, rb_str_new2(" "), rb_str_new2("_"));
979
+
980
+ rb_hash_aset(ret, rb_to_symbol(key), val);
981
+ }
982
+
983
+ return ret;
984
+ }
985
+
986
+ static VALUE
987
+ create_time_meta(png_decoder_t* ptr)
988
+ {
989
+ VALUE ret;
990
+
991
+ ret = rb_funcall(rb_cTime,
992
+ rb_intern("utc"),
993
+ 6,
994
+ INT2FIX(ptr->classic.time->year),
995
+ INT2FIX(ptr->classic.time->month),
996
+ INT2FIX(ptr->classic.time->day),
997
+ INT2FIX(ptr->classic.time->hour),
998
+ INT2FIX(ptr->classic.time->minute),
999
+ INT2FIX(ptr->classic.time->second));
1000
+
1001
+ rb_funcall(ret, rb_intern("localtime"), 0);
1002
+
1003
+ return ret;
1004
+ }
1005
+
1006
+ static VALUE
1007
+ create_meta(png_decoder_t* ptr)
1008
+ {
1009
+ VALUE ret;
1010
+
1011
+ ret = rb_obj_alloc(meta_klass);
1012
+
1013
+ rb_ivar_set(ret, rb_intern("@width"), INT2FIX(ptr->classic.width));
1014
+ rb_ivar_set(ret, rb_intern("@height"), INT2FIX(ptr->classic.height));
1015
+ rb_ivar_set(ret, rb_intern("@bit_depth"), INT2FIX(ptr->classic.depth));
1016
+ rb_ivar_set(ret, rb_intern("@color_type"), get_color_type_str(ptr));
1017
+
1018
+ rb_ivar_set(ret, rb_intern("@interlace_method"),
1019
+ get_interlace_method_str(ptr));
1020
+
1021
+ rb_ivar_set(ret, rb_intern("@compression_method"),
1022
+ get_compression_method_str(ptr));
1023
+
1024
+ rb_ivar_set(ret, rb_intern("@filter_method"), get_filter_method_str(ptr));
1025
+
1026
+ if (ptr->classic.text) {
1027
+ rb_ivar_set(ret, rb_intern("@text"), create_text_meta(ptr));
1028
+ }
1029
+
1030
+ if (ptr->classic.time) {
1031
+ rb_ivar_set(ret, rb_intern("@time"), create_time_meta(ptr));
1032
+ }
1033
+
1034
+ if (!isnan(ptr->classic.file_gamma)) {
1035
+ rb_ivar_set(ret,
1036
+ rb_intern("@file_gamma"),
1037
+ DBL2NUM(ptr->classic.file_gamma));
1038
+ }
1039
+
1040
+ return ret;
1041
+ }
1042
+
1043
+ static VALUE
1044
+ create_tiny_meta(png_decoder_t* ptr)
1045
+ {
1046
+ VALUE ret;
1047
+ const char* fmt;
1048
+ int nc;
1049
+
1050
+ ret = rb_obj_alloc(meta_klass);
1051
+
1052
+ rb_ivar_set(ret, rb_intern("@width"),
1053
+ INT2FIX(ptr->simplified.ctx->width));
1054
+
1055
+ rb_ivar_set(ret, id_stride,
1056
+ INT2FIX(PNG_IMAGE_ROW_STRIDE(*ptr->simplified.ctx)));
1057
+
1058
+ rb_ivar_set(ret, rb_intern("@height"),
1059
+ INT2FIX(ptr->simplified.ctx->height));
1060
+
1061
+ switch (ptr->common.format) {
1062
+ case PNG_FORMAT_GRAY:
1063
+ fmt = "GRAY";
1064
+ nc = 1;
1065
+ break;
1066
+
1067
+ case PNG_FORMAT_GA:
1068
+ fmt = "GA";
1069
+ nc = 2;
1070
+ break;
1071
+
1072
+ case PNG_FORMAT_AG:
1073
+ fmt = "AG";
1074
+ nc = 2;
1075
+ break;
1076
+
1077
+ case PNG_FORMAT_RGB:
1078
+ fmt = "RGB";
1079
+ nc = 3;
1080
+ break;
1081
+
1082
+ case PNG_FORMAT_BGR:
1083
+ fmt = "BGR";
1084
+ nc = 3;
1085
+ break;
1086
+
1087
+ case PNG_FORMAT_RGBA:
1088
+ fmt = "RGBA";
1089
+ nc = 4;
1090
+ break;
1091
+
1092
+ case PNG_FORMAT_ARGB:
1093
+ fmt = "ARGB";
1094
+ nc = 4;
1095
+ break;
1096
+
1097
+ case PNG_FORMAT_BGRA:
1098
+ fmt = "BGRA";
1099
+ nc = 4;
1100
+ break;
1101
+
1102
+ case PNG_FORMAT_ABGR:
1103
+ fmt = "ABGR";
1104
+ nc = 4;
1105
+ break;
1106
+
1107
+ default:
1108
+ fmt = "unknown";
1109
+ nc = 0;
1110
+ break;
1111
+ }
1112
+
1113
+ rb_ivar_set(ret, id_pixfmt, rb_str_new_cstr(fmt));
1114
+ rb_ivar_set(ret, id_ncompo, INT2FIX(nc));
1115
+
1116
+ return ret;
1117
+ }
1118
+
1119
+ static void
1120
+ clear_read_context(png_decoder_t* ptr)
1121
+ {
1122
+ if (ptr->classic.ctx != NULL) {
1123
+ png_destroy_read_struct(&ptr->classic.ctx,
1124
+ &ptr->classic.fsi,
1125
+ &ptr->classic.bsi);
1126
+
1127
+ ptr->classic.ctx = NULL;
1128
+ ptr->classic.fsi = NULL;
1129
+ ptr->classic.bsi = NULL;
1130
+
1131
+ ptr->classic.text = NULL;
1132
+ ptr->classic.time = NULL;
1133
+ }
1134
+
1135
+ ptr->classic.io.ptr = NULL;
1136
+ ptr->classic.io.size = 0;
1137
+ ptr->classic.io.pos = 0;
1138
+ }
1139
+
1140
+ typedef struct {
1141
+ png_decoder_t* ptr;
1142
+ VALUE data;
1143
+ } read_header_arg_t ;
1144
+
1145
+ static VALUE
1146
+ read_header_body(VALUE _arg)
1147
+ {
1148
+ read_header_arg_t* arg;
1149
+ png_decoder_t* ptr;
1150
+ VALUE data;
1151
+
1152
+ /*
1153
+ * initialize
1154
+ */
1155
+ arg = (read_header_arg_t*)_arg;
1156
+ ptr = arg->ptr;
1157
+ data = arg->data;
1158
+
1159
+ /*
1160
+ * set context
1161
+ */
1162
+ set_read_context(ptr, data);
1163
+
1164
+ /*
1165
+ * read header
1166
+ */
1167
+ png_read_info(ptr->classic.ctx, ptr->classic.fsi);
1168
+ get_header_info(ptr);
1169
+
1170
+ return create_meta(ptr);
1171
+ }
1172
+
1173
+ static VALUE
1174
+ read_header_ensure(VALUE _arg)
1175
+ {
1176
+ clear_read_context((png_decoder_t*)_arg);
1177
+
1178
+ return Qundef;
1179
+ }
1180
+
1181
+ static VALUE
1182
+ rb_decoder_read_header(VALUE self, VALUE data)
1183
+ {
1184
+ VALUE ret;
1185
+ png_decoder_t* ptr;
1186
+ read_header_arg_t arg;
1187
+
1188
+ /*
1189
+ * argument check
1190
+ */
1191
+ Check_Type(data, T_STRING);
1192
+
1193
+ if (RSTRING_LEN(data) < 8) {
1194
+ RUNTIME_ERROR("data too short.");
1195
+ }
1196
+
1197
+ if (png_sig_cmp((png_const_bytep)RSTRING_PTR(data), 0, 8)) {
1198
+ RUNTIME_ERROR("Invalid PNG signature.");
1199
+ }
1200
+
1201
+ /*
1202
+ * strip object
1203
+ */
1204
+ Data_Get_Struct(self, png_decoder_t, ptr);
1205
+
1206
+ /*
1207
+ * call read header funciton
1208
+ */
1209
+ arg.ptr = ptr;
1210
+ arg.data = data;
1211
+
1212
+ ret = rb_ensure(read_header_body, (VALUE)&arg,
1213
+ read_header_ensure, (VALUE)ptr);
1214
+
1215
+ return ret;
1216
+ }
1217
+
1218
+ static VALUE
1219
+ rb_decode_result_meta(VALUE self)
1220
+ {
1221
+ return rb_ivar_get(self, id_meta);
1222
+ }
1223
+
1224
+ typedef struct {
1225
+ png_decoder_t* ptr;
1226
+ VALUE data;
1227
+ } decode_arg_t;
1228
+
1229
+ static VALUE
1230
+ decode_simplified_api_body(VALUE _arg)
1231
+ {
1232
+ VALUE ret;
1233
+
1234
+ decode_arg_t* arg;
1235
+ png_decoder_t* ptr;
1236
+ VALUE data;
1237
+
1238
+ size_t stride;
1239
+ size_t size;
1240
+
1241
+ /*
1242
+ * initialize
1243
+ */
1244
+ arg = (decode_arg_t*)_arg;
1245
+ ptr = arg->ptr;
1246
+ data = arg->data;
1247
+
1248
+ /*
1249
+ * call simplified API
1250
+ */
1251
+ do {
1252
+ ptr->simplified.ctx = (png_imagep)xmalloc(sizeof(png_image));
1253
+ memset(ptr->simplified.ctx, 0, sizeof(png_image));
1254
+
1255
+ ptr->simplified.ctx->version = PNG_IMAGE_VERSION;
1256
+
1257
+ png_image_begin_read_from_memory(ptr->simplified.ctx,
1258
+ RSTRING_PTR(data), RSTRING_LEN(data));
1259
+ if (PNG_IMAGE_FAILED(*ptr->simplified.ctx)) {
1260
+ RUNTIME_ERROR("png_image_begin_read_from_memory() failed");
1261
+ }
1262
+
1263
+ ptr->simplified.ctx->format = ptr->common.format;
1264
+
1265
+ stride = PNG_IMAGE_ROW_STRIDE(*ptr->simplified.ctx);
1266
+ size = PNG_IMAGE_BUFFER_SIZE(*ptr->simplified.ctx, stride);
1267
+ ret = rb_str_buf_new(size);
1268
+ rb_str_set_len(ret, size);
1269
+
1270
+ png_image_finish_read(ptr->simplified.ctx,
1271
+ NULL, RSTRING_PTR(ret), stride, NULL);
1272
+ if (PNG_IMAGE_FAILED(*ptr->simplified.ctx)) {
1273
+ RUNTIME_ERROR("png_image_finish_read() failed");
1274
+ }
1275
+
1276
+ if (ptr->common.need_meta) {
1277
+ rb_ivar_set(ret, id_meta, create_tiny_meta(ptr));
1278
+ rb_define_singleton_method(ret, "meta", rb_decode_result_meta, 0);
1279
+ }
1280
+ } while(0);
1281
+
1282
+ return ret;
1283
+ }
1284
+
1285
+ static VALUE
1286
+ decode_simplified_api_ensure(VALUE _arg)
1287
+ {
1288
+ png_decoder_t* ptr;
1289
+
1290
+ ptr = (png_decoder_t*)_arg;
1291
+
1292
+ if (ptr->simplified.ctx) {
1293
+ png_image_free(ptr->simplified.ctx);
1294
+ xfree(ptr->simplified.ctx);
1295
+ }
1296
+
1297
+ ptr->simplified.ctx = NULL;
1298
+
1299
+ return Qundef;
1300
+ }
1301
+
1302
+ static VALUE
1303
+ decode_classic_api_body(VALUE _arg)
1304
+ {
1305
+ VALUE ret;
1306
+
1307
+ decode_arg_t* arg;
1308
+ png_decoder_t* ptr;
1309
+ VALUE data;
1310
+
1311
+ size_t stride;
1312
+
1313
+ png_uint_32 i;
1314
+ png_byte* p;
1315
+
1316
+ double file_gamma;
1317
+
1318
+ /*
1319
+ * initialize
1320
+ */
1321
+ arg = (decode_arg_t*)_arg;
1322
+ ptr = arg->ptr;
1323
+ data = arg->data;
1324
+
1325
+ /*
1326
+ * set context
1327
+ */
1328
+ set_read_context(ptr, data);
1329
+
1330
+ /*
1331
+ * read basic info
1332
+ */
1333
+ png_read_info(ptr->classic.ctx, ptr->classic.fsi);
1334
+
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
+ }
1342
+
1343
+ png_set_gamma(ptr->classic.ctx, ptr->common.display_gamma, file_gamma);
1344
+ }
1345
+
1346
+ png_read_update_info(ptr->classic.ctx, ptr->classic.fsi);
1347
+
1348
+ /*
1349
+ * get image size
1350
+ */
1351
+
1352
+ ptr->classic.width = \
1353
+ png_get_image_width(ptr->classic.ctx, ptr->classic.fsi);
1354
+
1355
+ ptr->classic.height = \
1356
+ png_get_image_height(ptr->classic.ctx, ptr->classic.fsi);
1357
+
1358
+ stride = png_get_rowbytes(ptr->classic.ctx, ptr->classic.fsi);
1359
+
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);
1365
+
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
+ }
1374
+
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
+ }
1380
+
1381
+ png_read_image(ptr->classic.ctx, ptr->classic.rows);
1382
+ png_read_end(ptr->classic.ctx, ptr->classic.fsi);
1383
+
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);
1388
+ }
1389
+
1390
+ return ret;
1391
+ }
1392
+
1393
+ static VALUE
1394
+ decode_classic_api_ensure(VALUE _arg)
1395
+ {
1396
+ png_decoder_t* ptr;
1397
+
1398
+ ptr = (png_decoder_t*)_arg;
1399
+
1400
+ if (ptr->classic.rows) {
1401
+ png_free(ptr->classic.ctx, ptr->classic.rows);
1402
+ ptr->classic.rows = NULL;
1403
+ }
1404
+
1405
+ clear_read_context(ptr);
1406
+
1407
+ return Qundef;
1408
+ }
1409
+
1410
+ static VALUE
1411
+ rb_decoder_decode(VALUE self, VALUE data)
1412
+ {
1413
+ VALUE ret;
1414
+ png_decoder_t* ptr;
1415
+ decode_arg_t arg;
1416
+
1417
+ /*
1418
+ * argument check
1419
+ */
1420
+ Check_Type(data, T_STRING);
1421
+
1422
+ /*
1423
+ * strip object
1424
+ */
1425
+ Data_Get_Struct(self, png_decoder_t, ptr);
1426
+
1427
+ /*
1428
+ * call decode funcs
1429
+ */
1430
+ arg.ptr = ptr;
1431
+ arg.data = data;
1432
+
1433
+ if (ptr->common.api_type == API_SIMPLIFIED) {
1434
+ ret = rb_ensure(decode_simplified_api_body, (VALUE)&arg,
1435
+ decode_simplified_api_ensure, (VALUE)ptr);
1436
+
1437
+ } else {
1438
+ ret = rb_ensure(decode_classic_api_body, (VALUE)&arg,
1439
+ decode_classic_api_ensure, (VALUE)ptr);
1440
+ }
1441
+
1442
+ return ret;
1443
+ }
1444
+
1445
+ #define DEFINE_SYMBOL(name, str)
1446
+
1447
+ void
1448
+ Init_png()
1449
+ {
1450
+ int i;
1451
+
1452
+ module = rb_define_module("PNG");
1453
+
1454
+ encoder_klass = rb_define_class_under(module, "Encoder", rb_cObject);
1455
+ rb_define_alloc_func(encoder_klass, rb_encoder_alloc);
1456
+ rb_define_method(encoder_klass, "initialize", rb_encoder_initialize, -1);
1457
+ rb_define_method(encoder_klass, "encode", rb_encoder_encode, 1);
1458
+ rb_define_alias(encoder_klass, "compress", "encode");
1459
+ rb_define_alias(encoder_klass, "<<", "encode");
1460
+
1461
+ decoder_klass = rb_define_class_under(module, "Decoder", rb_cObject);
1462
+ rb_define_alloc_func(decoder_klass, rb_decoder_alloc);
1463
+ rb_define_method(decoder_klass, "initialize", rb_decoder_initialize, -1);
1464
+ rb_define_method(decoder_klass, "read_header", rb_decoder_read_header, 1);
1465
+ rb_define_method(decoder_klass, "decode", rb_decoder_decode, 1);
1466
+ rb_define_alias(decoder_klass, "decompress", "decode");
1467
+ rb_define_alias(decoder_klass, "<<", "decode");
1468
+
1469
+ meta_klass = rb_define_class_under(module, "Meta", rb_cObject);
1470
+ rb_define_attr(meta_klass, "width", 1, 0);
1471
+ rb_define_attr(meta_klass, "height", 1, 0);
1472
+ rb_define_attr(meta_klass, "stride", 1, 0);
1473
+ rb_define_attr(meta_klass, "bit_depth", 1, 0);
1474
+ rb_define_attr(meta_klass, "color_type", 1, 0);
1475
+ rb_define_attr(meta_klass, "interlace_method", 1, 0);
1476
+ rb_define_attr(meta_klass, "compression_method", 1, 0);
1477
+ rb_define_attr(meta_klass, "filter_method", 1, 0);
1478
+ rb_define_attr(meta_klass, "pixel_format", 1, 0);
1479
+ rb_define_attr(meta_klass, "num_components", 1, 0);
1480
+ rb_define_attr(meta_klass, "text", 1, 0);
1481
+ rb_define_attr(meta_klass, "time", 1, 0);
1482
+ rb_define_attr(meta_klass, "file_gamma", 1, 0);
1483
+
1484
+ for (i = 0; i < (int)N(encoder_opt_keys); i++) {
1485
+ encoder_opt_ids[i] = rb_intern_const(encoder_opt_keys[i]);
1486
+ }
1487
+
1488
+ for (i = 0; i < (int)N(decoder_opt_keys); i++) {
1489
+ decoder_opt_ids[i] = rb_intern_const(decoder_opt_keys[i]);
1490
+ }
1491
+
1492
+ id_meta = rb_intern_const("@meta");
1493
+ id_stride = rb_intern_const("@stride");
1494
+ id_format = rb_intern_const("@format");
1495
+ id_pixfmt = rb_intern_const("@pixel_format");
1496
+ id_ncompo = rb_intern_const("@num_components");
1497
+ }