libpng-ruby 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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
+ }