libjpeg-ruby 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +7 -0
- data/LICENSE +21 -0
- data/README.md +2 -0
- data/Rakefile +2 -0
- data/ext/jpeg/extconf.rb +6 -0
- data/ext/jpeg/jpeg.c +1506 -0
- data/lib/jpeg/version.rb +3 -0
- data/lib/jpeg.rb +5 -0
- data/libjpeg-ruby.gemspec +37 -0
- metadata +96 -0
data/ext/jpeg/jpeg.c
ADDED
@@ -0,0 +1,1506 @@
|
|
1
|
+
/*
|
2
|
+
* JPEG encode/decode library for Ruby
|
3
|
+
*
|
4
|
+
* Copyright (C) 2015 Hiroshi Kuwagata <kgt9221@gmail.com>
|
5
|
+
*/
|
6
|
+
|
7
|
+
/*
|
8
|
+
* $Id: jpeg.c 159 2017-12-17 18:40:28Z pi $
|
9
|
+
*/
|
10
|
+
|
11
|
+
/*
|
12
|
+
* TODO
|
13
|
+
* libjpegのエラーハンドリングを全くやっていないので
|
14
|
+
* いずれ修正する事
|
15
|
+
*/
|
16
|
+
|
17
|
+
#include <stdio.h>
|
18
|
+
#include <stdint.h>
|
19
|
+
#include <strings.h>
|
20
|
+
#include <setjmp.h>
|
21
|
+
|
22
|
+
#include <jpeglib.h>
|
23
|
+
|
24
|
+
#include "ruby.h"
|
25
|
+
|
26
|
+
#define UNIT_LINES 10
|
27
|
+
|
28
|
+
#ifdef DEFAULT_QUALITY
|
29
|
+
#undef DEFAULT_QUALITY
|
30
|
+
#endif /* defined(DEFAULT_QUALITY) */
|
31
|
+
|
32
|
+
#define DEFAULT_QUALITY 75
|
33
|
+
#define DEFAULT_INPUT_COLOR_SPACE JCS_YCbCr
|
34
|
+
#define DEFAULT_INPUT_COMPONENTS 2
|
35
|
+
|
36
|
+
#define FMT_YUV422 1
|
37
|
+
#define FMT_RGB565 2
|
38
|
+
#define FMT_GRAYSCALE 3
|
39
|
+
#define FMT_YUV 4
|
40
|
+
#define FMT_RGB 5
|
41
|
+
#define FMT_BGR 6
|
42
|
+
#define FMT_RGB32 7
|
43
|
+
#define FMT_BGR32 8
|
44
|
+
|
45
|
+
#define FMT_YVU 20 /* original extend */
|
46
|
+
|
47
|
+
#define N(x) (sizeof(x)/sizeof(*x))
|
48
|
+
|
49
|
+
#define RUNTIME_ERROR(msg) rb_raise(rb_eRuntimeError, (msg))
|
50
|
+
#define ARGUMENT_ERROR(msg) rb_raise(rb_eArgError, (msg))
|
51
|
+
|
52
|
+
#define IS_COLORMAPPED(ci) (((ci)->actual_number_of_colors > 0) &&\
|
53
|
+
((ci)->colormap != NULL) && \
|
54
|
+
((ci)->output_components == 1) && \
|
55
|
+
(((ci)->out_color_components == 1) || \
|
56
|
+
((ci)->out_color_components == 3)))
|
57
|
+
|
58
|
+
#define ALLOC_ARRAY() \
|
59
|
+
((JSAMPARRAY)xmalloc(sizeof(JSAMPROW) * UNIT_LINES))
|
60
|
+
#define ALLOC_ROWS(w,c) \
|
61
|
+
((JSAMPROW)xmalloc(sizeof(JSAMPLE) * (w) * (c) * UNIT_LINES))
|
62
|
+
|
63
|
+
#define EQ_STR(val,str) (rb_to_id(val) == rb_intern(str))
|
64
|
+
#define EQ_INT(val,n) (FIX2INT(val) == n)
|
65
|
+
|
66
|
+
static VALUE module;
|
67
|
+
static VALUE encoder_klass;
|
68
|
+
static VALUE encerr_klass;
|
69
|
+
|
70
|
+
static VALUE decoder_klass;
|
71
|
+
static VALUE meta_klass;
|
72
|
+
static VALUE decerr_klass;
|
73
|
+
|
74
|
+
static ID id_meta;
|
75
|
+
static ID id_width;
|
76
|
+
static ID id_height;
|
77
|
+
static ID id_orig_cs;
|
78
|
+
static ID id_out_cs;
|
79
|
+
static ID id_ncompo;
|
80
|
+
|
81
|
+
static const char* encoder_opts_keys[] = {
|
82
|
+
"pixel_format", // {str}
|
83
|
+
"quality", // {integer}
|
84
|
+
"scale", // {rational} or {float}
|
85
|
+
};
|
86
|
+
|
87
|
+
static ID encoder_opts_ids[N(encoder_opts_keys)];
|
88
|
+
|
89
|
+
typedef struct {
|
90
|
+
int format;
|
91
|
+
int width;
|
92
|
+
int height;
|
93
|
+
|
94
|
+
int data_size;
|
95
|
+
|
96
|
+
struct jpeg_compress_struct cinfo;
|
97
|
+
struct jpeg_error_mgr jerr;
|
98
|
+
|
99
|
+
JSAMPARRAY array;
|
100
|
+
JSAMPROW rows;
|
101
|
+
} jpeg_encode_t;
|
102
|
+
|
103
|
+
static const char* decoder_opts_keys[] = {
|
104
|
+
"pixel_format", // {str}
|
105
|
+
"output_gamma", // {float}
|
106
|
+
"do_fancy_upsampling", // {bool}
|
107
|
+
"do_smoothing", // {bool}
|
108
|
+
"dither", // [{str}MODE, {bool}2PASS, {int}NUM_COLORS]
|
109
|
+
"use_1pass_quantizer", // {bool}
|
110
|
+
"use_external_colormap", // {bool}
|
111
|
+
"use_2pass_quantizer", // {bool}
|
112
|
+
"without_meta", // {bool}
|
113
|
+
"expand_colormap", // {bool}
|
114
|
+
"scale", // {rational} or {float}
|
115
|
+
};
|
116
|
+
|
117
|
+
static ID decoder_opts_ids[N(decoder_opts_keys)];
|
118
|
+
|
119
|
+
typedef struct {
|
120
|
+
struct jpeg_error_mgr jerr;
|
121
|
+
|
122
|
+
char msg[JMSG_LENGTH_MAX+10];
|
123
|
+
jmp_buf jmpbuf;
|
124
|
+
} ext_error_t;
|
125
|
+
|
126
|
+
typedef struct {
|
127
|
+
int format;
|
128
|
+
int need_meta;
|
129
|
+
int expand_colormap;
|
130
|
+
|
131
|
+
J_COLOR_SPACE out_color_space;
|
132
|
+
int scale_num;
|
133
|
+
int scale_denom;
|
134
|
+
int out_color_components;
|
135
|
+
double output_gamma;
|
136
|
+
boolean buffered_image;
|
137
|
+
boolean do_fancy_upsampling;
|
138
|
+
boolean do_block_smoothing;
|
139
|
+
boolean quantize_colors;
|
140
|
+
J_DITHER_MODE dither_mode;
|
141
|
+
boolean two_pass_quantize;
|
142
|
+
int desired_number_of_colors;
|
143
|
+
boolean enable_1pass_quant;
|
144
|
+
boolean enable_external_quant;
|
145
|
+
boolean enable_2pass_quant;
|
146
|
+
|
147
|
+
struct jpeg_decompress_struct cinfo;
|
148
|
+
ext_error_t err_mgr;
|
149
|
+
} jpeg_decode_t;
|
150
|
+
|
151
|
+
static void
|
152
|
+
encode_output_message(j_common_ptr cinfo)
|
153
|
+
{
|
154
|
+
char msg[JMSG_LENGTH_MAX];
|
155
|
+
|
156
|
+
(*cinfo->err->format_message)(cinfo, msg);
|
157
|
+
}
|
158
|
+
|
159
|
+
static void
|
160
|
+
encode_error_exit(j_common_ptr cinfo)
|
161
|
+
{
|
162
|
+
char msg[JMSG_LENGTH_MAX];
|
163
|
+
|
164
|
+
(*cinfo->err->format_message)(cinfo, msg);
|
165
|
+
|
166
|
+
jpeg_destroy_compress((j_compress_ptr)cinfo);
|
167
|
+
rb_raise(encerr_klass, "%s", msg);
|
168
|
+
}
|
169
|
+
|
170
|
+
|
171
|
+
static void
|
172
|
+
rb_encoder_free( void* _ptr)
|
173
|
+
{
|
174
|
+
jpeg_encode_t* ptr;
|
175
|
+
|
176
|
+
ptr = (jpeg_encode_t*)_ptr;
|
177
|
+
|
178
|
+
if (ptr->array != NULL) xfree(ptr->array);
|
179
|
+
if (ptr->rows != NULL) xfree(ptr->rows);
|
180
|
+
|
181
|
+
jpeg_destroy_compress(&ptr->cinfo);
|
182
|
+
|
183
|
+
free(_ptr);
|
184
|
+
}
|
185
|
+
|
186
|
+
static VALUE
|
187
|
+
rb_encoder_alloc(VALUE self)
|
188
|
+
{
|
189
|
+
jpeg_encode_t* ptr;
|
190
|
+
|
191
|
+
ptr = ALLOC(jpeg_encode_t);
|
192
|
+
memset(ptr, 0, sizeof(*ptr));
|
193
|
+
|
194
|
+
return Data_Wrap_Struct(encoder_klass, 0, rb_encoder_free, ptr);
|
195
|
+
}
|
196
|
+
|
197
|
+
static void
|
198
|
+
set_encoder_context(jpeg_encode_t* ptr, int wd, int ht, VALUE opt)
|
199
|
+
{
|
200
|
+
VALUE opts[N(encoder_opts_ids)];
|
201
|
+
int format;
|
202
|
+
int color_space;
|
203
|
+
int components;
|
204
|
+
int data_size;
|
205
|
+
int quality;
|
206
|
+
int scale_num;
|
207
|
+
int scale_denom;
|
208
|
+
int i;
|
209
|
+
|
210
|
+
/*
|
211
|
+
* parse options
|
212
|
+
*/
|
213
|
+
rb_get_kwargs(opt, encoder_opts_ids, 0, N(encoder_opts_ids), opts);
|
214
|
+
|
215
|
+
/*
|
216
|
+
* eval :pixel_format option
|
217
|
+
*/
|
218
|
+
if (opts[0] == Qundef||EQ_STR(opts[0], "YUV422")||EQ_STR(opts[0], "YUYV")) {
|
219
|
+
format = FMT_YUV422;
|
220
|
+
color_space = JCS_YCbCr;
|
221
|
+
components = 3;
|
222
|
+
data_size = (wd * ht * 2);
|
223
|
+
|
224
|
+
} else if (EQ_STR(opts[0], "RGB565")) {
|
225
|
+
format = FMT_RGB565;
|
226
|
+
color_space = JCS_RGB;
|
227
|
+
components = 3;
|
228
|
+
data_size = (wd * ht * 2);
|
229
|
+
|
230
|
+
} else if (EQ_STR(opts[0], "RGB") || EQ_STR(opts[0], "RGB24")) {
|
231
|
+
format = FMT_RGB;
|
232
|
+
color_space = JCS_RGB;
|
233
|
+
components = 3;
|
234
|
+
data_size = (wd * ht * 3);
|
235
|
+
|
236
|
+
} else if (EQ_STR(opts[0], "BGR") || EQ_STR(opts[0], "BGR24")) {
|
237
|
+
format = FMT_BGR;
|
238
|
+
color_space = JCS_EXT_BGR;
|
239
|
+
components = 3;
|
240
|
+
data_size = (wd * ht * 3);
|
241
|
+
|
242
|
+
} else if (EQ_STR(opts[0], "YUV444") || EQ_STR(opts[0], "YCbCr")) {
|
243
|
+
format = FMT_YUV;
|
244
|
+
color_space = JCS_YCbCr;
|
245
|
+
components = 3;
|
246
|
+
data_size = (wd * ht * 3);
|
247
|
+
|
248
|
+
} else if (EQ_STR(opts[0], "RGBX") || EQ_STR(opts[0], "RGB32")) {
|
249
|
+
format = FMT_RGB32;
|
250
|
+
color_space = JCS_EXT_RGBX;
|
251
|
+
components = 4;
|
252
|
+
data_size = (wd * ht * 4);
|
253
|
+
|
254
|
+
|
255
|
+
} else if (EQ_STR(opts[0], "BGRX") || EQ_STR(opts[0], "BGR32")) {
|
256
|
+
format = FMT_BGR32;
|
257
|
+
color_space = JCS_EXT_BGRX;
|
258
|
+
components = 4;
|
259
|
+
data_size = (wd * ht * 4);
|
260
|
+
|
261
|
+
|
262
|
+
} else if (EQ_STR(opts[0], "GRAYSCALE")) {
|
263
|
+
format = FMT_GRAYSCALE;
|
264
|
+
color_space = JCS_GRAYSCALE;
|
265
|
+
components = 1;
|
266
|
+
data_size = (wd * ht);
|
267
|
+
|
268
|
+
} else {
|
269
|
+
ARGUMENT_ERROR("Unsupportd :pixel_format option value.");
|
270
|
+
}
|
271
|
+
|
272
|
+
/*
|
273
|
+
* eval :quality option
|
274
|
+
*/
|
275
|
+
if (opts[1] == Qundef) {
|
276
|
+
quality = DEFAULT_QUALITY;
|
277
|
+
|
278
|
+
} else {
|
279
|
+
if (TYPE(opts[1]) != T_FIXNUM) {
|
280
|
+
ARGUMENT_ERROR("Unsupportd :quality option value.");
|
281
|
+
|
282
|
+
} else {
|
283
|
+
quality = FIX2INT(opts[1]);
|
284
|
+
}
|
285
|
+
|
286
|
+
if (quality < 0) {
|
287
|
+
ARGUMENT_ERROR(":quality value is to little.");
|
288
|
+
|
289
|
+
} else if (quality > 100) {
|
290
|
+
ARGUMENT_ERROR(":quality value is to big.");
|
291
|
+
}
|
292
|
+
}
|
293
|
+
|
294
|
+
/*
|
295
|
+
* eval scale option
|
296
|
+
*/
|
297
|
+
switch (TYPE(opts[2])) {
|
298
|
+
case T_UNDEF:
|
299
|
+
// Nothing
|
300
|
+
break;
|
301
|
+
|
302
|
+
case T_FLOAT:
|
303
|
+
scale_num = 1000;
|
304
|
+
scale_denom = (int)(NUM2DBL(opts[2]) * 1000.0);
|
305
|
+
break;
|
306
|
+
|
307
|
+
case T_RATIONAL:
|
308
|
+
scale_num = FIX2INT(rb_rational_num(opts[2]));
|
309
|
+
scale_denom = FIX2INT(rb_rational_den(opts[2]));
|
310
|
+
break;
|
311
|
+
|
312
|
+
default:
|
313
|
+
ARGUMENT_ERROR("Unsupportd :scale option value.");
|
314
|
+
break;
|
315
|
+
}
|
316
|
+
|
317
|
+
|
318
|
+
/*
|
319
|
+
* set context
|
320
|
+
*/
|
321
|
+
ptr->format = format;
|
322
|
+
ptr->width = wd;
|
323
|
+
ptr->height = ht;
|
324
|
+
ptr->data_size = data_size;
|
325
|
+
ptr->array = ALLOC_ARRAY();
|
326
|
+
ptr->rows = ALLOC_ROWS(wd, components);
|
327
|
+
|
328
|
+
for (i = 0; i < UNIT_LINES; i++) {
|
329
|
+
ptr->array[i] = ptr->rows + (i * components * wd);
|
330
|
+
}
|
331
|
+
|
332
|
+
jpeg_create_compress(&ptr->cinfo);
|
333
|
+
|
334
|
+
ptr->cinfo.err = jpeg_std_error(&ptr->jerr);
|
335
|
+
ptr->jerr.output_message = encode_output_message;
|
336
|
+
ptr->jerr.error_exit = encode_error_exit;
|
337
|
+
|
338
|
+
ptr->cinfo.image_width = wd;
|
339
|
+
ptr->cinfo.image_height = ht;
|
340
|
+
ptr->cinfo.in_color_space = color_space;
|
341
|
+
ptr->cinfo.input_components = components;
|
342
|
+
|
343
|
+
ptr->cinfo.optimize_coding = TRUE;
|
344
|
+
ptr->cinfo.arith_code = TRUE;
|
345
|
+
ptr->cinfo.raw_data_in = FALSE;
|
346
|
+
ptr->cinfo.dct_method = JDCT_FASTEST;
|
347
|
+
|
348
|
+
jpeg_set_defaults(&ptr->cinfo);
|
349
|
+
jpeg_set_quality(&ptr->cinfo, quality, TRUE);
|
350
|
+
jpeg_suppress_tables(&ptr->cinfo, TRUE);
|
351
|
+
}
|
352
|
+
|
353
|
+
static VALUE
|
354
|
+
rb_encoder_initialize(int argc, VALUE *argv, VALUE self)
|
355
|
+
{
|
356
|
+
jpeg_encode_t* ptr;
|
357
|
+
VALUE wd;
|
358
|
+
VALUE ht;
|
359
|
+
VALUE opt;
|
360
|
+
|
361
|
+
/*
|
362
|
+
* initialize
|
363
|
+
*/
|
364
|
+
Data_Get_Struct(self, jpeg_encode_t, ptr);
|
365
|
+
|
366
|
+
/*
|
367
|
+
* parse arguments
|
368
|
+
*/
|
369
|
+
rb_scan_args(argc, argv, "21", &wd, &ht, &opt);
|
370
|
+
|
371
|
+
Check_Type(wd, T_FIXNUM);
|
372
|
+
Check_Type(ht, T_FIXNUM);
|
373
|
+
if (opt != Qnil) Check_Type(opt, T_HASH);
|
374
|
+
|
375
|
+
/*
|
376
|
+
* set context
|
377
|
+
*/
|
378
|
+
set_encoder_context(ptr, FIX2INT(wd), FIX2INT(ht), opt);
|
379
|
+
|
380
|
+
return Qtrue;
|
381
|
+
}
|
382
|
+
|
383
|
+
static int
|
384
|
+
push_rows_yuv422(JSAMPROW rows, int wd, uint8_t* data, int nrow)
|
385
|
+
{
|
386
|
+
int size;
|
387
|
+
int i;
|
388
|
+
|
389
|
+
size = wd * nrow;
|
390
|
+
|
391
|
+
for (i = 0; i < size; i += 2) {
|
392
|
+
rows[0] = data[0];
|
393
|
+
rows[1] = data[1];
|
394
|
+
rows[2] = data[3];
|
395
|
+
rows[3] = data[2];
|
396
|
+
rows[4] = data[1];
|
397
|
+
rows[5] = data[3];
|
398
|
+
|
399
|
+
rows += 6;
|
400
|
+
data += 4;
|
401
|
+
}
|
402
|
+
|
403
|
+
return (size * 2);
|
404
|
+
}
|
405
|
+
|
406
|
+
static int
|
407
|
+
push_rows_rgb565(JSAMPROW rows, int wd, uint8_t* data, int nrow)
|
408
|
+
{
|
409
|
+
int size;
|
410
|
+
int i;
|
411
|
+
|
412
|
+
size = wd * nrow;
|
413
|
+
|
414
|
+
for (i = 0; i < size; i += 1) {
|
415
|
+
rows[0] = data[1] & 0xf8;
|
416
|
+
rows[1] = ((data[1] << 5) & 0xe0) | ((data[0] >> 3) & 0x1c);
|
417
|
+
rows[2] = (data[0] << 3) & 0xf8;
|
418
|
+
|
419
|
+
rows += 3;
|
420
|
+
data += 2;
|
421
|
+
}
|
422
|
+
|
423
|
+
return (size * 2);
|
424
|
+
}
|
425
|
+
|
426
|
+
static int
|
427
|
+
push_rows_comp3(JSAMPROW rows, int wd, uint8_t* data, int nrow)
|
428
|
+
{
|
429
|
+
int size;
|
430
|
+
|
431
|
+
size = wd * nrow * 3;
|
432
|
+
memcpy(rows, data, size);
|
433
|
+
|
434
|
+
return size;
|
435
|
+
}
|
436
|
+
|
437
|
+
static int
|
438
|
+
push_rows_comp4(JSAMPROW rows, int wd, uint8_t* data, int nrow)
|
439
|
+
{
|
440
|
+
int size;
|
441
|
+
|
442
|
+
size = wd * nrow * 4;
|
443
|
+
memcpy(rows, data, size);
|
444
|
+
|
445
|
+
return size;
|
446
|
+
}
|
447
|
+
|
448
|
+
|
449
|
+
|
450
|
+
static int
|
451
|
+
push_rows_grayscale(JSAMPROW rows, int wd, uint8_t* data, int nrow)
|
452
|
+
{
|
453
|
+
int size;
|
454
|
+
|
455
|
+
size = wd * nrow;
|
456
|
+
memcpy(rows, data, size);
|
457
|
+
|
458
|
+
return size;
|
459
|
+
}
|
460
|
+
|
461
|
+
static int
|
462
|
+
push_rows(jpeg_encode_t* ptr, uint8_t* data, int nrow)
|
463
|
+
{
|
464
|
+
int ret;
|
465
|
+
|
466
|
+
switch (ptr->format) {
|
467
|
+
case FMT_YUV422:
|
468
|
+
ret = push_rows_yuv422(ptr->rows, ptr->width, data, nrow);
|
469
|
+
break;
|
470
|
+
|
471
|
+
case FMT_RGB565:
|
472
|
+
ret = push_rows_rgb565(ptr->rows, ptr->width, data, nrow);
|
473
|
+
break;
|
474
|
+
|
475
|
+
case FMT_YUV:
|
476
|
+
case FMT_RGB:
|
477
|
+
case FMT_BGR:
|
478
|
+
ret = push_rows_comp3(ptr->rows, ptr->width, data, nrow);
|
479
|
+
break;
|
480
|
+
|
481
|
+
case FMT_RGB32:
|
482
|
+
case FMT_BGR32:
|
483
|
+
ret = push_rows_comp4(ptr->rows, ptr->width, data, nrow);
|
484
|
+
break;
|
485
|
+
|
486
|
+
case FMT_GRAYSCALE:
|
487
|
+
ret = push_rows_grayscale(ptr->rows, ptr->width, data, nrow);
|
488
|
+
break;
|
489
|
+
|
490
|
+
default:
|
491
|
+
RUNTIME_ERROR("Really?");
|
492
|
+
}
|
493
|
+
|
494
|
+
return ret;
|
495
|
+
}
|
496
|
+
|
497
|
+
static VALUE
|
498
|
+
do_encode(jpeg_encode_t* ptr, uint8_t* data)
|
499
|
+
{
|
500
|
+
VALUE ret;
|
501
|
+
|
502
|
+
unsigned char* buf;
|
503
|
+
unsigned long buf_size;
|
504
|
+
int nrow;
|
505
|
+
|
506
|
+
buf = NULL;
|
507
|
+
|
508
|
+
|
509
|
+
jpeg_mem_dest(&ptr->cinfo, &buf, &buf_size);
|
510
|
+
|
511
|
+
jpeg_start_compress(&ptr->cinfo, TRUE);
|
512
|
+
|
513
|
+
while (ptr->cinfo.next_scanline < ptr->cinfo.image_height) {
|
514
|
+
nrow = ptr->cinfo.image_height - ptr->cinfo.next_scanline;
|
515
|
+
if (nrow > UNIT_LINES) nrow = UNIT_LINES;
|
516
|
+
|
517
|
+
data += push_rows(ptr, data, nrow);
|
518
|
+
jpeg_write_scanlines(&ptr->cinfo, ptr->array, nrow);
|
519
|
+
}
|
520
|
+
|
521
|
+
jpeg_finish_compress(&ptr->cinfo);
|
522
|
+
|
523
|
+
/*
|
524
|
+
* create return data
|
525
|
+
*/
|
526
|
+
ret = rb_str_buf_new(buf_size);
|
527
|
+
rb_str_set_len(ret, buf_size);
|
528
|
+
|
529
|
+
memcpy(RSTRING_PTR(ret), buf, buf_size);
|
530
|
+
|
531
|
+
/*
|
532
|
+
* post process
|
533
|
+
*/
|
534
|
+
free(buf);
|
535
|
+
|
536
|
+
return ret;
|
537
|
+
}
|
538
|
+
|
539
|
+
static VALUE
|
540
|
+
rb_encoder_encode(VALUE self, VALUE data)
|
541
|
+
{
|
542
|
+
VALUE ret;
|
543
|
+
jpeg_encode_t* ptr;
|
544
|
+
|
545
|
+
/*
|
546
|
+
* initialize
|
547
|
+
*/
|
548
|
+
Data_Get_Struct(self, jpeg_encode_t, ptr);
|
549
|
+
|
550
|
+
/*
|
551
|
+
* argument check
|
552
|
+
*/
|
553
|
+
Check_Type(data, T_STRING);
|
554
|
+
|
555
|
+
if (RSTRING_LEN(data) < ptr->data_size) {
|
556
|
+
ARGUMENT_ERROR("raw image data is too short.");
|
557
|
+
}
|
558
|
+
|
559
|
+
if (RSTRING_LEN(data) > ptr->data_size) {
|
560
|
+
ARGUMENT_ERROR("raw image data is too large.");
|
561
|
+
}
|
562
|
+
|
563
|
+
/*
|
564
|
+
* do encode
|
565
|
+
*/
|
566
|
+
ret = do_encode(ptr, (uint8_t*)RSTRING_PTR(data));
|
567
|
+
|
568
|
+
return ret;
|
569
|
+
}
|
570
|
+
|
571
|
+
static void
|
572
|
+
rb_decoder_free(void* ptr)
|
573
|
+
{
|
574
|
+
//jpeg_destroy_decompress(&(((jpeg_decode_t*)ptr)->cinfo));
|
575
|
+
|
576
|
+
free(ptr);
|
577
|
+
}
|
578
|
+
|
579
|
+
static void
|
580
|
+
decode_output_message(j_common_ptr cinfo)
|
581
|
+
{
|
582
|
+
ext_error_t* err;
|
583
|
+
|
584
|
+
err = (ext_error_t*)cinfo->err;
|
585
|
+
|
586
|
+
(*err->jerr.format_message)(cinfo, err->msg);
|
587
|
+
}
|
588
|
+
|
589
|
+
static void
|
590
|
+
decode_emit_message(j_common_ptr cinfo, int msg_level)
|
591
|
+
{
|
592
|
+
ext_error_t* err;
|
593
|
+
|
594
|
+
if (msg_level < 0) {
|
595
|
+
err = (ext_error_t*)cinfo->err;
|
596
|
+
(*err->jerr.format_message)(cinfo, err->msg);
|
597
|
+
/*
|
598
|
+
* 以前はemit_messageが呼ばれるとエラー扱いしていたが、
|
599
|
+
* Logicoolの一部のモデルなどで問題が出るので無視する
|
600
|
+
* ようにした。
|
601
|
+
* また本来であれば、警告表示を行うべきでもあるが一部
|
602
|
+
* のモデルで大量にemitされる場合があるので表示しない
|
603
|
+
* ようにしている。
|
604
|
+
* 問題が発生した際は、最後のメッセージをオブジェクト
|
605
|
+
* のインスタンスとして持たすべき。
|
606
|
+
*/
|
607
|
+
// longjmp(err->jmpbuf, 1);
|
608
|
+
}
|
609
|
+
}
|
610
|
+
|
611
|
+
static void
|
612
|
+
decode_error_exit(j_common_ptr cinfo)
|
613
|
+
{
|
614
|
+
ext_error_t* err;
|
615
|
+
|
616
|
+
err = (ext_error_t*)cinfo->err;
|
617
|
+
(*err->jerr.format_message)(cinfo, err->msg);
|
618
|
+
longjmp(err->jmpbuf, 1);
|
619
|
+
}
|
620
|
+
|
621
|
+
static VALUE
|
622
|
+
rb_decoder_alloc(VALUE self)
|
623
|
+
{
|
624
|
+
jpeg_decode_t* ptr;
|
625
|
+
|
626
|
+
ptr = ALLOC(jpeg_decode_t);
|
627
|
+
memset(ptr, 0, sizeof(*ptr));
|
628
|
+
|
629
|
+
ptr->format = FMT_RGB;
|
630
|
+
ptr->need_meta = !0;
|
631
|
+
ptr->expand_colormap = 0;
|
632
|
+
|
633
|
+
ptr->out_color_space = JCS_RGB;
|
634
|
+
|
635
|
+
ptr->scale_num = 1;
|
636
|
+
ptr->scale_denom = 1;
|
637
|
+
|
638
|
+
ptr->out_color_components = 3;
|
639
|
+
ptr->output_gamma = 0.0;
|
640
|
+
ptr->buffered_image = FALSE;
|
641
|
+
ptr->do_fancy_upsampling = FALSE;
|
642
|
+
ptr->do_block_smoothing = FALSE;
|
643
|
+
ptr->quantize_colors = FALSE;
|
644
|
+
ptr->dither_mode = JDITHER_NONE;
|
645
|
+
ptr->desired_number_of_colors = 0;
|
646
|
+
ptr->enable_1pass_quant = FALSE;
|
647
|
+
ptr->enable_external_quant = FALSE;
|
648
|
+
ptr->enable_2pass_quant = FALSE;
|
649
|
+
|
650
|
+
return Data_Wrap_Struct(decoder_klass, 0, rb_decoder_free, ptr);
|
651
|
+
}
|
652
|
+
|
653
|
+
static void
|
654
|
+
eval_decoder_opt_pixel_format(jpeg_decode_t* ptr, VALUE opt)
|
655
|
+
{
|
656
|
+
int format;
|
657
|
+
int color_space;
|
658
|
+
int components;
|
659
|
+
|
660
|
+
if (opt != Qundef) {
|
661
|
+
if(EQ_STR(opt, "RGB") || EQ_STR(opt, "RGB24")) {
|
662
|
+
format = FMT_RGB;
|
663
|
+
color_space = JCS_RGB;
|
664
|
+
components = 3;
|
665
|
+
|
666
|
+
} else if (EQ_STR(opt, "YUV422") || EQ_STR(opt, "YUYV")) {
|
667
|
+
format = FMT_YUV422;
|
668
|
+
color_space = JCS_YCbCr;
|
669
|
+
components = 3;
|
670
|
+
|
671
|
+
} else if (EQ_STR(opt, "RGB565")) {
|
672
|
+
format = FMT_RGB565;
|
673
|
+
color_space = JCS_RGB;
|
674
|
+
components = 3;
|
675
|
+
|
676
|
+
} else if (EQ_STR(opt, "GRAYSCALE")) {
|
677
|
+
format = FMT_GRAYSCALE;
|
678
|
+
color_space = JCS_GRAYSCALE;
|
679
|
+
components = 1;
|
680
|
+
|
681
|
+
} else if (EQ_STR(opt, "YUV444") || EQ_STR(opt, "YCbCr")) {
|
682
|
+
format = FMT_YUV;
|
683
|
+
color_space = JCS_YCbCr;
|
684
|
+
components = 3;
|
685
|
+
|
686
|
+
} else if (EQ_STR(opt, "BGR") || EQ_STR(opt, "BGR24")) {
|
687
|
+
format = FMT_BGR;
|
688
|
+
color_space = JCS_EXT_BGR;
|
689
|
+
components = 3;
|
690
|
+
|
691
|
+
} else if (EQ_STR(opt, "YVU444") || EQ_STR(opt, "YCrCb")) {
|
692
|
+
format = FMT_YVU;
|
693
|
+
color_space = JCS_YCbCr;
|
694
|
+
components = 3;
|
695
|
+
|
696
|
+
} else if (EQ_STR(opt, "RGBX") || EQ_STR(opt, "RGB32")) {
|
697
|
+
format = FMT_RGB32;
|
698
|
+
color_space = JCS_EXT_RGBX;
|
699
|
+
components = 4;
|
700
|
+
|
701
|
+
} else if (EQ_STR(opt, "BGRX") || EQ_STR(opt, "BGR32")) {
|
702
|
+
format = FMT_BGR32;
|
703
|
+
color_space = JCS_EXT_BGRX;
|
704
|
+
components = 4;
|
705
|
+
|
706
|
+
} else {
|
707
|
+
ARGUMENT_ERROR("Unsupportd :pixel_format option value.");
|
708
|
+
}
|
709
|
+
|
710
|
+
ptr->format = format;
|
711
|
+
ptr->out_color_space = color_space;
|
712
|
+
ptr->out_color_components = components;
|
713
|
+
}
|
714
|
+
}
|
715
|
+
|
716
|
+
static void
|
717
|
+
eval_decoder_opt_output_gamma(jpeg_decode_t* ptr, VALUE opt)
|
718
|
+
{
|
719
|
+
switch (TYPE(opt)) {
|
720
|
+
case T_UNDEF:
|
721
|
+
// Nothing
|
722
|
+
break;
|
723
|
+
|
724
|
+
case T_FLOAT:
|
725
|
+
ptr->output_gamma = NUM2DBL(opt);
|
726
|
+
break;
|
727
|
+
|
728
|
+
default:
|
729
|
+
ARGUMENT_ERROR("Unsupportd :output_gamma option value.");
|
730
|
+
break;
|
731
|
+
}
|
732
|
+
}
|
733
|
+
|
734
|
+
static void
|
735
|
+
eval_decoder_opt_do_fancy_upsampling(jpeg_decode_t* ptr, VALUE opt)
|
736
|
+
{
|
737
|
+
switch (TYPE(opt)) {
|
738
|
+
case T_UNDEF:
|
739
|
+
// Nothing
|
740
|
+
break;
|
741
|
+
|
742
|
+
case T_TRUE:
|
743
|
+
ptr->do_fancy_upsampling = TRUE;
|
744
|
+
break;
|
745
|
+
|
746
|
+
case T_FALSE:
|
747
|
+
ptr->do_fancy_upsampling = FALSE;
|
748
|
+
break;
|
749
|
+
|
750
|
+
default:
|
751
|
+
ARGUMENT_ERROR("Unsupportd :do_fancy_up_sampling option value.");
|
752
|
+
break;
|
753
|
+
}
|
754
|
+
}
|
755
|
+
|
756
|
+
static void
|
757
|
+
eval_decoder_opt_do_smoothing(jpeg_decode_t* ptr, VALUE opt)
|
758
|
+
{
|
759
|
+
switch (TYPE(opt)) {
|
760
|
+
case T_UNDEF:
|
761
|
+
// Nothing
|
762
|
+
break;
|
763
|
+
|
764
|
+
case T_TRUE:
|
765
|
+
ptr->do_block_smoothing = TRUE;
|
766
|
+
break;
|
767
|
+
|
768
|
+
case T_FALSE:
|
769
|
+
ptr->do_block_smoothing = FALSE;
|
770
|
+
break;
|
771
|
+
|
772
|
+
default:
|
773
|
+
ARGUMENT_ERROR("Unsupportd :do_smoothing option value.");
|
774
|
+
break;
|
775
|
+
}
|
776
|
+
}
|
777
|
+
|
778
|
+
static void
|
779
|
+
eval_decoder_opt_dither( jpeg_decode_t* ptr, VALUE opt)
|
780
|
+
{
|
781
|
+
VALUE dmode;
|
782
|
+
VALUE pass2;
|
783
|
+
VALUE n_col;
|
784
|
+
|
785
|
+
if (opt != Qundef) {
|
786
|
+
if (TYPE(opt) != T_ARRAY) {
|
787
|
+
ARGUMENT_ERROR("Unsupportd :dither option value.");
|
788
|
+
}
|
789
|
+
|
790
|
+
if (RARRAY_LEN(opt) != 3) {
|
791
|
+
ARGUMENT_ERROR(":dither is illeagal length (shall be 3 entries).");
|
792
|
+
}
|
793
|
+
|
794
|
+
dmode = RARRAY_AREF(opt, 0);
|
795
|
+
pass2 = RARRAY_AREF(opt, 1);
|
796
|
+
n_col = RARRAY_AREF(opt, 2);
|
797
|
+
|
798
|
+
if (EQ_STR(dmode, "NONE")) {
|
799
|
+
ptr->dither_mode = JDITHER_NONE;
|
800
|
+
ptr->quantize_colors = FALSE;
|
801
|
+
|
802
|
+
} else if(EQ_STR(dmode, "ORDERED")) {
|
803
|
+
ptr->dither_mode = JDITHER_ORDERED;
|
804
|
+
ptr->quantize_colors = TRUE;
|
805
|
+
|
806
|
+
} else if(EQ_STR(dmode, "FS")) {
|
807
|
+
ptr->dither_mode = JDITHER_FS;
|
808
|
+
ptr->quantize_colors = TRUE;
|
809
|
+
|
810
|
+
} else {
|
811
|
+
ARGUMENT_ERROR("dither mode is illeagal value.");
|
812
|
+
}
|
813
|
+
|
814
|
+
switch (TYPE(pass2)) {
|
815
|
+
case T_TRUE:
|
816
|
+
ptr->two_pass_quantize = TRUE;
|
817
|
+
break;
|
818
|
+
|
819
|
+
case T_FALSE:
|
820
|
+
ptr->two_pass_quantize = FALSE;
|
821
|
+
break;
|
822
|
+
|
823
|
+
default:
|
824
|
+
ARGUMENT_ERROR( "2pass quantize flag is illeagal value.");
|
825
|
+
}
|
826
|
+
|
827
|
+
if (TYPE(n_col) == T_FIXNUM) {
|
828
|
+
ptr->desired_number_of_colors = FIX2INT(n_col);
|
829
|
+
} else {
|
830
|
+
ARGUMENT_ERROR( "number of dithered colors is illeagal value.");
|
831
|
+
}
|
832
|
+
}
|
833
|
+
}
|
834
|
+
|
835
|
+
static void
|
836
|
+
eval_decoder_opt_use_1pass_quantizer(jpeg_decode_t* ptr, VALUE opt)
|
837
|
+
{
|
838
|
+
switch (TYPE(opt)) {
|
839
|
+
case T_UNDEF:
|
840
|
+
// Nothing
|
841
|
+
break;
|
842
|
+
|
843
|
+
case T_TRUE:
|
844
|
+
ptr->enable_1pass_quant = TRUE;
|
845
|
+
ptr->buffered_image = TRUE;
|
846
|
+
break;
|
847
|
+
|
848
|
+
case T_FALSE:
|
849
|
+
ptr->enable_1pass_quant = FALSE;
|
850
|
+
break;
|
851
|
+
|
852
|
+
default:
|
853
|
+
ARGUMENT_ERROR("Unsupportd :use_1pass_quantizer option value.");
|
854
|
+
break;
|
855
|
+
}
|
856
|
+
}
|
857
|
+
|
858
|
+
static void
|
859
|
+
eval_decoder_opt_use_external_colormap(jpeg_decode_t* ptr, VALUE opt)
|
860
|
+
{
|
861
|
+
switch (TYPE(opt)) {
|
862
|
+
case T_UNDEF:
|
863
|
+
// Nothing
|
864
|
+
break;
|
865
|
+
|
866
|
+
case T_TRUE:
|
867
|
+
ptr->enable_external_quant = TRUE;
|
868
|
+
ptr->buffered_image = TRUE;
|
869
|
+
break;
|
870
|
+
|
871
|
+
case T_FALSE:
|
872
|
+
ptr->enable_external_quant = FALSE;
|
873
|
+
break;
|
874
|
+
|
875
|
+
default:
|
876
|
+
ARGUMENT_ERROR("Unsupportd :use_external_colormap option value.");
|
877
|
+
break;
|
878
|
+
}
|
879
|
+
}
|
880
|
+
|
881
|
+
static void
|
882
|
+
eval_decoder_opt_use_2pass_quantizer(jpeg_decode_t* ptr, VALUE opt)
|
883
|
+
{
|
884
|
+
switch (TYPE(opt)) {
|
885
|
+
case T_UNDEF:
|
886
|
+
// Nothing
|
887
|
+
break;
|
888
|
+
|
889
|
+
case T_TRUE:
|
890
|
+
ptr->enable_2pass_quant = TRUE;
|
891
|
+
ptr->buffered_image = TRUE;
|
892
|
+
break;
|
893
|
+
|
894
|
+
case T_FALSE:
|
895
|
+
ptr->enable_2pass_quant = FALSE;
|
896
|
+
break;
|
897
|
+
|
898
|
+
default:
|
899
|
+
ARGUMENT_ERROR("Unsupportd :use_2pass_quantizer option value.");
|
900
|
+
break;
|
901
|
+
}
|
902
|
+
}
|
903
|
+
|
904
|
+
static void
|
905
|
+
eval_decoder_opt_without_meta(jpeg_decode_t* ptr, VALUE opt)
|
906
|
+
{
|
907
|
+
switch (TYPE(opt)) {
|
908
|
+
case T_UNDEF:
|
909
|
+
// Nothing
|
910
|
+
break;
|
911
|
+
|
912
|
+
case T_TRUE:
|
913
|
+
ptr->need_meta = 0;
|
914
|
+
break;
|
915
|
+
|
916
|
+
case T_FALSE:
|
917
|
+
ptr->need_meta = !0;
|
918
|
+
break;
|
919
|
+
|
920
|
+
default:
|
921
|
+
ARGUMENT_ERROR("Unsupportd :without_meta option value.");
|
922
|
+
break;
|
923
|
+
}
|
924
|
+
}
|
925
|
+
|
926
|
+
static void
|
927
|
+
eval_decoder_opt_expand_colormap(jpeg_decode_t* ptr, VALUE opt)
|
928
|
+
{
|
929
|
+
switch (TYPE(opt)) {
|
930
|
+
case T_UNDEF:
|
931
|
+
// Nothing
|
932
|
+
break;
|
933
|
+
|
934
|
+
case T_TRUE:
|
935
|
+
ptr->expand_colormap = !0;
|
936
|
+
break;
|
937
|
+
|
938
|
+
case T_FALSE:
|
939
|
+
ptr->expand_colormap = 0;
|
940
|
+
break;
|
941
|
+
|
942
|
+
default:
|
943
|
+
ARGUMENT_ERROR("Unsupportd :exapnd_colormap option value.");
|
944
|
+
break;
|
945
|
+
}
|
946
|
+
}
|
947
|
+
|
948
|
+
static void
|
949
|
+
eval_decoder_opt_scale(jpeg_decode_t* ptr, VALUE opt)
|
950
|
+
{
|
951
|
+
switch (TYPE(opt)) {
|
952
|
+
case T_UNDEF:
|
953
|
+
// Nothing
|
954
|
+
break;
|
955
|
+
|
956
|
+
case T_FLOAT:
|
957
|
+
ptr->scale_num = 1000;
|
958
|
+
ptr->scale_denom = (int)(NUM2DBL(opt) * 1000.0);
|
959
|
+
break;
|
960
|
+
|
961
|
+
case T_RATIONAL:
|
962
|
+
ptr->scale_num = FIX2INT(rb_rational_num(opt));
|
963
|
+
ptr->scale_denom = FIX2INT(rb_rational_den(opt));
|
964
|
+
break;
|
965
|
+
|
966
|
+
default:
|
967
|
+
ARGUMENT_ERROR("Unsupportd :exapnd_colormap option value.");
|
968
|
+
break;
|
969
|
+
}
|
970
|
+
}
|
971
|
+
|
972
|
+
static void
|
973
|
+
set_decoder_context( jpeg_decode_t* ptr, VALUE opt)
|
974
|
+
{
|
975
|
+
VALUE opts[N(decoder_opts_ids)];
|
976
|
+
|
977
|
+
/*
|
978
|
+
* parse options
|
979
|
+
*/
|
980
|
+
rb_get_kwargs( opt, decoder_opts_ids, 0, N(decoder_opts_ids), opts);
|
981
|
+
|
982
|
+
/*
|
983
|
+
* set context
|
984
|
+
*/
|
985
|
+
eval_decoder_opt_pixel_format(ptr, opts[0]);
|
986
|
+
eval_decoder_opt_output_gamma(ptr, opts[1]);
|
987
|
+
eval_decoder_opt_do_fancy_upsampling(ptr, opts[2]);
|
988
|
+
eval_decoder_opt_do_smoothing(ptr, opts[3]);
|
989
|
+
eval_decoder_opt_dither(ptr, opts[4]);
|
990
|
+
eval_decoder_opt_use_1pass_quantizer(ptr, opts[5]);
|
991
|
+
eval_decoder_opt_use_external_colormap(ptr, opts[6]);
|
992
|
+
eval_decoder_opt_use_2pass_quantizer(ptr, opts[7]);
|
993
|
+
|
994
|
+
eval_decoder_opt_without_meta(ptr, opts[8]);
|
995
|
+
eval_decoder_opt_expand_colormap(ptr, opts[9]);
|
996
|
+
|
997
|
+
eval_decoder_opt_scale(ptr, opts[10]);
|
998
|
+
}
|
999
|
+
|
1000
|
+
static VALUE
|
1001
|
+
rb_decoder_initialize( int argc, VALUE *argv, VALUE self)
|
1002
|
+
{
|
1003
|
+
jpeg_decode_t* ptr;
|
1004
|
+
VALUE opt;
|
1005
|
+
|
1006
|
+
/*
|
1007
|
+
* initialize
|
1008
|
+
*/
|
1009
|
+
Data_Get_Struct(self, jpeg_decode_t, ptr);
|
1010
|
+
|
1011
|
+
/*
|
1012
|
+
* parse arguments
|
1013
|
+
*/
|
1014
|
+
rb_scan_args( argc, argv, "01", &opt);
|
1015
|
+
|
1016
|
+
if (opt != Qnil) Check_Type(opt, T_HASH);
|
1017
|
+
|
1018
|
+
/*
|
1019
|
+
* set context
|
1020
|
+
*/
|
1021
|
+
set_decoder_context(ptr, opt);
|
1022
|
+
|
1023
|
+
return Qtrue;
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
static VALUE
|
1027
|
+
rb_decoder_set(VALUE self, VALUE opt)
|
1028
|
+
{
|
1029
|
+
VALUE ret;
|
1030
|
+
jpeg_decode_t* ptr;
|
1031
|
+
|
1032
|
+
/*
|
1033
|
+
* initialize
|
1034
|
+
*/
|
1035
|
+
Data_Get_Struct(self, jpeg_decode_t, ptr);
|
1036
|
+
|
1037
|
+
/*
|
1038
|
+
* check argument
|
1039
|
+
*/
|
1040
|
+
Check_Type( opt, T_HASH);
|
1041
|
+
|
1042
|
+
/*
|
1043
|
+
* set context
|
1044
|
+
*/
|
1045
|
+
set_decoder_context(ptr, opt);
|
1046
|
+
|
1047
|
+
return Qtrue;
|
1048
|
+
}
|
1049
|
+
|
1050
|
+
|
1051
|
+
|
1052
|
+
static VALUE
|
1053
|
+
get_colorspace_str( J_COLOR_SPACE cs)
|
1054
|
+
{
|
1055
|
+
const char* cstr;
|
1056
|
+
|
1057
|
+
switch (cs) {
|
1058
|
+
case JCS_GRAYSCALE:
|
1059
|
+
cstr = "GRAYSCALE";
|
1060
|
+
break;
|
1061
|
+
|
1062
|
+
case JCS_RGB:
|
1063
|
+
cstr = "RGB";
|
1064
|
+
break;
|
1065
|
+
|
1066
|
+
case JCS_YCbCr:
|
1067
|
+
cstr = "YCbCr";
|
1068
|
+
break;
|
1069
|
+
|
1070
|
+
case JCS_CMYK:
|
1071
|
+
cstr = "CMYK";
|
1072
|
+
break;
|
1073
|
+
|
1074
|
+
case JCS_YCCK:
|
1075
|
+
cstr = "YCCK";
|
1076
|
+
break;
|
1077
|
+
#if JPEG_LIB_VERSION < 90
|
1078
|
+
case JCS_EXT_RGB:
|
1079
|
+
cstr = "RGB";
|
1080
|
+
break;
|
1081
|
+
|
1082
|
+
case JCS_EXT_RGBX:
|
1083
|
+
cstr = "RGBX";
|
1084
|
+
break;
|
1085
|
+
|
1086
|
+
case JCS_EXT_BGR:
|
1087
|
+
cstr = "BGR";
|
1088
|
+
break;
|
1089
|
+
|
1090
|
+
case JCS_EXT_BGRX:
|
1091
|
+
cstr = "BGRX";
|
1092
|
+
break;
|
1093
|
+
|
1094
|
+
case JCS_EXT_XBGR:
|
1095
|
+
cstr = "XBGR";
|
1096
|
+
break;
|
1097
|
+
|
1098
|
+
case JCS_EXT_XRGB:
|
1099
|
+
cstr = "XRGB";
|
1100
|
+
break;
|
1101
|
+
|
1102
|
+
case JCS_EXT_RGBA:
|
1103
|
+
cstr = "RGBA";
|
1104
|
+
break;
|
1105
|
+
|
1106
|
+
case JCS_EXT_BGRA:
|
1107
|
+
cstr = "BGRA";
|
1108
|
+
break;
|
1109
|
+
|
1110
|
+
case JCS_EXT_ABGR:
|
1111
|
+
cstr = "ABGR";
|
1112
|
+
break;
|
1113
|
+
|
1114
|
+
case JCS_EXT_ARGB:
|
1115
|
+
cstr = "ARGB";
|
1116
|
+
break;
|
1117
|
+
#endif /* JPEG_LIB_VERSION < 90 */
|
1118
|
+
|
1119
|
+
default:
|
1120
|
+
cstr = "UNKNOWN";
|
1121
|
+
break;
|
1122
|
+
}
|
1123
|
+
|
1124
|
+
return rb_str_new_cstr(cstr);
|
1125
|
+
}
|
1126
|
+
|
1127
|
+
static VALUE
|
1128
|
+
create_meta(jpeg_decode_t* ptr)
|
1129
|
+
{
|
1130
|
+
VALUE ret;
|
1131
|
+
struct jpeg_decompress_struct* cinfo;
|
1132
|
+
|
1133
|
+
/* TODO: そのうちディザをかけた場合のカラーマップをメタで返すようにする */
|
1134
|
+
|
1135
|
+
ret = rb_obj_alloc( meta_klass);
|
1136
|
+
cinfo = &ptr->cinfo;
|
1137
|
+
|
1138
|
+
rb_ivar_set(ret, id_width, INT2FIX(cinfo->output_width));
|
1139
|
+
rb_ivar_set(ret, id_height, INT2FIX(cinfo->output_height));
|
1140
|
+
rb_ivar_set(ret, id_orig_cs, get_colorspace_str(cinfo->jpeg_color_space));
|
1141
|
+
|
1142
|
+
if (ptr->format == FMT_YVU) {
|
1143
|
+
rb_ivar_set(ret, id_out_cs, rb_str_new_cstr("YCrCb"));
|
1144
|
+
} else {
|
1145
|
+
rb_ivar_set(ret, id_out_cs, get_colorspace_str(cinfo->out_color_space));
|
1146
|
+
}
|
1147
|
+
|
1148
|
+
rb_ivar_set(ret, id_ncompo, INT2FIX(cinfo->output_components));
|
1149
|
+
|
1150
|
+
return ret;
|
1151
|
+
}
|
1152
|
+
|
1153
|
+
static VALUE
|
1154
|
+
do_read_header(jpeg_decode_t* ptr, uint8_t* jpg, size_t jpg_sz)
|
1155
|
+
{
|
1156
|
+
VALUE ret;
|
1157
|
+
|
1158
|
+
switch (ptr->format) {
|
1159
|
+
case FMT_YUV422:
|
1160
|
+
case FMT_RGB565:
|
1161
|
+
RUNTIME_ERROR( "Not implement");
|
1162
|
+
break;
|
1163
|
+
}
|
1164
|
+
|
1165
|
+
jpeg_create_decompress(&ptr->cinfo);
|
1166
|
+
|
1167
|
+
ptr->cinfo.err = jpeg_std_error(&ptr->err_mgr.jerr);
|
1168
|
+
ptr->err_mgr.jerr.output_message = decode_output_message;
|
1169
|
+
ptr->err_mgr.jerr.emit_message = decode_emit_message;
|
1170
|
+
ptr->err_mgr.jerr.error_exit = decode_error_exit;
|
1171
|
+
|
1172
|
+
ptr->cinfo.raw_data_out = FALSE;
|
1173
|
+
ptr->cinfo.dct_method = JDCT_FLOAT;
|
1174
|
+
|
1175
|
+
if (setjmp(ptr->err_mgr.jmpbuf)) {
|
1176
|
+
jpeg_destroy_decompress(&ptr->cinfo);
|
1177
|
+
rb_raise(decerr_klass, "%s", ptr->err_mgr.msg);
|
1178
|
+
} else {
|
1179
|
+
jpeg_mem_src(&ptr->cinfo, jpg, jpg_sz);
|
1180
|
+
jpeg_read_header(&ptr->cinfo, TRUE);
|
1181
|
+
jpeg_calc_output_dimensions(&ptr->cinfo);
|
1182
|
+
|
1183
|
+
ret = create_meta(ptr);
|
1184
|
+
|
1185
|
+
jpeg_destroy_decompress(&ptr->cinfo);
|
1186
|
+
}
|
1187
|
+
|
1188
|
+
return ret;
|
1189
|
+
}
|
1190
|
+
|
1191
|
+
static VALUE
|
1192
|
+
rb_decoder_read_header(VALUE self, VALUE data)
|
1193
|
+
{
|
1194
|
+
VALUE ret;
|
1195
|
+
jpeg_decode_t* ptr;
|
1196
|
+
|
1197
|
+
/*
|
1198
|
+
* initialize
|
1199
|
+
*/
|
1200
|
+
Data_Get_Struct(self, jpeg_decode_t, ptr);
|
1201
|
+
|
1202
|
+
/*
|
1203
|
+
* argument check
|
1204
|
+
*/
|
1205
|
+
Check_Type(data, T_STRING);
|
1206
|
+
|
1207
|
+
/*
|
1208
|
+
* do encode
|
1209
|
+
*/
|
1210
|
+
ret = do_read_header(ptr, (uint8_t*)RSTRING_PTR(data), RSTRING_LEN(data));
|
1211
|
+
|
1212
|
+
return ret;
|
1213
|
+
}
|
1214
|
+
|
1215
|
+
static VALUE
|
1216
|
+
rb_decode_result_meta(VALUE self)
|
1217
|
+
{
|
1218
|
+
return rb_ivar_get(self, id_meta);
|
1219
|
+
}
|
1220
|
+
|
1221
|
+
static void
|
1222
|
+
add_meta(VALUE obj, jpeg_decode_t* ptr)
|
1223
|
+
{
|
1224
|
+
VALUE meta;
|
1225
|
+
|
1226
|
+
meta = create_meta(ptr);
|
1227
|
+
|
1228
|
+
rb_ivar_set(obj, id_meta, meta);
|
1229
|
+
rb_define_singleton_method(obj, "meta", rb_decode_result_meta, 0);
|
1230
|
+
}
|
1231
|
+
|
1232
|
+
static VALUE
|
1233
|
+
expand_colormap(struct jpeg_decompress_struct* cinfo, uint8_t* src)
|
1234
|
+
{
|
1235
|
+
/*
|
1236
|
+
* 本関数はcinfo->out_color_componentsが1または3であることを前提に
|
1237
|
+
* 作成されています。
|
1238
|
+
*/
|
1239
|
+
|
1240
|
+
VALUE ret;
|
1241
|
+
int i;
|
1242
|
+
int n;
|
1243
|
+
uint8_t* dst;
|
1244
|
+
JSAMPARRAY map;
|
1245
|
+
|
1246
|
+
n = cinfo->output_width * cinfo->output_height;
|
1247
|
+
ret = rb_str_buf_new(n * cinfo->out_color_components);
|
1248
|
+
dst = (uint8_t*)RSTRING_PTR(ret);
|
1249
|
+
map = cinfo->colormap;
|
1250
|
+
|
1251
|
+
switch (cinfo->out_color_components) {
|
1252
|
+
case 1:
|
1253
|
+
for (i = 0; i < n; i++) {
|
1254
|
+
dst[i] = map[0][src[i]];
|
1255
|
+
}
|
1256
|
+
break;
|
1257
|
+
|
1258
|
+
case 3:
|
1259
|
+
for (i = 0; i < n; i++) {
|
1260
|
+
dst[0] = map[0][src[i]];
|
1261
|
+
dst[1] = map[1][src[i]];
|
1262
|
+
dst[2] = map[2][src[i]];
|
1263
|
+
|
1264
|
+
dst += 3;
|
1265
|
+
}
|
1266
|
+
break;
|
1267
|
+
|
1268
|
+
default:
|
1269
|
+
RUNTIME_ERROR("Really?");
|
1270
|
+
}
|
1271
|
+
|
1272
|
+
rb_str_set_len(ret, n * cinfo->out_color_components);
|
1273
|
+
|
1274
|
+
return ret;
|
1275
|
+
}
|
1276
|
+
|
1277
|
+
static void
|
1278
|
+
swap_cbcr(uint8_t* p, size_t size)
|
1279
|
+
{
|
1280
|
+
int i;
|
1281
|
+
uint8_t tmp;
|
1282
|
+
|
1283
|
+
for (i = 0; i < size; i++) {
|
1284
|
+
tmp = p[1];
|
1285
|
+
p[1] = p[2];
|
1286
|
+
p[2] = tmp;
|
1287
|
+
}
|
1288
|
+
}
|
1289
|
+
|
1290
|
+
static VALUE
|
1291
|
+
do_decode(jpeg_decode_t* ptr, uint8_t* jpg, size_t jpg_sz)
|
1292
|
+
{
|
1293
|
+
VALUE ret;
|
1294
|
+
struct jpeg_decompress_struct* cinfo;
|
1295
|
+
JSAMPARRAY array;
|
1296
|
+
|
1297
|
+
size_t stride;
|
1298
|
+
size_t raw_sz;
|
1299
|
+
uint8_t* raw;
|
1300
|
+
int i;
|
1301
|
+
int j;
|
1302
|
+
|
1303
|
+
ret = Qundef; // warning対策
|
1304
|
+
cinfo = &ptr->cinfo;
|
1305
|
+
array = ALLOC_ARRAY();
|
1306
|
+
|
1307
|
+
switch (ptr->format) {
|
1308
|
+
case FMT_YUV422:
|
1309
|
+
case FMT_RGB565:
|
1310
|
+
RUNTIME_ERROR( "Not implement");
|
1311
|
+
break;
|
1312
|
+
|
1313
|
+
case FMT_GRAYSCALE:
|
1314
|
+
case FMT_YUV:
|
1315
|
+
case FMT_RGB:
|
1316
|
+
case FMT_BGR:
|
1317
|
+
case FMT_YVU:
|
1318
|
+
case FMT_RGB32:
|
1319
|
+
case FMT_BGR32:
|
1320
|
+
jpeg_create_decompress(cinfo);
|
1321
|
+
|
1322
|
+
cinfo->err = jpeg_std_error(&ptr->err_mgr.jerr);
|
1323
|
+
ptr->err_mgr.jerr.output_message = decode_output_message;
|
1324
|
+
ptr->err_mgr.jerr.emit_message = decode_emit_message;
|
1325
|
+
ptr->err_mgr.jerr.error_exit = decode_error_exit;
|
1326
|
+
|
1327
|
+
if (setjmp(ptr->err_mgr.jmpbuf)) {
|
1328
|
+
jpeg_abort_decompress(cinfo);
|
1329
|
+
jpeg_destroy_decompress(&ptr->cinfo);
|
1330
|
+
|
1331
|
+
free(array);
|
1332
|
+
rb_raise(decerr_klass, "%s", ptr->err_mgr.msg);
|
1333
|
+
|
1334
|
+
} else {
|
1335
|
+
jpeg_mem_src(cinfo, jpg, jpg_sz);
|
1336
|
+
jpeg_read_header(cinfo, TRUE);
|
1337
|
+
|
1338
|
+
cinfo->raw_data_out = FALSE;
|
1339
|
+
cinfo->dct_method = JDCT_FLOAT;
|
1340
|
+
|
1341
|
+
cinfo->out_color_space = ptr->out_color_space;
|
1342
|
+
cinfo->out_color_components = ptr->out_color_components;
|
1343
|
+
cinfo->scale_num = ptr->scale_num;
|
1344
|
+
cinfo->scale_denom = ptr->scale_denom;
|
1345
|
+
cinfo->output_gamma = ptr->output_gamma;
|
1346
|
+
cinfo->do_fancy_upsampling = ptr->do_fancy_upsampling;
|
1347
|
+
cinfo->do_block_smoothing = ptr->do_block_smoothing;
|
1348
|
+
cinfo->quantize_colors = ptr->quantize_colors;
|
1349
|
+
cinfo->dither_mode = ptr->dither_mode;
|
1350
|
+
cinfo->two_pass_quantize = ptr->two_pass_quantize;
|
1351
|
+
cinfo->desired_number_of_colors = ptr->desired_number_of_colors;
|
1352
|
+
cinfo->enable_1pass_quant = ptr->enable_1pass_quant;
|
1353
|
+
cinfo->enable_external_quant = ptr->enable_external_quant;
|
1354
|
+
cinfo->enable_2pass_quant = ptr->enable_2pass_quant;
|
1355
|
+
|
1356
|
+
jpeg_calc_output_dimensions(cinfo);
|
1357
|
+
jpeg_start_decompress(cinfo);
|
1358
|
+
|
1359
|
+
stride = cinfo->output_components * cinfo->output_width;
|
1360
|
+
raw_sz = stride * cinfo->output_height;
|
1361
|
+
ret = rb_str_buf_new(raw_sz);
|
1362
|
+
raw = (uint8_t*)RSTRING_PTR(ret);
|
1363
|
+
|
1364
|
+
|
1365
|
+
while (cinfo->output_scanline < cinfo->output_height) {
|
1366
|
+
for (i = 0, j = cinfo->output_scanline; i < UNIT_LINES; i++, j++) {
|
1367
|
+
array[i] = raw + (j * stride);
|
1368
|
+
}
|
1369
|
+
|
1370
|
+
jpeg_read_scanlines(cinfo, array, UNIT_LINES);
|
1371
|
+
}
|
1372
|
+
|
1373
|
+
if (ptr->expand_colormap && IS_COLORMAPPED( cinfo)) {
|
1374
|
+
ret = expand_colormap(cinfo, raw);
|
1375
|
+
} else {
|
1376
|
+
rb_str_set_len( ret, raw_sz);
|
1377
|
+
}
|
1378
|
+
|
1379
|
+
if (ptr->need_meta) add_meta(ret, ptr);
|
1380
|
+
if (ptr->format == FMT_YVU) swap_cbcr(raw, raw_sz);
|
1381
|
+
|
1382
|
+
jpeg_finish_decompress(cinfo);
|
1383
|
+
jpeg_destroy_decompress(&ptr->cinfo);
|
1384
|
+
}
|
1385
|
+
break;
|
1386
|
+
}
|
1387
|
+
|
1388
|
+
free(array);
|
1389
|
+
|
1390
|
+
return ret;
|
1391
|
+
}
|
1392
|
+
|
1393
|
+
static VALUE
|
1394
|
+
rb_decoder_decode(VALUE self, VALUE data)
|
1395
|
+
{
|
1396
|
+
VALUE ret;
|
1397
|
+
jpeg_decode_t* ptr;
|
1398
|
+
|
1399
|
+
/*
|
1400
|
+
* initialize
|
1401
|
+
*/
|
1402
|
+
Data_Get_Struct(self, jpeg_decode_t, ptr);
|
1403
|
+
|
1404
|
+
/*
|
1405
|
+
* argument check
|
1406
|
+
*/
|
1407
|
+
Check_Type(data, T_STRING);
|
1408
|
+
|
1409
|
+
/*
|
1410
|
+
* do encode
|
1411
|
+
*/
|
1412
|
+
ret = do_decode(ptr, (uint8_t*)RSTRING_PTR(data), RSTRING_LEN(data));
|
1413
|
+
|
1414
|
+
return ret;
|
1415
|
+
}
|
1416
|
+
|
1417
|
+
static VALUE
|
1418
|
+
rb_test_image(VALUE self, VALUE data)
|
1419
|
+
{
|
1420
|
+
VALUE ret;
|
1421
|
+
struct jpeg_decompress_struct cinfo;
|
1422
|
+
ext_error_t err_mgr;
|
1423
|
+
|
1424
|
+
jpeg_create_decompress(&cinfo);
|
1425
|
+
|
1426
|
+
cinfo.err = jpeg_std_error(&err_mgr.jerr);
|
1427
|
+
|
1428
|
+
err_mgr.jerr.output_message = decode_output_message;
|
1429
|
+
err_mgr.jerr.emit_message = decode_emit_message;
|
1430
|
+
err_mgr.jerr.error_exit = decode_error_exit;
|
1431
|
+
|
1432
|
+
cinfo.raw_data_out = FALSE;
|
1433
|
+
cinfo.dct_method = JDCT_FLOAT;
|
1434
|
+
|
1435
|
+
if (setjmp(err_mgr.jmpbuf)) {
|
1436
|
+
ret = Qtrue;
|
1437
|
+
} else {
|
1438
|
+
ret = Qfalse;
|
1439
|
+
|
1440
|
+
jpeg_mem_src(&cinfo, RSTRING_PTR(data), RSTRING_LEN(data));
|
1441
|
+
jpeg_read_header(&cinfo, TRUE);
|
1442
|
+
jpeg_calc_output_dimensions(&cinfo);
|
1443
|
+
}
|
1444
|
+
|
1445
|
+
jpeg_destroy_decompress(&cinfo);
|
1446
|
+
|
1447
|
+
return ret;
|
1448
|
+
}
|
1449
|
+
|
1450
|
+
void
|
1451
|
+
Init_jpeg()
|
1452
|
+
{
|
1453
|
+
int i;
|
1454
|
+
|
1455
|
+
module = rb_define_module("JPEG");
|
1456
|
+
rb_define_singleton_method(module, "broken?", rb_test_image, 1);
|
1457
|
+
|
1458
|
+
encoder_klass = rb_define_class_under(module, "Encoder", rb_cObject);
|
1459
|
+
rb_define_alloc_func(encoder_klass, rb_encoder_alloc);
|
1460
|
+
rb_define_method(encoder_klass, "initialize", rb_encoder_initialize, -1);
|
1461
|
+
rb_define_method(encoder_klass, "encode", rb_encoder_encode, 1);
|
1462
|
+
rb_define_alias(encoder_klass, "compress", "encode");
|
1463
|
+
rb_define_alias(encoder_klass, "<<", "encode");
|
1464
|
+
|
1465
|
+
encerr_klass = rb_define_class_under(module,
|
1466
|
+
"EncodeError", rb_eRuntimeError);
|
1467
|
+
|
1468
|
+
decoder_klass = rb_define_class_under(module, "Decoder", rb_cObject);
|
1469
|
+
rb_define_alloc_func(decoder_klass, rb_decoder_alloc);
|
1470
|
+
rb_define_method(decoder_klass, "initialize", rb_decoder_initialize, -1);
|
1471
|
+
rb_define_method(decoder_klass, "set", rb_decoder_set, 1);
|
1472
|
+
rb_define_method(decoder_klass, "read_header", rb_decoder_read_header, 1);
|
1473
|
+
rb_define_method(decoder_klass, "decode", rb_decoder_decode, 1);
|
1474
|
+
rb_define_alias(decoder_klass, "decompress", "decode");
|
1475
|
+
rb_define_alias(decoder_klass, "<<", "decode");
|
1476
|
+
|
1477
|
+
meta_klass = rb_define_class_under(module, "Meta", rb_cObject);
|
1478
|
+
rb_define_attr(meta_klass, "width", 1, 0);
|
1479
|
+
rb_define_attr(meta_klass, "height", 1, 0);
|
1480
|
+
rb_define_attr(meta_klass, "original_colorspace", 1, 0);
|
1481
|
+
rb_define_attr(meta_klass, "output_colorspace", 1, 0);
|
1482
|
+
rb_define_attr(meta_klass, "num_components", 1, 0);
|
1483
|
+
|
1484
|
+
decerr_klass = rb_define_class_under(module,
|
1485
|
+
"DecodeError", rb_eRuntimeError);
|
1486
|
+
|
1487
|
+
/*
|
1488
|
+
* 必要になる都度ID計算をさせるとコストがかかるので、本ライブラリで使用
|
1489
|
+
* するID値の計算を先に済ませておく
|
1490
|
+
* 但し、可読性を優先して随時計算する箇所を残しているので注意すること。
|
1491
|
+
*/
|
1492
|
+
for (i = 0; i < (int)N(encoder_opts_keys); i++) {
|
1493
|
+
encoder_opts_ids[i] = rb_intern_const(encoder_opts_keys[i]);
|
1494
|
+
}
|
1495
|
+
|
1496
|
+
for (i = 0; i < (int)N(decoder_opts_keys); i++) {
|
1497
|
+
decoder_opts_ids[i] = rb_intern_const(decoder_opts_keys[i]);
|
1498
|
+
}
|
1499
|
+
|
1500
|
+
id_meta = rb_intern_const("@meta");
|
1501
|
+
id_width = rb_intern_const("@width");
|
1502
|
+
id_height = rb_intern_const("@height");
|
1503
|
+
id_orig_cs = rb_intern_const("@original_colorspace");
|
1504
|
+
id_out_cs = rb_intern_const("@output_colorspace");
|
1505
|
+
id_ncompo = rb_intern_const("@num_components");
|
1506
|
+
}
|