image_pack 0.2.2 → 0.2.4
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 +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +108 -119
- data/ext/image_pack/extconf.rb +36 -125
- data/ext/image_pack/image_pack.c +736 -614
- data/ext/image_pack/mozjpeg_sources.rb +178 -0
- data/ext/image_pack/vendor/mozjpeg/BUILDING.md +744 -0
- data/ext/image_pack/vendor/mozjpeg/CODE_OF_CONDUCT.md +15 -0
- data/ext/image_pack/vendor/mozjpeg/ChangeLog.md +1996 -0
- data/lib/image_pack/configuration.rb +54 -8
- data/lib/image_pack/version.rb +1 -1
- data/lib/image_pack.rb +87 -25
- metadata +14 -79
- data/ext/image_pack/vendor/mozjpeg/README.ijg +0 -258
- data/ext/image_pack/vendor/mozjpeg/cdjpeg.c +0 -156
- data/ext/image_pack/vendor/mozjpeg/cjpeg.c +0 -961
- data/ext/image_pack/vendor/mozjpeg/djpeg.c +0 -855
- data/ext/image_pack/vendor/mozjpeg/jaricom.c +0 -157
- data/ext/image_pack/vendor/mozjpeg/jcarith.c +0 -972
- data/ext/image_pack/vendor/mozjpeg/jcstest.c +0 -126
- data/ext/image_pack/vendor/mozjpeg/jdarith.c +0 -782
- data/ext/image_pack/vendor/mozjpeg/jdatadst-tj.c +0 -198
- data/ext/image_pack/vendor/mozjpeg/jdatasrc-tj.c +0 -194
- data/ext/image_pack/vendor/mozjpeg/jpegtran.c +0 -827
- data/ext/image_pack/vendor/mozjpeg/jpegyuv.c +0 -172
- data/ext/image_pack/vendor/mozjpeg/rdbmp.c +0 -690
- data/ext/image_pack/vendor/mozjpeg/rdcolmap.c +0 -253
- data/ext/image_pack/vendor/mozjpeg/rdgif.c +0 -720
- data/ext/image_pack/vendor/mozjpeg/rdjpeg.c +0 -160
- data/ext/image_pack/vendor/mozjpeg/rdjpgcom.c +0 -494
- data/ext/image_pack/vendor/mozjpeg/rdpng.c +0 -194
- data/ext/image_pack/vendor/mozjpeg/rdppm.c +0 -781
- data/ext/image_pack/vendor/mozjpeg/rdswitch.c +0 -642
- data/ext/image_pack/vendor/mozjpeg/rdtarga.c +0 -508
- data/ext/image_pack/vendor/mozjpeg/simd/arm/aarch32/jccolext-neon.c +0 -148
- data/ext/image_pack/vendor/mozjpeg/simd/arm/aarch32/jchuff-neon.c +0 -334
- data/ext/image_pack/vendor/mozjpeg/simd/arm/aarch32/jsimd.c +0 -976
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jsimd.c +0 -1312
- data/ext/image_pack/vendor/mozjpeg/simd/mips/jsimd.c +0 -1143
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jccolext-mmi.c +0 -455
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jccolor-mmi.c +0 -148
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jcgray-mmi.c +0 -132
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jcgryext-mmi.c +0 -374
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jcsample-mmi.c +0 -98
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jdcolext-mmi.c +0 -415
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jdcolor-mmi.c +0 -139
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jdmerge-mmi.c +0 -149
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jdmrgext-mmi.c +0 -615
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jdsample-mmi.c +0 -304
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jfdctfst-mmi.c +0 -255
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jfdctint-mmi.c +0 -398
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jidctfst-mmi.c +0 -395
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jidctint-mmi.c +0 -571
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jquanti-mmi.c +0 -124
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jsimd.c +0 -866
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jccolext-altivec.c +0 -269
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jccolor-altivec.c +0 -116
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jcgray-altivec.c +0 -111
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jcgryext-altivec.c +0 -228
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jcsample-altivec.c +0 -159
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jdcolext-altivec.c +0 -276
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jdcolor-altivec.c +0 -106
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jdmerge-altivec.c +0 -130
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jdmrgext-altivec.c +0 -329
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jdsample-altivec.c +0 -400
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jfdctfst-altivec.c +0 -154
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jfdctint-altivec.c +0 -258
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jidctfst-altivec.c +0 -255
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jidctint-altivec.c +0 -357
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jquanti-altivec.c +0 -250
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jsimd.c +0 -884
- data/ext/image_pack/vendor/mozjpeg/strtest.c +0 -170
- data/ext/image_pack/vendor/mozjpeg/tjbench.c +0 -1044
- data/ext/image_pack/vendor/mozjpeg/tjexample.c +0 -406
- data/ext/image_pack/vendor/mozjpeg/tjunittest.c +0 -961
- data/ext/image_pack/vendor/mozjpeg/tjutil.c +0 -70
- data/ext/image_pack/vendor/mozjpeg/transupp.c +0 -2373
- data/ext/image_pack/vendor/mozjpeg/turbojpeg-jni.c +0 -1259
- data/ext/image_pack/vendor/mozjpeg/turbojpeg.c +0 -2320
- data/ext/image_pack/vendor/mozjpeg/wrbmp.c +0 -552
- data/ext/image_pack/vendor/mozjpeg/wrgif.c +0 -580
- data/ext/image_pack/vendor/mozjpeg/wrjpgcom.c +0 -577
- data/ext/image_pack/vendor/mozjpeg/wrppm.c +0 -366
- data/ext/image_pack/vendor/mozjpeg/wrtarga.c +0 -258
- data/ext/image_pack/vendor/mozjpeg/yuvjpeg.c +0 -268
data/ext/image_pack/image_pack.c
CHANGED
|
@@ -3,18 +3,26 @@
|
|
|
3
3
|
#include <ruby/version.h>
|
|
4
4
|
#include <ruby/encoding.h>
|
|
5
5
|
|
|
6
|
-
#if
|
|
7
|
-
#
|
|
6
|
+
#if defined(RB_NOGVL_OFFLOAD_SAFE)
|
|
7
|
+
#define IMAGE_PACK_HAS_OFFLOAD_SAFE 1
|
|
8
|
+
#else
|
|
9
|
+
#define IMAGE_PACK_HAS_OFFLOAD_SAFE 0
|
|
10
|
+
#define RB_NOGVL_OFFLOAD_SAFE 0
|
|
8
11
|
#endif
|
|
9
12
|
|
|
10
13
|
#include <errno.h>
|
|
11
14
|
#include <setjmp.h>
|
|
12
15
|
#include <stdatomic.h>
|
|
13
16
|
#include <stdint.h>
|
|
17
|
+
#include <inttypes.h>
|
|
14
18
|
#include <limits.h>
|
|
19
|
+
#include <sys/types.h>
|
|
15
20
|
#include <stdio.h>
|
|
16
21
|
#include <stdlib.h>
|
|
17
22
|
#include <string.h>
|
|
23
|
+
#if defined(_WIN32)
|
|
24
|
+
#include <windows.h>
|
|
25
|
+
#endif
|
|
18
26
|
#include <jpeglib.h>
|
|
19
27
|
#include <jconfigint.h>
|
|
20
28
|
|
|
@@ -28,14 +36,14 @@
|
|
|
28
36
|
#endif
|
|
29
37
|
#endif
|
|
30
38
|
|
|
31
|
-
#ifndef RB_NOGVL_OFFLOAD_SAFE
|
|
32
|
-
#error "RB_NOGVL_OFFLOAD_SAFE is required by image_pack"
|
|
33
|
-
#endif
|
|
34
|
-
|
|
35
39
|
#ifndef TRUE
|
|
36
40
|
#define TRUE 1
|
|
37
41
|
#endif
|
|
38
42
|
|
|
43
|
+
#ifndef IMAGE_PACK_MOZJPEG_VERSION
|
|
44
|
+
#define IMAGE_PACK_MOZJPEG_VERSION VERSION
|
|
45
|
+
#endif
|
|
46
|
+
|
|
39
47
|
#ifndef FALSE
|
|
40
48
|
#define FALSE 0
|
|
41
49
|
#endif
|
|
@@ -48,6 +56,32 @@
|
|
|
48
56
|
#define IP_RESTRICT
|
|
49
57
|
#endif
|
|
50
58
|
|
|
59
|
+
#if defined(IMAGE_PACK_HAS_SIMD)
|
|
60
|
+
#define IP_FAST_DCT JDCT_ISLOW
|
|
61
|
+
#else
|
|
62
|
+
#define IP_FAST_DCT JDCT_FASTEST
|
|
63
|
+
#endif
|
|
64
|
+
|
|
65
|
+
#define IP_FAIL(ctx, st, msg) \
|
|
66
|
+
do { \
|
|
67
|
+
ip_context_set_error((ctx), (st), (msg)); \
|
|
68
|
+
return 0; \
|
|
69
|
+
} while (0)
|
|
70
|
+
|
|
71
|
+
#define IP_FAIL_NULL(ctx, st, msg) \
|
|
72
|
+
do { \
|
|
73
|
+
ip_context_set_error((ctx), (st), (msg)); \
|
|
74
|
+
return NULL; \
|
|
75
|
+
} while (0)
|
|
76
|
+
|
|
77
|
+
#define IP_FAIL_GOTO(ctx, st, msg) \
|
|
78
|
+
do { \
|
|
79
|
+
ip_context_set_error((ctx), (st), (msg)); \
|
|
80
|
+
goto fail; \
|
|
81
|
+
} while (0)
|
|
82
|
+
|
|
83
|
+
#define IP_ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
|
|
84
|
+
|
|
51
85
|
typedef enum { IP_ALGO_JPEG_TURBO = 1, IP_ALGO_MOZJPEG = 2 } ip_algo_t;
|
|
52
86
|
|
|
53
87
|
typedef enum {
|
|
@@ -87,11 +121,11 @@ typedef struct {
|
|
|
87
121
|
int height;
|
|
88
122
|
int channels;
|
|
89
123
|
int bit_depth;
|
|
124
|
+
int jpeg_color_space;
|
|
90
125
|
size_t decoded_bytes;
|
|
91
126
|
|
|
92
127
|
unsigned char *output_data;
|
|
93
128
|
size_t output_size;
|
|
94
|
-
size_t output_capacity;
|
|
95
129
|
ip_output_owner_t output_owner;
|
|
96
130
|
char *output_path;
|
|
97
131
|
|
|
@@ -121,13 +155,13 @@ typedef struct {
|
|
|
121
155
|
|
|
122
156
|
atomic_int cancelled;
|
|
123
157
|
int cancellable_requested;
|
|
158
|
+
int strict;
|
|
159
|
+
int warning_count;
|
|
160
|
+
char first_warning[200];
|
|
124
161
|
|
|
125
162
|
jmp_buf jmpbuf;
|
|
126
163
|
int jmp_armed;
|
|
127
164
|
|
|
128
|
-
unsigned char *scratch_row;
|
|
129
|
-
size_t scratch_row_size;
|
|
130
|
-
|
|
131
165
|
struct {
|
|
132
166
|
int marker;
|
|
133
167
|
unsigned char *data;
|
|
@@ -138,7 +172,6 @@ typedef struct {
|
|
|
138
172
|
|
|
139
173
|
unsigned char *transient_jpeg_buf;
|
|
140
174
|
unsigned char *transient_decode_buf;
|
|
141
|
-
unsigned char *transient_scratch_buf;
|
|
142
175
|
int source_orientation;
|
|
143
176
|
} ip_context_t;
|
|
144
177
|
|
|
@@ -148,6 +181,7 @@ typedef struct {
|
|
|
148
181
|
} ip_jpeg_error_mgr;
|
|
149
182
|
|
|
150
183
|
static VALUE rb_mImagePack;
|
|
184
|
+
static int ip_offload_runtime_enabled = 1;
|
|
151
185
|
static VALUE rb_eImagePackError;
|
|
152
186
|
static VALUE rb_eImagePackInvalidArgumentError;
|
|
153
187
|
static VALUE rb_eImagePackInvalidImageError;
|
|
@@ -188,6 +222,9 @@ static int ip_checked_image_size(int width, int height, int channels, size_t *ou
|
|
|
188
222
|
static void ip_validate_quality_or_raise(ip_context_t *ctx);
|
|
189
223
|
static void ip_validate_min_ssim_or_raise(ip_context_t *ctx);
|
|
190
224
|
static int ip_bool_value(VALUE value);
|
|
225
|
+
static int ip_value_negative(VALUE value);
|
|
226
|
+
static char *ip_strdup(const char *source);
|
|
227
|
+
static int ip_replace_file(const char *tmp_path, const char *output_path);
|
|
191
228
|
|
|
192
229
|
static ip_algo_t ip_parse_algo(VALUE sym);
|
|
193
230
|
static ip_execution_t ip_parse_execution(VALUE sym);
|
|
@@ -195,11 +232,15 @@ static ip_input_kind_t ip_parse_input_kind(VALUE sym);
|
|
|
195
232
|
static ip_output_kind_t ip_parse_output_kind(VALUE sym);
|
|
196
233
|
|
|
197
234
|
static int ip_prepare_input_bytes(ip_context_t *ctx, VALUE input, ip_input_kind_t kind);
|
|
198
|
-
static int ip_prepare_pixels(ip_context_t *ctx, VALUE buffer, int width, int height, int channels
|
|
235
|
+
static int ip_prepare_pixels(ip_context_t *ctx, VALUE buffer, int width, int height, int channels,
|
|
236
|
+
int exact_size);
|
|
237
|
+
static int ip_ensure_owned_input_for_async(ip_context_t *ctx, VALUE input, ip_input_kind_t kind);
|
|
238
|
+
static int ip_ensure_owned_pixels_for_async(ip_context_t *ctx, VALUE buffer);
|
|
199
239
|
static int ip_prepare_output_path(ip_context_t *ctx, VALUE output, ip_output_kind_t kind);
|
|
200
240
|
static VALUE ip_finish_output(ip_context_t *ctx, ip_output_kind_t kind);
|
|
241
|
+
static void apply_configuration(VALUE self, ip_context_t *ctx);
|
|
201
242
|
|
|
202
|
-
static int ip_inspect_jpeg_header(ip_context_t *ctx);
|
|
243
|
+
static int ip_inspect_jpeg_header(ip_context_t *ctx, int allow_cmyk_ycck);
|
|
203
244
|
static VALUE ip_inspect_image_entry(VALUE self, VALUE input, VALUE input_kind);
|
|
204
245
|
|
|
205
246
|
static void ip_resolve_execution(ip_context_t *ctx);
|
|
@@ -222,18 +263,20 @@ static int ip_run_optimize_context(ip_context_t *ctx);
|
|
|
222
263
|
typedef struct {
|
|
223
264
|
VALUE self, input, input_kind, output, output_kind, algo, quality, min_ssim;
|
|
224
265
|
VALUE mozjpeg_trellis, progressive, strip_metadata, execution, cancellable, has_scheduler;
|
|
266
|
+
VALUE report, strict;
|
|
225
267
|
ip_context_t *ctx;
|
|
226
268
|
} ip_compress_jpeg_call_t;
|
|
227
269
|
|
|
228
270
|
typedef struct {
|
|
229
271
|
VALUE self, buffer, width, height, channels, output, output_kind, algo, quality, min_ssim;
|
|
230
|
-
VALUE progressive, execution, cancellable, has_scheduler;
|
|
272
|
+
VALUE mozjpeg_trellis, progressive, exact_size, execution, cancellable, has_scheduler;
|
|
273
|
+
VALUE report, strict;
|
|
231
274
|
ip_context_t *ctx;
|
|
232
275
|
} ip_compress_pixels_call_t;
|
|
233
276
|
|
|
234
277
|
typedef struct {
|
|
235
278
|
VALUE self, input, input_kind, output, output_kind, progressive, strip_metadata;
|
|
236
|
-
VALUE execution, cancellable, has_scheduler;
|
|
279
|
+
VALUE execution, cancellable, has_scheduler, strict;
|
|
237
280
|
ip_context_t *ctx;
|
|
238
281
|
} ip_optimize_jpeg_call_t;
|
|
239
282
|
|
|
@@ -254,14 +297,13 @@ static VALUE ip_call_cleanup(VALUE ptr) {
|
|
|
254
297
|
static VALUE ip_compress_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
|
|
255
298
|
VALUE output_kind, VALUE algo, VALUE quality, VALUE min_ssim,
|
|
256
299
|
VALUE mozjpeg_trellis, VALUE progressive, VALUE strip_metadata,
|
|
257
|
-
VALUE execution, VALUE cancellable, VALUE has_scheduler
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
VALUE quality, VALUE min_ssim, VALUE progressive,
|
|
261
|
-
VALUE execution, VALUE cancellable, VALUE has_scheduler);
|
|
300
|
+
VALUE execution, VALUE cancellable, VALUE has_scheduler,
|
|
301
|
+
VALUE report, VALUE strict);
|
|
302
|
+
static VALUE ip_compress_pixels_entry(int argc, VALUE *argv, VALUE self);
|
|
262
303
|
static VALUE ip_optimize_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
|
|
263
304
|
VALUE output_kind, VALUE progressive, VALUE strip_metadata,
|
|
264
|
-
VALUE execution, VALUE cancellable, VALUE has_scheduler
|
|
305
|
+
VALUE execution, VALUE cancellable, VALUE has_scheduler,
|
|
306
|
+
VALUE strict);
|
|
265
307
|
|
|
266
308
|
static VALUE ip_status_to_exception(ip_status_t status) {
|
|
267
309
|
switch (status) {
|
|
@@ -310,10 +352,6 @@ static int ip_checked_image_size(int width, int height, int channels, size_t *ou
|
|
|
310
352
|
return ip_checked_mul_size(pixels, (size_t)channels, out);
|
|
311
353
|
}
|
|
312
354
|
|
|
313
|
-
static void *ip_malloc_hot(size_t size) {
|
|
314
|
-
return malloc(size);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
355
|
static void ip_validate_quality_or_raise(ip_context_t *ctx) {
|
|
318
356
|
if (ctx->quality >= 1 && ctx->quality <= 100)
|
|
319
357
|
return;
|
|
@@ -333,6 +371,67 @@ static void ip_validate_min_ssim_or_raise(ip_context_t *ctx) {
|
|
|
333
371
|
"min_ssim must be Numeric > 0.0 and <= 1.0, got: %.17g", min_ssim);
|
|
334
372
|
}
|
|
335
373
|
|
|
374
|
+
static int ip_value_negative(VALUE value) {
|
|
375
|
+
return RTEST(rb_funcall(value, rb_intern("<"), 1, INT2FIX(0)));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
static char *ip_strdup(const char *source) {
|
|
379
|
+
size_t len = strlen(source) + 1;
|
|
380
|
+
char *copy = (char *)malloc(len);
|
|
381
|
+
if (!copy)
|
|
382
|
+
return NULL;
|
|
383
|
+
memcpy(copy, source, len);
|
|
384
|
+
return copy;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
#if defined(_WIN32)
|
|
388
|
+
static int ip_replace_file(const char *tmp_path, const char *output_path) {
|
|
389
|
+
return MoveFileExA(tmp_path, output_path, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)
|
|
390
|
+
? 0
|
|
391
|
+
: -1;
|
|
392
|
+
}
|
|
393
|
+
#else
|
|
394
|
+
static int ip_replace_file(const char *tmp_path, const char *output_path) {
|
|
395
|
+
return rename(tmp_path, output_path);
|
|
396
|
+
}
|
|
397
|
+
#endif
|
|
398
|
+
|
|
399
|
+
static int ip_file_seek_end(FILE *fp) {
|
|
400
|
+
#if defined(_WIN32)
|
|
401
|
+
return _fseeki64(fp, 0, SEEK_END);
|
|
402
|
+
#else
|
|
403
|
+
return fseeko(fp, 0, SEEK_END);
|
|
404
|
+
#endif
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
static int ip_file_rewind(FILE *fp) {
|
|
408
|
+
#if defined(_WIN32)
|
|
409
|
+
return _fseeki64(fp, 0, SEEK_SET);
|
|
410
|
+
#else
|
|
411
|
+
return fseeko(fp, 0, SEEK_SET);
|
|
412
|
+
#endif
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
static int ip_file_tell(FILE *fp, size_t *out) {
|
|
416
|
+
#if defined(_WIN32)
|
|
417
|
+
__int64 pos = _ftelli64(fp);
|
|
418
|
+
if (pos < 0)
|
|
419
|
+
return 0;
|
|
420
|
+
if ((unsigned long long)pos > (unsigned long long)SIZE_MAX)
|
|
421
|
+
return 0;
|
|
422
|
+
*out = (size_t)pos;
|
|
423
|
+
return 1;
|
|
424
|
+
#else
|
|
425
|
+
off_t pos = ftello(fp);
|
|
426
|
+
if (pos < 0)
|
|
427
|
+
return 0;
|
|
428
|
+
if ((uintmax_t)pos > (uintmax_t)SIZE_MAX)
|
|
429
|
+
return 0;
|
|
430
|
+
*out = (size_t)pos;
|
|
431
|
+
return 1;
|
|
432
|
+
#endif
|
|
433
|
+
}
|
|
434
|
+
|
|
336
435
|
static int ip_bool_value(VALUE value) {
|
|
337
436
|
if (NIL_P(value) || value == Qfalse)
|
|
338
437
|
return 0;
|
|
@@ -371,10 +470,8 @@ static void ip_context_free(ip_context_t *ctx) {
|
|
|
371
470
|
free(ctx->owned_input_data);
|
|
372
471
|
free(ctx->owned_pixel_data);
|
|
373
472
|
free(ctx->output_path);
|
|
374
|
-
free(ctx->scratch_row);
|
|
375
473
|
free(ctx->transient_jpeg_buf);
|
|
376
474
|
free(ctx->transient_decode_buf);
|
|
377
|
-
free(ctx->transient_scratch_buf);
|
|
378
475
|
|
|
379
476
|
if (ctx->preserved_markers) {
|
|
380
477
|
for (size_t i = 0; i < ctx->preserved_marker_count; i++) {
|
|
@@ -407,46 +504,120 @@ static ID symbol_id(VALUE sym, const char *kind) {
|
|
|
407
504
|
return SYM2ID(sym);
|
|
408
505
|
}
|
|
409
506
|
|
|
507
|
+
typedef struct {
|
|
508
|
+
const ID *id;
|
|
509
|
+
int value;
|
|
510
|
+
} ip_symbol_entry;
|
|
511
|
+
|
|
512
|
+
static int ip_map_symbol(VALUE sym, const char *kind, const ip_symbol_entry *table) {
|
|
513
|
+
ID id = symbol_id(sym, kind);
|
|
514
|
+
for (; table->id != NULL; table++) {
|
|
515
|
+
if (id == *table->id)
|
|
516
|
+
return table->value;
|
|
517
|
+
}
|
|
518
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "unknown %s", kind);
|
|
519
|
+
}
|
|
520
|
+
|
|
410
521
|
static ip_algo_t ip_parse_algo(VALUE sym) {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
if (id == id_mozjpeg)
|
|
415
|
-
return IP_ALGO_MOZJPEG;
|
|
416
|
-
rb_raise(rb_eImagePackInvalidArgumentError, "unknown algo");
|
|
522
|
+
static const ip_symbol_entry table[] = {
|
|
523
|
+
{&id_jpeg_turbo, IP_ALGO_JPEG_TURBO}, {&id_mozjpeg, IP_ALGO_MOZJPEG}, {NULL, 0}};
|
|
524
|
+
return (ip_algo_t)ip_map_symbol(sym, "algo", table);
|
|
417
525
|
}
|
|
418
526
|
|
|
419
527
|
static ip_execution_t ip_parse_execution(VALUE sym) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
return IP_EXEC_OFFLOAD;
|
|
427
|
-
if (id == id_auto)
|
|
428
|
-
return IP_EXEC_AUTO;
|
|
429
|
-
rb_raise(rb_eImagePackInvalidArgumentError, "unknown execution");
|
|
528
|
+
static const ip_symbol_entry table[] = {{&id_direct, IP_EXEC_DIRECT},
|
|
529
|
+
{&id_nogvl, IP_EXEC_NOGVL},
|
|
530
|
+
{&id_offload, IP_EXEC_OFFLOAD},
|
|
531
|
+
{&id_auto, IP_EXEC_AUTO},
|
|
532
|
+
{NULL, 0}};
|
|
533
|
+
return (ip_execution_t)ip_map_symbol(sym, "execution", table);
|
|
430
534
|
}
|
|
431
535
|
|
|
432
536
|
static ip_input_kind_t ip_parse_input_kind(VALUE sym) {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
if (id == id_io_buffer)
|
|
439
|
-
return IP_INPUT_IO_BUFFER;
|
|
440
|
-
rb_raise(rb_eImagePackInvalidArgumentError, "unknown input kind");
|
|
537
|
+
static const ip_symbol_entry table[] = {{&id_bytes, IP_INPUT_BYTES},
|
|
538
|
+
{&id_path, IP_INPUT_PATH},
|
|
539
|
+
{&id_io_buffer, IP_INPUT_IO_BUFFER},
|
|
540
|
+
{NULL, 0}};
|
|
541
|
+
return (ip_input_kind_t)ip_map_symbol(sym, "input kind", table);
|
|
441
542
|
}
|
|
442
543
|
|
|
443
544
|
static ip_output_kind_t ip_parse_output_kind(VALUE sym) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
545
|
+
static const ip_symbol_entry table[] = {
|
|
546
|
+
{&id_return_string, IP_OUTPUT_RETURN_STRING}, {&id_path, IP_OUTPUT_PATH}, {NULL, 0}};
|
|
547
|
+
return (ip_output_kind_t)ip_map_symbol(sym, "output kind", table);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
static VALUE ip_sym(const char *name) {
|
|
551
|
+
return ID2SYM(rb_intern(name));
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
static void ip_jpeg_emit_message_collect(j_common_ptr cinfo, int msg_level) {
|
|
555
|
+
if (msg_level >= 0)
|
|
556
|
+
return;
|
|
557
|
+
|
|
558
|
+
ip_jpeg_error_mgr *err = (ip_jpeg_error_mgr *)cinfo->err;
|
|
559
|
+
ip_context_t *ctx = err->ctx;
|
|
560
|
+
if (!ctx)
|
|
561
|
+
return;
|
|
562
|
+
|
|
563
|
+
ctx->warning_count++;
|
|
564
|
+
if (ctx->first_warning[0] == '\0') {
|
|
565
|
+
char buffer[JMSG_LENGTH_MAX];
|
|
566
|
+
(*cinfo->err->format_message)(cinfo, buffer);
|
|
567
|
+
snprintf(ctx->first_warning, sizeof(ctx->first_warning), "%s", buffer);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (ctx->strict) {
|
|
571
|
+
if (ctx->status == IP_OK) {
|
|
572
|
+
char buffer[JMSG_LENGTH_MAX];
|
|
573
|
+
(*cinfo->err->format_message)(cinfo, buffer);
|
|
574
|
+
ip_context_set_error(ctx, IP_ERR_INVALID_IMAGE, buffer);
|
|
575
|
+
}
|
|
576
|
+
if (ctx->jmp_armed)
|
|
577
|
+
longjmp(ctx->jmpbuf, 1);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
static void ip_jpeg_output_message_silent(j_common_ptr cinfo) {
|
|
582
|
+
(void)cinfo;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
static struct jpeg_error_mgr *ip_use_error(ip_jpeg_error_mgr *jerr, ip_context_t *ctx,
|
|
586
|
+
void (*handler)(j_common_ptr)) {
|
|
587
|
+
struct jpeg_error_mgr *base = jpeg_std_error(&jerr->pub);
|
|
588
|
+
jerr->pub.error_exit = handler;
|
|
589
|
+
jerr->pub.emit_message = ip_jpeg_emit_message_collect;
|
|
590
|
+
jerr->pub.output_message = ip_jpeg_output_message_silent;
|
|
591
|
+
jerr->ctx = ctx;
|
|
592
|
+
return base;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
static void ip_apply_fast_decode(struct jpeg_decompress_struct *cinfo) {
|
|
596
|
+
cinfo->dct_method = IP_FAST_DCT;
|
|
597
|
+
cinfo->do_fancy_upsampling = FALSE;
|
|
598
|
+
cinfo->do_block_smoothing = FALSE;
|
|
599
|
+
cinfo->quantize_colors = FALSE;
|
|
600
|
+
cinfo->two_pass_quantize = FALSE;
|
|
601
|
+
cinfo->dither_mode = JDITHER_NONE;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
static void ip_disable_mozjpeg_trellis(struct jpeg_compress_struct *cinfo) {
|
|
605
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_QUANT, FALSE);
|
|
606
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_QUANT_DC, FALSE);
|
|
607
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_EOB_OPT, FALSE);
|
|
608
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_USE_SCANS_IN_TRELLIS, FALSE);
|
|
609
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_Q_OPT, FALSE);
|
|
610
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_OVERSHOOT_DERINGING, FALSE);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
static int ip_check_max_dimension_limits(ip_context_t *ctx) {
|
|
614
|
+
if (ctx->max_width > 0 && ctx->width > ctx->max_width)
|
|
615
|
+
IP_FAIL(ctx, IP_ERR_LIMIT, "image width exceeds max_width");
|
|
616
|
+
if (ctx->max_height > 0 && ctx->height > ctx->max_height)
|
|
617
|
+
IP_FAIL(ctx, IP_ERR_LIMIT, "image height exceeds max_height");
|
|
618
|
+
if (ctx->max_pixels > 0 && (uint64_t)ctx->width * (uint64_t)ctx->height > ctx->max_pixels)
|
|
619
|
+
IP_FAIL(ctx, IP_ERR_LIMIT, "image pixels exceed max_pixels");
|
|
620
|
+
return 1;
|
|
450
621
|
}
|
|
451
622
|
|
|
452
623
|
static VALUE pathname_to_s(VALUE object) {
|
|
@@ -464,36 +635,41 @@ static int read_file_to_owned_buffer(ip_context_t *ctx, const char *path) {
|
|
|
464
635
|
return 0;
|
|
465
636
|
}
|
|
466
637
|
|
|
467
|
-
if (
|
|
638
|
+
if (ip_file_seek_end(fp) != 0) {
|
|
468
639
|
fclose(fp);
|
|
469
640
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "failed to seek input path");
|
|
470
641
|
return 0;
|
|
471
642
|
}
|
|
472
643
|
|
|
473
|
-
|
|
474
|
-
if (size
|
|
644
|
+
size_t size = 0;
|
|
645
|
+
if (!ip_file_tell(fp, &size)) {
|
|
475
646
|
fclose(fp);
|
|
476
647
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "failed to determine input size");
|
|
477
648
|
return 0;
|
|
478
649
|
}
|
|
479
|
-
rewind(fp);
|
|
480
650
|
|
|
481
|
-
if (
|
|
651
|
+
if (ip_file_rewind(fp) != 0) {
|
|
652
|
+
fclose(fp);
|
|
653
|
+
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "failed to rewind input path");
|
|
654
|
+
return 0;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (ctx->max_input_size > 0 && size > ctx->max_input_size) {
|
|
482
658
|
fclose(fp);
|
|
483
659
|
ip_context_set_error(ctx, IP_ERR_LIMIT, "input file exceeds max_input_size");
|
|
484
660
|
return 0;
|
|
485
661
|
}
|
|
486
662
|
|
|
487
|
-
unsigned char *data = (unsigned char *)malloc(
|
|
663
|
+
unsigned char *data = (unsigned char *)malloc(size);
|
|
488
664
|
if (!data && size > 0) {
|
|
489
665
|
fclose(fp);
|
|
490
666
|
ip_context_set_error(ctx, IP_ERR_OOM, "failed to allocate input file buffer");
|
|
491
667
|
return 0;
|
|
492
668
|
}
|
|
493
669
|
|
|
494
|
-
size_t read_size = fread(data, 1,
|
|
670
|
+
size_t read_size = fread(data, 1, size, fp);
|
|
495
671
|
fclose(fp);
|
|
496
|
-
if (read_size !=
|
|
672
|
+
if (read_size != size) {
|
|
497
673
|
free(data);
|
|
498
674
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "failed to read input path");
|
|
499
675
|
return 0;
|
|
@@ -505,9 +681,36 @@ static int read_file_to_owned_buffer(ip_context_t *ctx, const char *path) {
|
|
|
505
681
|
return 1;
|
|
506
682
|
}
|
|
507
683
|
|
|
508
|
-
static
|
|
509
|
-
VALUE
|
|
510
|
-
|
|
684
|
+
static size_t io_buffer_size_or_raise(VALUE buffer) {
|
|
685
|
+
VALUE size_value = rb_funcall(buffer, rb_intern("size"), 0);
|
|
686
|
+
if (!RB_INTEGER_TYPE_P(size_value) || ip_value_negative(size_value))
|
|
687
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "IO::Buffer#size must return Integer >= 0");
|
|
688
|
+
return NUM2SIZET(size_value);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
static VALUE io_buffer_to_string_slice(VALUE buffer, size_t len) {
|
|
692
|
+
return rb_funcall(buffer, rb_intern("get_string"), 2, LONG2NUM(0), SIZET2NUM(len));
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
static int ip_copy_string_to_owned_input(ip_context_t *ctx, VALUE input) {
|
|
696
|
+
Check_Type(input, T_STRING);
|
|
697
|
+
size_t len = (size_t)RSTRING_LEN(input);
|
|
698
|
+
if (ctx->max_input_size > 0 && len > ctx->max_input_size) {
|
|
699
|
+
ip_context_set_error(ctx, IP_ERR_LIMIT, "input bytes exceed max_input_size");
|
|
700
|
+
return 0;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
unsigned char *copy = (unsigned char *)malloc(len);
|
|
704
|
+
if (!copy && len > 0) {
|
|
705
|
+
ip_context_set_error(ctx, IP_ERR_OOM, "failed to copy binary String input");
|
|
706
|
+
return 0;
|
|
707
|
+
}
|
|
708
|
+
if (len > 0)
|
|
709
|
+
memcpy(copy, RSTRING_PTR(input), len);
|
|
710
|
+
ctx->owned_input_data = copy;
|
|
711
|
+
ctx->input_data = copy;
|
|
712
|
+
ctx->input_size = len;
|
|
713
|
+
return 1;
|
|
511
714
|
}
|
|
512
715
|
|
|
513
716
|
static int ip_prepare_input_bytes(ip_context_t *ctx, VALUE input, ip_input_kind_t kind) {
|
|
@@ -518,33 +721,32 @@ static int ip_prepare_input_bytes(ip_context_t *ctx, VALUE input, ip_input_kind_
|
|
|
518
721
|
ip_context_set_error(ctx, IP_ERR_LIMIT, "input bytes exceed max_input_size");
|
|
519
722
|
return 0;
|
|
520
723
|
}
|
|
521
|
-
|
|
724
|
+
|
|
725
|
+
if (ctx->requested_execution == IP_EXEC_DIRECT ||
|
|
726
|
+
ctx->requested_execution == IP_EXEC_AUTO) {
|
|
522
727
|
ctx->input_data = (const unsigned char *)RSTRING_PTR(input);
|
|
523
728
|
ctx->input_size = len;
|
|
524
729
|
return 1;
|
|
525
730
|
}
|
|
526
731
|
|
|
527
|
-
|
|
528
|
-
if (!copy && len > 0) {
|
|
529
|
-
ip_context_set_error(ctx, IP_ERR_OOM, "failed to copy binary String input");
|
|
530
|
-
return 0;
|
|
531
|
-
}
|
|
532
|
-
if (len > 0)
|
|
533
|
-
memcpy(copy, RSTRING_PTR(input), len);
|
|
534
|
-
ctx->owned_input_data = copy;
|
|
535
|
-
ctx->input_data = copy;
|
|
536
|
-
ctx->input_size = len;
|
|
537
|
-
return 1;
|
|
732
|
+
return ip_copy_string_to_owned_input(ctx, input);
|
|
538
733
|
}
|
|
539
734
|
|
|
540
735
|
if (kind == IP_INPUT_IO_BUFFER) {
|
|
541
|
-
|
|
542
|
-
StringValue(str);
|
|
543
|
-
size_t len = (size_t)RSTRING_LEN(str);
|
|
736
|
+
size_t len = io_buffer_size_or_raise(input);
|
|
544
737
|
if (ctx->max_input_size > 0 && len > ctx->max_input_size) {
|
|
545
738
|
ip_context_set_error(ctx, IP_ERR_LIMIT, "input IO::Buffer exceeds max_input_size");
|
|
546
739
|
return 0;
|
|
547
740
|
}
|
|
741
|
+
|
|
742
|
+
VALUE str = io_buffer_to_string_slice(input, len);
|
|
743
|
+
StringValue(str);
|
|
744
|
+
if ((size_t)RSTRING_LEN(str) != len) {
|
|
745
|
+
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT,
|
|
746
|
+
"IO::Buffer#get_string returned unexpected size");
|
|
747
|
+
return 0;
|
|
748
|
+
}
|
|
749
|
+
|
|
548
750
|
unsigned char *copy = (unsigned char *)malloc(len);
|
|
549
751
|
if (!copy && len > 0) {
|
|
550
752
|
ip_context_set_error(ctx, IP_ERR_OOM, "failed to copy IO::Buffer input");
|
|
@@ -563,15 +765,31 @@ static int ip_prepare_input_bytes(ip_context_t *ctx, VALUE input, ip_input_kind_
|
|
|
563
765
|
return read_file_to_owned_buffer(ctx, StringValueCStr(path_value));
|
|
564
766
|
}
|
|
565
767
|
|
|
566
|
-
static int
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
768
|
+
static int ip_ensure_owned_input_for_async(ip_context_t *ctx, VALUE input, ip_input_kind_t kind) {
|
|
769
|
+
if (ctx->resolved_execution == IP_EXEC_DIRECT)
|
|
770
|
+
return 1;
|
|
771
|
+
if (kind != IP_INPUT_BYTES || ctx->owned_input_data)
|
|
772
|
+
return 1;
|
|
773
|
+
return ip_copy_string_to_owned_input(ctx, input);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
static int ip_copy_string_to_owned_pixels(ip_context_t *ctx, VALUE str, size_t expected) {
|
|
777
|
+
unsigned char *copy = (unsigned char *)malloc(expected);
|
|
778
|
+
if (!copy && expected > 0) {
|
|
779
|
+
ip_context_set_error(ctx, IP_ERR_OOM, "failed to copy pixel buffer");
|
|
780
|
+
return 0;
|
|
572
781
|
}
|
|
573
782
|
|
|
574
|
-
|
|
783
|
+
if (expected > 0)
|
|
784
|
+
memcpy(copy, RSTRING_PTR(str), expected);
|
|
785
|
+
ctx->owned_pixel_data = copy;
|
|
786
|
+
ctx->pixel_data = copy;
|
|
787
|
+
ctx->pixel_size = expected;
|
|
788
|
+
return 1;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
static int ip_prepare_pixels(ip_context_t *ctx, VALUE buffer, int width, int height, int channels,
|
|
792
|
+
int exact_size) {
|
|
575
793
|
size_t expected = 0;
|
|
576
794
|
if (!ip_checked_image_size(width, height, channels, &expected)) {
|
|
577
795
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT,
|
|
@@ -579,34 +797,49 @@ static int ip_prepare_pixels(ip_context_t *ctx, VALUE buffer, int width, int hei
|
|
|
579
797
|
return 0;
|
|
580
798
|
}
|
|
581
799
|
|
|
582
|
-
|
|
800
|
+
VALUE str = Qnil;
|
|
801
|
+
size_t actual = 0;
|
|
802
|
+
int buffer_is_string = RB_TYPE_P(buffer, T_STRING);
|
|
803
|
+
|
|
804
|
+
if (buffer_is_string) {
|
|
805
|
+
str = buffer;
|
|
806
|
+
actual = (size_t)RSTRING_LEN(str);
|
|
807
|
+
} else {
|
|
808
|
+
actual = io_buffer_size_or_raise(buffer);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if (actual < expected) {
|
|
583
812
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT,
|
|
584
813
|
"pixel buffer is smaller than width * height * channels");
|
|
585
814
|
return 0;
|
|
586
815
|
}
|
|
587
816
|
|
|
588
|
-
if (
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
ctx->channels = channels;
|
|
594
|
-
ctx->bit_depth = 8;
|
|
595
|
-
ctx->decoded_bytes = expected;
|
|
596
|
-
return 1;
|
|
817
|
+
if (exact_size && actual != expected) {
|
|
818
|
+
ip_context_set_error(
|
|
819
|
+
ctx, IP_ERR_INVALID_ARGUMENT,
|
|
820
|
+
"pixel buffer size must equal width * height * channels when exact_size is true");
|
|
821
|
+
return 0;
|
|
597
822
|
}
|
|
598
823
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
824
|
+
if (!buffer_is_string) {
|
|
825
|
+
str = io_buffer_to_string_slice(buffer, expected);
|
|
826
|
+
StringValue(str);
|
|
827
|
+
if ((size_t)RSTRING_LEN(str) != expected) {
|
|
828
|
+
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT,
|
|
829
|
+
"IO::Buffer#get_string returned unexpected pixel size");
|
|
830
|
+
return 0;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
if ((ctx->requested_execution == IP_EXEC_DIRECT || ctx->requested_execution == IP_EXEC_AUTO) &&
|
|
835
|
+
buffer_is_string) {
|
|
836
|
+
ctx->pixel_data = (const unsigned char *)RSTRING_PTR(str);
|
|
837
|
+
ctx->pixel_size = expected;
|
|
838
|
+
} else {
|
|
839
|
+
if (!ip_copy_string_to_owned_pixels(ctx, str, expected))
|
|
840
|
+
return 0;
|
|
603
841
|
}
|
|
604
842
|
|
|
605
|
-
if (expected > 0)
|
|
606
|
-
memcpy(copy, RSTRING_PTR(str), expected);
|
|
607
|
-
ctx->owned_pixel_data = copy;
|
|
608
|
-
ctx->pixel_data = copy;
|
|
609
|
-
ctx->pixel_size = expected;
|
|
610
843
|
ctx->width = width;
|
|
611
844
|
ctx->height = height;
|
|
612
845
|
ctx->channels = channels;
|
|
@@ -615,6 +848,14 @@ static int ip_prepare_pixels(ip_context_t *ctx, VALUE buffer, int width, int hei
|
|
|
615
848
|
return 1;
|
|
616
849
|
}
|
|
617
850
|
|
|
851
|
+
static int ip_ensure_owned_pixels_for_async(ip_context_t *ctx, VALUE buffer) {
|
|
852
|
+
if (ctx->resolved_execution == IP_EXEC_DIRECT)
|
|
853
|
+
return 1;
|
|
854
|
+
if (!RB_TYPE_P(buffer, T_STRING) || ctx->owned_pixel_data)
|
|
855
|
+
return 1;
|
|
856
|
+
return ip_copy_string_to_owned_pixels(ctx, buffer, ctx->pixel_size);
|
|
857
|
+
}
|
|
858
|
+
|
|
618
859
|
static int ip_prepare_output_path(ip_context_t *ctx, VALUE output, ip_output_kind_t kind) {
|
|
619
860
|
if (kind == IP_OUTPUT_RETURN_STRING)
|
|
620
861
|
return 1;
|
|
@@ -622,7 +863,7 @@ static int ip_prepare_output_path(ip_context_t *ctx, VALUE output, ip_output_kin
|
|
|
622
863
|
VALUE path_value = pathname_to_s(output);
|
|
623
864
|
StringValue(path_value);
|
|
624
865
|
const char *path = StringValueCStr(path_value);
|
|
625
|
-
ctx->output_path =
|
|
866
|
+
ctx->output_path = ip_strdup(path);
|
|
626
867
|
if (!ctx->output_path) {
|
|
627
868
|
ip_context_set_error(ctx, IP_ERR_OOM, "failed to copy output path");
|
|
628
869
|
return 0;
|
|
@@ -666,7 +907,7 @@ static VALUE ip_finish_output(ip_context_t *ctx, ip_output_kind_t kind) {
|
|
|
666
907
|
rb_raise(rb_eImagePackEncodeError, "failed to write full JPEG output");
|
|
667
908
|
}
|
|
668
909
|
|
|
669
|
-
if (
|
|
910
|
+
if (ip_replace_file(tmp_path, ctx->output_path) != 0) {
|
|
670
911
|
remove(tmp_path);
|
|
671
912
|
free(tmp_path);
|
|
672
913
|
rb_raise(rb_eImagePackEncodeError, "failed to move temporary JPEG output into place");
|
|
@@ -676,11 +917,32 @@ static VALUE ip_finish_output(ip_context_t *ctx, ip_output_kind_t kind) {
|
|
|
676
917
|
return Qtrue;
|
|
677
918
|
}
|
|
678
919
|
|
|
920
|
+
static VALUE ip_build_report(ip_context_t *ctx, VALUE output_value) {
|
|
921
|
+
VALUE hash = rb_hash_new();
|
|
922
|
+
rb_hash_aset(hash, ip_sym("output"), output_value);
|
|
923
|
+
rb_hash_aset(hash, ip_sym("quality"), INT2NUM(ctx->selected_quality));
|
|
924
|
+
rb_hash_aset(hash, ip_sym("ssim"),
|
|
925
|
+
ctx->ssim_guard_enabled ? DBL2NUM(ctx->measured_ssim) : Qnil);
|
|
926
|
+
rb_hash_aset(hash, ip_sym("algo"),
|
|
927
|
+
ctx->algo == IP_ALGO_MOZJPEG ? ip_sym("mozjpeg") : ip_sym("jpeg_turbo"));
|
|
928
|
+
rb_hash_aset(hash, ip_sym("bytesize"), SIZET2NUM(ctx->output_size));
|
|
929
|
+
rb_hash_aset(hash, ip_sym("input_bytesize"),
|
|
930
|
+
SIZET2NUM(ctx->input_size > 0 ? ctx->input_size : ctx->pixel_size));
|
|
931
|
+
rb_hash_aset(hash, ip_sym("warning_count"), INT2NUM(ctx->warning_count));
|
|
932
|
+
rb_hash_aset(hash, ip_sym("warning"),
|
|
933
|
+
ctx->first_warning[0] ? rb_str_new_cstr(ctx->first_warning) : Qnil);
|
|
934
|
+
return hash;
|
|
935
|
+
}
|
|
936
|
+
|
|
679
937
|
static int ip_save_marker(ip_context_t *ctx, int marker, const unsigned char *data,
|
|
680
938
|
unsigned int len) {
|
|
681
939
|
if (ctx->preserved_marker_count == ctx->preserved_marker_capacity) {
|
|
940
|
+
if (ctx->preserved_marker_capacity > SIZE_MAX / 2)
|
|
941
|
+
return 0;
|
|
682
942
|
size_t new_cap =
|
|
683
943
|
ctx->preserved_marker_capacity == 0 ? 4 : ctx->preserved_marker_capacity * 2;
|
|
944
|
+
if (new_cap > SIZE_MAX / sizeof(*ctx->preserved_markers))
|
|
945
|
+
return 0;
|
|
684
946
|
void *new_buf = realloc(ctx->preserved_markers, new_cap * sizeof(*ctx->preserved_markers));
|
|
685
947
|
if (!new_buf)
|
|
686
948
|
return 0;
|
|
@@ -812,7 +1074,7 @@ static int ip_transform_pixels_for_orientation(ip_context_t *ctx, unsigned char
|
|
|
812
1074
|
}
|
|
813
1075
|
|
|
814
1076
|
unsigned char *src = *pixels;
|
|
815
|
-
unsigned char *dst = (unsigned char *)
|
|
1077
|
+
unsigned char *dst = (unsigned char *)malloc(out_size);
|
|
816
1078
|
if (!dst && out_size > 0) {
|
|
817
1079
|
ip_context_set_error(ctx, IP_ERR_OOM, "failed to allocate EXIF-oriented pixel buffer");
|
|
818
1080
|
return 0;
|
|
@@ -894,82 +1156,71 @@ static void ip_jpeg_encode_error_exit(j_common_ptr cinfo) {
|
|
|
894
1156
|
longjmp(err->ctx->jmpbuf, 1);
|
|
895
1157
|
}
|
|
896
1158
|
|
|
897
|
-
static
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1159
|
+
static const char *ip_jpeg_color_space_name(int color_space) {
|
|
1160
|
+
switch ((J_COLOR_SPACE)color_space) {
|
|
1161
|
+
case JCS_GRAYSCALE:
|
|
1162
|
+
return "grayscale";
|
|
1163
|
+
case JCS_RGB:
|
|
1164
|
+
return "rgb";
|
|
1165
|
+
case JCS_YCbCr:
|
|
1166
|
+
return "ycbcr";
|
|
1167
|
+
case JCS_CMYK:
|
|
1168
|
+
return "cmyk";
|
|
1169
|
+
case JCS_YCCK:
|
|
1170
|
+
return "ycck";
|
|
1171
|
+
default:
|
|
1172
|
+
return "unknown";
|
|
902
1173
|
}
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
static int ip_inspect_jpeg_header(ip_context_t *ctx, int allow_cmyk_ycck) {
|
|
1177
|
+
if (!ctx->input_data || ctx->input_size < 2 || ctx->input_data[0] != 0xFF ||
|
|
1178
|
+
ctx->input_data[1] != 0xD8)
|
|
1179
|
+
IP_FAIL(ctx, IP_ERR_INVALID_IMAGE, "input is not a JPEG image");
|
|
903
1180
|
|
|
904
1181
|
struct jpeg_decompress_struct cinfo;
|
|
905
1182
|
ip_jpeg_error_mgr jerr;
|
|
906
1183
|
memset(&cinfo, 0, sizeof(cinfo));
|
|
907
1184
|
memset(&jerr, 0, sizeof(jerr));
|
|
908
|
-
|
|
909
|
-
cinfo.err = jpeg_std_error(&jerr.pub);
|
|
910
|
-
jerr.pub.error_exit = ip_jpeg_invalid_error_exit;
|
|
911
|
-
jerr.ctx = ctx;
|
|
1185
|
+
cinfo.err = ip_use_error(&jerr, ctx, ip_jpeg_invalid_error_exit);
|
|
912
1186
|
|
|
913
1187
|
ctx->jmp_armed = 1;
|
|
914
|
-
if (setjmp(ctx->jmpbuf))
|
|
915
|
-
|
|
916
|
-
jpeg_destroy_decompress(&cinfo);
|
|
917
|
-
return 0;
|
|
918
|
-
}
|
|
1188
|
+
if (setjmp(ctx->jmpbuf))
|
|
1189
|
+
goto fail;
|
|
919
1190
|
|
|
920
1191
|
jpeg_create_decompress(&cinfo);
|
|
921
1192
|
jpeg_mem_src(&cinfo, ctx->input_data, (unsigned long)ctx->input_size);
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
jpeg_destroy_decompress(&cinfo);
|
|
925
|
-
ctx->jmp_armed = 0;
|
|
926
|
-
ip_context_set_error(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
927
|
-
return 0;
|
|
928
|
-
}
|
|
1193
|
+
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
|
|
1194
|
+
IP_FAIL_GOTO(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
929
1195
|
|
|
930
|
-
if (cinfo.num_components == 4 || cinfo.jpeg_color_space == JCS_CMYK ||
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
935
|
-
"CMYK/YCCK JPEG input is not supported in this release");
|
|
936
|
-
return 0;
|
|
937
|
-
}
|
|
1196
|
+
if (!allow_cmyk_ycck && (cinfo.num_components == 4 || cinfo.jpeg_color_space == JCS_CMYK ||
|
|
1197
|
+
cinfo.jpeg_color_space == JCS_YCCK))
|
|
1198
|
+
IP_FAIL_GOTO(ctx, IP_ERR_UNSUPPORTED,
|
|
1199
|
+
"CMYK/YCCK JPEG input is not supported for pixel recompression");
|
|
938
1200
|
|
|
939
1201
|
ctx->width = (int)cinfo.image_width;
|
|
940
1202
|
ctx->height = (int)cinfo.image_height;
|
|
941
1203
|
ctx->channels = cinfo.num_components;
|
|
942
|
-
|
|
943
|
-
jpeg_destroy_decompress(&cinfo);
|
|
944
|
-
ctx->jmp_armed = 0;
|
|
945
|
-
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED, "JPEG component count is not supported");
|
|
946
|
-
return 0;
|
|
947
|
-
}
|
|
948
|
-
ctx->bit_depth = 8;
|
|
949
|
-
ctx->decoded_bytes = (size_t)ctx->width * (size_t)ctx->height * (size_t)ctx->channels;
|
|
1204
|
+
ctx->jpeg_color_space = (int)cinfo.jpeg_color_space;
|
|
950
1205
|
|
|
951
1206
|
jpeg_destroy_decompress(&cinfo);
|
|
952
1207
|
ctx->jmp_armed = 0;
|
|
953
1208
|
|
|
954
|
-
if (ctx->width <= 0 || ctx->height <= 0)
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1209
|
+
if (ctx->width <= 0 || ctx->height <= 0)
|
|
1210
|
+
IP_FAIL(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG dimensions");
|
|
1211
|
+
if (ctx->channels != 1 && ctx->channels != 3 && ctx->channels != 4)
|
|
1212
|
+
IP_FAIL(ctx, IP_ERR_UNSUPPORTED, "JPEG component count is not supported");
|
|
958
1213
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
}
|
|
963
|
-
if (ctx->max_height > 0 && ctx->height > ctx->max_height) {
|
|
964
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "image height exceeds max_height");
|
|
965
|
-
return 0;
|
|
966
|
-
}
|
|
967
|
-
if (ctx->max_pixels > 0 && (uint64_t)ctx->width * (uint64_t)ctx->height > ctx->max_pixels) {
|
|
968
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "image pixels exceed max_pixels");
|
|
969
|
-
return 0;
|
|
970
|
-
}
|
|
1214
|
+
ctx->bit_depth = 8;
|
|
1215
|
+
if (!ip_checked_image_size(ctx->width, ctx->height, ctx->channels, &ctx->decoded_bytes))
|
|
1216
|
+
IP_FAIL(ctx, IP_ERR_LIMIT, "decoded image size overflows native size");
|
|
971
1217
|
|
|
972
|
-
return
|
|
1218
|
+
return ip_check_max_dimension_limits(ctx);
|
|
1219
|
+
|
|
1220
|
+
fail:
|
|
1221
|
+
jpeg_destroy_decompress(&cinfo);
|
|
1222
|
+
ctx->jmp_armed = 0;
|
|
1223
|
+
return 0;
|
|
973
1224
|
}
|
|
974
1225
|
|
|
975
1226
|
static VALUE ip_inspect_image_entry_body(VALUE ptr) {
|
|
@@ -979,8 +1230,10 @@ static VALUE ip_inspect_image_entry_body(VALUE ptr) {
|
|
|
979
1230
|
rb_raise(rb_eImagePackOutOfMemoryError, "failed to allocate native context");
|
|
980
1231
|
call->ctx = ctx;
|
|
981
1232
|
|
|
1233
|
+
apply_configuration(call->self, ctx);
|
|
1234
|
+
|
|
982
1235
|
if (!ip_prepare_input_bytes(ctx, call->input, ip_parse_input_kind(call->input_kind)) ||
|
|
983
|
-
!ip_inspect_jpeg_header(ctx)) {
|
|
1236
|
+
!ip_inspect_jpeg_header(ctx, 1)) {
|
|
984
1237
|
ip_raise_for_status(ctx);
|
|
985
1238
|
rb_raise(rb_eImagePackInvalidImageError, "failed to inspect JPEG image");
|
|
986
1239
|
}
|
|
@@ -989,18 +1242,20 @@ static VALUE ip_inspect_image_entry_body(VALUE ptr) {
|
|
|
989
1242
|
int height = ctx->height;
|
|
990
1243
|
int channels = ctx->channels;
|
|
991
1244
|
int bit_depth = ctx->bit_depth;
|
|
1245
|
+
int color_space = ctx->jpeg_color_space;
|
|
992
1246
|
size_t decoded_bytes = ctx->decoded_bytes;
|
|
993
1247
|
|
|
994
1248
|
ip_context_free(ctx);
|
|
995
1249
|
call->ctx = NULL;
|
|
996
1250
|
|
|
997
1251
|
VALUE hash = rb_hash_new();
|
|
998
|
-
rb_hash_aset(hash,
|
|
999
|
-
rb_hash_aset(hash,
|
|
1000
|
-
rb_hash_aset(hash,
|
|
1001
|
-
rb_hash_aset(hash,
|
|
1002
|
-
rb_hash_aset(hash,
|
|
1003
|
-
rb_hash_aset(hash,
|
|
1252
|
+
rb_hash_aset(hash, ip_sym("format"), ip_sym("jpeg"));
|
|
1253
|
+
rb_hash_aset(hash, ip_sym("width"), INT2NUM(width));
|
|
1254
|
+
rb_hash_aset(hash, ip_sym("height"), INT2NUM(height));
|
|
1255
|
+
rb_hash_aset(hash, ip_sym("channels"), INT2NUM(channels));
|
|
1256
|
+
rb_hash_aset(hash, ip_sym("bit_depth"), INT2NUM(bit_depth));
|
|
1257
|
+
rb_hash_aset(hash, ip_sym("color_space"), ip_sym(ip_jpeg_color_space_name(color_space)));
|
|
1258
|
+
rb_hash_aset(hash, ip_sym("decoded_bytes"), SIZET2NUM(decoded_bytes));
|
|
1004
1259
|
return hash;
|
|
1005
1260
|
}
|
|
1006
1261
|
|
|
@@ -1035,32 +1290,17 @@ static void configure_mozjpeg_features_after_defaults(struct jpeg_compress_struc
|
|
|
1035
1290
|
jpeg_c_set_bool_param(cinfo, JBOOLEAN_OPTIMIZE_SCANS, FALSE);
|
|
1036
1291
|
}
|
|
1037
1292
|
|
|
1038
|
-
if (!mozjpeg_trellis_enabled)
|
|
1039
|
-
|
|
1040
|
-
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_QUANT_DC, FALSE);
|
|
1041
|
-
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_EOB_OPT, FALSE);
|
|
1042
|
-
jpeg_c_set_bool_param(cinfo, JBOOLEAN_USE_SCANS_IN_TRELLIS, FALSE);
|
|
1043
|
-
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_Q_OPT, FALSE);
|
|
1044
|
-
jpeg_c_set_bool_param(cinfo, JBOOLEAN_OVERSHOOT_DERINGING, FALSE);
|
|
1045
|
-
}
|
|
1293
|
+
if (!mozjpeg_trellis_enabled)
|
|
1294
|
+
ip_disable_mozjpeg_trellis(cinfo);
|
|
1046
1295
|
|
|
1047
1296
|
return;
|
|
1048
1297
|
}
|
|
1049
1298
|
|
|
1050
1299
|
cinfo->optimize_coding = FALSE;
|
|
1051
|
-
|
|
1052
|
-
cinfo->dct_method = JDCT_ISLOW;
|
|
1053
|
-
#else
|
|
1054
|
-
cinfo->dct_method = JDCT_FASTEST;
|
|
1055
|
-
#endif
|
|
1300
|
+
cinfo->dct_method = IP_FAST_DCT;
|
|
1056
1301
|
cinfo->smoothing_factor = 0;
|
|
1057
1302
|
jpeg_c_set_bool_param(cinfo, JBOOLEAN_OPTIMIZE_SCANS, FALSE);
|
|
1058
|
-
|
|
1059
|
-
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_QUANT_DC, FALSE);
|
|
1060
|
-
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_EOB_OPT, FALSE);
|
|
1061
|
-
jpeg_c_set_bool_param(cinfo, JBOOLEAN_USE_SCANS_IN_TRELLIS, FALSE);
|
|
1062
|
-
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_Q_OPT, FALSE);
|
|
1063
|
-
jpeg_c_set_bool_param(cinfo, JBOOLEAN_OVERSHOOT_DERINGING, FALSE);
|
|
1303
|
+
ip_disable_mozjpeg_trellis(cinfo);
|
|
1064
1304
|
|
|
1065
1305
|
if (progressive_requested) {
|
|
1066
1306
|
jpeg_simple_progression(cinfo);
|
|
@@ -1068,73 +1308,20 @@ static void configure_mozjpeg_features_after_defaults(struct jpeg_compress_struc
|
|
|
1068
1308
|
}
|
|
1069
1309
|
}
|
|
1070
1310
|
|
|
1071
|
-
static int prepare_encode_rows(ip_context_t *ctx, JDIMENSION start, JDIMENSION batch,
|
|
1072
|
-
JSAMPROW *rows) {
|
|
1073
|
-
if (ctx->channels != 4) {
|
|
1074
|
-
for (JDIMENSION i = 0; i < batch; i++) {
|
|
1075
|
-
JDIMENSION y = start + i;
|
|
1076
|
-
rows[i] = (JSAMPROW)(ctx->pixel_data +
|
|
1077
|
-
((size_t)y * (size_t)ctx->width * (size_t)ctx->channels));
|
|
1078
|
-
}
|
|
1079
|
-
return 1;
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
size_t rgb_row_size = 0;
|
|
1083
|
-
size_t scratch_size = 0;
|
|
1084
|
-
if (!ip_checked_mul_size((size_t)ctx->width, 3, &rgb_row_size) ||
|
|
1085
|
-
!ip_checked_mul_size(rgb_row_size, (size_t)batch, &scratch_size)) {
|
|
1086
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "RGBA scratch row size overflow");
|
|
1087
|
-
return 0;
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
if (ctx->scratch_row_size < scratch_size) {
|
|
1091
|
-
unsigned char *new_rows = (unsigned char *)realloc(ctx->scratch_row, scratch_size);
|
|
1092
|
-
if (!new_rows) {
|
|
1093
|
-
ip_context_set_error(ctx, IP_ERR_OOM, "failed to allocate RGBA scratch rows");
|
|
1094
|
-
return 0;
|
|
1095
|
-
}
|
|
1096
|
-
ctx->scratch_row = new_rows;
|
|
1097
|
-
ctx->scratch_row_size = scratch_size;
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
for (JDIMENSION i = 0; i < batch; i++) {
|
|
1101
|
-
JDIMENSION y = start + i;
|
|
1102
|
-
const unsigned char *IP_RESTRICT src =
|
|
1103
|
-
ctx->pixel_data + ((size_t)y * (size_t)ctx->width * 4);
|
|
1104
|
-
unsigned char *IP_RESTRICT dst = ctx->scratch_row + ((size_t)i * rgb_row_size);
|
|
1105
|
-
const int w = ctx->width;
|
|
1106
|
-
for (int x = 0; x < w; x++) {
|
|
1107
|
-
dst[x * 3 + 0] = src[x * 4 + 0];
|
|
1108
|
-
dst[x * 3 + 1] = src[x * 4 + 1];
|
|
1109
|
-
dst[x * 3 + 2] = src[x * 4 + 2];
|
|
1110
|
-
}
|
|
1111
|
-
rows[i] = dst;
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
return 1;
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
1311
|
static int encode_pixels_with_libjpeg(ip_context_t *ctx, int mozjpeg_size_mode) {
|
|
1118
1312
|
struct jpeg_compress_struct cinfo;
|
|
1119
1313
|
ip_jpeg_error_mgr jerr;
|
|
1120
1314
|
unsigned long jpeg_size = 0;
|
|
1121
1315
|
memset(&cinfo, 0, sizeof(cinfo));
|
|
1122
1316
|
memset(&jerr, 0, sizeof(jerr));
|
|
1123
|
-
|
|
1124
|
-
cinfo.err = jpeg_std_error(&jerr.pub);
|
|
1125
|
-
jerr.pub.error_exit = ip_jpeg_encode_error_exit;
|
|
1126
|
-
jerr.ctx = ctx;
|
|
1317
|
+
cinfo.err = ip_use_error(&jerr, ctx, ip_jpeg_encode_error_exit);
|
|
1127
1318
|
ctx->transient_jpeg_buf = NULL;
|
|
1128
1319
|
|
|
1129
1320
|
ctx->jmp_armed = 1;
|
|
1130
1321
|
if (setjmp(ctx->jmpbuf)) {
|
|
1131
|
-
ctx->jmp_armed = 0;
|
|
1132
|
-
jpeg_destroy_compress(&cinfo);
|
|
1133
|
-
free(ctx->transient_jpeg_buf);
|
|
1134
|
-
ctx->transient_jpeg_buf = NULL;
|
|
1135
1322
|
if (ctx->status == IP_OK)
|
|
1136
1323
|
ip_context_set_error(ctx, IP_ERR_ENCODE, "JPEG encode failed");
|
|
1137
|
-
|
|
1324
|
+
goto fail;
|
|
1138
1325
|
}
|
|
1139
1326
|
|
|
1140
1327
|
jpeg_create_compress(&cinfo);
|
|
@@ -1142,8 +1329,9 @@ static int encode_pixels_with_libjpeg(ip_context_t *ctx, int mozjpeg_size_mode)
|
|
|
1142
1329
|
|
|
1143
1330
|
cinfo.image_width = (JDIMENSION)ctx->width;
|
|
1144
1331
|
cinfo.image_height = (JDIMENSION)ctx->height;
|
|
1145
|
-
cinfo.input_components = ctx->channels
|
|
1146
|
-
cinfo.in_color_space =
|
|
1332
|
+
cinfo.input_components = ctx->channels;
|
|
1333
|
+
cinfo.in_color_space =
|
|
1334
|
+
ctx->channels == 4 ? JCS_EXT_RGBA : color_space_for_channels(ctx->channels);
|
|
1147
1335
|
|
|
1148
1336
|
configure_mozjpeg_profile_before_defaults(&cinfo, mozjpeg_size_mode);
|
|
1149
1337
|
jpeg_set_defaults(&cinfo);
|
|
@@ -1153,20 +1341,12 @@ static int encode_pixels_with_libjpeg(ip_context_t *ctx, int mozjpeg_size_mode)
|
|
|
1153
1341
|
|
|
1154
1342
|
jpeg_start_compress(&cinfo, TRUE);
|
|
1155
1343
|
|
|
1156
|
-
if (!ctx->strip_metadata)
|
|
1344
|
+
if (!ctx->strip_metadata)
|
|
1157
1345
|
ip_write_preserved_markers(ctx, &cinfo);
|
|
1158
|
-
}
|
|
1159
1346
|
|
|
1160
1347
|
while (cinfo.next_scanline < cinfo.image_height) {
|
|
1161
|
-
if (
|
|
1162
|
-
|
|
1163
|
-
jpeg_abort_compress(&cinfo);
|
|
1164
|
-
jpeg_destroy_compress(&cinfo);
|
|
1165
|
-
free(ctx->transient_jpeg_buf);
|
|
1166
|
-
ctx->transient_jpeg_buf = NULL;
|
|
1167
|
-
ctx->jmp_armed = 0;
|
|
1168
|
-
return 0;
|
|
1169
|
-
}
|
|
1348
|
+
if (atomic_load(&ctx->cancelled))
|
|
1349
|
+
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "JPEG encode cancelled");
|
|
1170
1350
|
|
|
1171
1351
|
JSAMPROW rows[16];
|
|
1172
1352
|
JDIMENSION start_scanline = cinfo.next_scanline;
|
|
@@ -1174,35 +1354,33 @@ static int encode_pixels_with_libjpeg(ip_context_t *ctx, int mozjpeg_size_mode)
|
|
|
1174
1354
|
if (batch > 16)
|
|
1175
1355
|
batch = 16;
|
|
1176
1356
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
free(ctx->transient_jpeg_buf);
|
|
1181
|
-
ctx->transient_jpeg_buf = NULL;
|
|
1182
|
-
ctx->jmp_armed = 0;
|
|
1183
|
-
return 0;
|
|
1184
|
-
}
|
|
1357
|
+
for (JDIMENSION i = 0; i < batch; i++)
|
|
1358
|
+
rows[i] = (JSAMPROW)(ctx->pixel_data + ((size_t)(start_scanline + i) *
|
|
1359
|
+
(size_t)ctx->width * (size_t)ctx->channels));
|
|
1185
1360
|
|
|
1186
1361
|
jpeg_write_scanlines(&cinfo, rows, batch);
|
|
1187
1362
|
}
|
|
1188
1363
|
|
|
1189
1364
|
jpeg_finish_compress(&cinfo);
|
|
1365
|
+
|
|
1366
|
+
if (ctx->max_output_size > 0 && (size_t)jpeg_size > ctx->max_output_size)
|
|
1367
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "output exceeds max_output_size");
|
|
1368
|
+
|
|
1190
1369
|
jpeg_destroy_compress(&cinfo);
|
|
1191
1370
|
ctx->jmp_armed = 0;
|
|
1192
1371
|
|
|
1193
|
-
if (ctx->max_output_size > 0 && (size_t)jpeg_size > ctx->max_output_size) {
|
|
1194
|
-
free(ctx->transient_jpeg_buf);
|
|
1195
|
-
ctx->transient_jpeg_buf = NULL;
|
|
1196
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "output exceeds max_output_size");
|
|
1197
|
-
return 0;
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
1372
|
ctx->output_data = ctx->transient_jpeg_buf;
|
|
1201
1373
|
ctx->transient_jpeg_buf = NULL;
|
|
1202
1374
|
ctx->output_size = (size_t)jpeg_size;
|
|
1203
|
-
ctx->output_capacity = (size_t)jpeg_size;
|
|
1204
1375
|
ctx->output_owner = IP_OUTPUT_OWNER_MALLOC;
|
|
1205
1376
|
return 1;
|
|
1377
|
+
|
|
1378
|
+
fail:
|
|
1379
|
+
jpeg_destroy_compress(&cinfo);
|
|
1380
|
+
free(ctx->transient_jpeg_buf);
|
|
1381
|
+
ctx->transient_jpeg_buf = NULL;
|
|
1382
|
+
ctx->jmp_armed = 0;
|
|
1383
|
+
return 0;
|
|
1206
1384
|
}
|
|
1207
1385
|
|
|
1208
1386
|
static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, int *width,
|
|
@@ -1211,22 +1389,15 @@ static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, i
|
|
|
1211
1389
|
ip_jpeg_error_mgr jerr;
|
|
1212
1390
|
memset(&cinfo, 0, sizeof(cinfo));
|
|
1213
1391
|
memset(&jerr, 0, sizeof(jerr));
|
|
1214
|
-
|
|
1215
|
-
cinfo.err = jpeg_std_error(&jerr.pub);
|
|
1216
|
-
jerr.pub.error_exit = ip_jpeg_invalid_error_exit;
|
|
1217
|
-
jerr.ctx = ctx;
|
|
1392
|
+
cinfo.err = ip_use_error(&jerr, ctx, ip_jpeg_invalid_error_exit);
|
|
1218
1393
|
ctx->transient_decode_buf = NULL;
|
|
1219
1394
|
ctx->source_orientation = 1;
|
|
1220
1395
|
|
|
1221
1396
|
ctx->jmp_armed = 1;
|
|
1222
1397
|
if (setjmp(ctx->jmpbuf)) {
|
|
1223
|
-
ctx->jmp_armed = 0;
|
|
1224
|
-
jpeg_destroy_decompress(&cinfo);
|
|
1225
|
-
free(ctx->transient_decode_buf);
|
|
1226
|
-
ctx->transient_decode_buf = NULL;
|
|
1227
1398
|
if (ctx->status == IP_OK)
|
|
1228
1399
|
ip_context_set_error(ctx, IP_ERR_INVALID_IMAGE, "JPEG decode failed");
|
|
1229
|
-
|
|
1400
|
+
goto fail;
|
|
1230
1401
|
}
|
|
1231
1402
|
|
|
1232
1403
|
jpeg_create_decompress(&cinfo);
|
|
@@ -1235,118 +1406,67 @@ static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, i
|
|
|
1235
1406
|
jpeg_save_markers(&cinfo, JPEG_APP0 + 1, 0xFFFF);
|
|
1236
1407
|
if (!ctx->strip_metadata) {
|
|
1237
1408
|
jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
|
|
1238
|
-
for (int app = 2; app < 16; app++)
|
|
1409
|
+
for (int app = 2; app < 16; app++)
|
|
1239
1410
|
jpeg_save_markers(&cinfo, JPEG_APP0 + app, 0xFFFF);
|
|
1240
|
-
}
|
|
1241
1411
|
}
|
|
1242
1412
|
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
jpeg_destroy_decompress(&cinfo);
|
|
1246
|
-
ctx->jmp_armed = 0;
|
|
1247
|
-
ip_context_set_error(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1248
|
-
return 0;
|
|
1249
|
-
}
|
|
1413
|
+
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
|
|
1414
|
+
IP_FAIL_GOTO(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1250
1415
|
|
|
1251
1416
|
ctx->source_orientation = ip_read_exif_orientation_from_decompress(&cinfo);
|
|
1252
1417
|
|
|
1253
|
-
if (cinfo.image_width > (JDIMENSION)INT_MAX || cinfo.image_height > (JDIMENSION)INT_MAX)
|
|
1254
|
-
|
|
1255
|
-
ctx->jmp_armed = 0;
|
|
1256
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "JPEG dimensions exceed native int range");
|
|
1257
|
-
return 0;
|
|
1258
|
-
}
|
|
1418
|
+
if (cinfo.image_width > (JDIMENSION)INT_MAX || cinfo.image_height > (JDIMENSION)INT_MAX)
|
|
1419
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "JPEG dimensions exceed native int range");
|
|
1259
1420
|
|
|
1260
1421
|
if (cinfo.num_components == 4 || cinfo.jpeg_color_space == JCS_CMYK ||
|
|
1261
|
-
cinfo.jpeg_color_space == JCS_YCCK)
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
1265
|
-
"CMYK/YCCK JPEG input is not supported in this release");
|
|
1266
|
-
return 0;
|
|
1267
|
-
}
|
|
1422
|
+
cinfo.jpeg_color_space == JCS_YCCK)
|
|
1423
|
+
IP_FAIL_GOTO(ctx, IP_ERR_UNSUPPORTED,
|
|
1424
|
+
"CMYK/YCCK JPEG input is not supported in this release");
|
|
1268
1425
|
|
|
1269
1426
|
int ch = cinfo.num_components == 1 ? 1 : 3;
|
|
1270
1427
|
|
|
1271
1428
|
ctx->width = (int)cinfo.image_width;
|
|
1272
1429
|
ctx->height = (int)cinfo.image_height;
|
|
1273
1430
|
ctx->channels = ch;
|
|
1274
|
-
if (!ip_checked_image_size(ctx->width, ctx->height, ctx->channels, &ctx->decoded_bytes))
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "decoded image size overflows native size");
|
|
1278
|
-
return 0;
|
|
1279
|
-
}
|
|
1431
|
+
if (!ip_checked_image_size(ctx->width, ctx->height, ctx->channels, &ctx->decoded_bytes))
|
|
1432
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "decoded image size overflows native size");
|
|
1433
|
+
|
|
1280
1434
|
validate_limits_for_pixels(ctx);
|
|
1281
|
-
if (ctx->status != IP_OK)
|
|
1282
|
-
|
|
1283
|
-
ctx->jmp_armed = 0;
|
|
1284
|
-
return 0;
|
|
1285
|
-
}
|
|
1435
|
+
if (ctx->status != IP_OK)
|
|
1436
|
+
goto fail;
|
|
1286
1437
|
|
|
1287
1438
|
cinfo.out_color_space = ch == 1 ? JCS_GRAYSCALE : JCS_RGB;
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
#if defined(IMAGE_PACK_HAS_SIMD)
|
|
1291
|
-
cinfo.dct_method = JDCT_ISLOW;
|
|
1292
|
-
#else
|
|
1293
|
-
cinfo.dct_method = JDCT_FASTEST;
|
|
1294
|
-
#endif
|
|
1295
|
-
cinfo.do_fancy_upsampling = FALSE;
|
|
1296
|
-
cinfo.do_block_smoothing = FALSE;
|
|
1297
|
-
cinfo.quantize_colors = FALSE;
|
|
1298
|
-
cinfo.two_pass_quantize = FALSE;
|
|
1299
|
-
cinfo.dither_mode = JDITHER_NONE;
|
|
1300
|
-
}
|
|
1439
|
+
if (fast_decode_mode)
|
|
1440
|
+
ip_apply_fast_decode(&cinfo);
|
|
1301
1441
|
|
|
1302
1442
|
jpeg_start_decompress(&cinfo);
|
|
1303
1443
|
|
|
1304
|
-
if (!ctx->strip_metadata)
|
|
1305
|
-
|
|
1306
|
-
jpeg_destroy_decompress(&cinfo);
|
|
1307
|
-
ctx->jmp_armed = 0;
|
|
1308
|
-
return 0;
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1444
|
+
if (!ctx->strip_metadata && !ip_save_markers_from_decompress(ctx, &cinfo))
|
|
1445
|
+
goto fail;
|
|
1311
1446
|
|
|
1312
1447
|
size_t row_stride = 0;
|
|
1313
1448
|
size_t size = 0;
|
|
1314
1449
|
if (!ip_checked_mul_size((size_t)cinfo.output_width, (size_t)cinfo.output_components,
|
|
1315
1450
|
&row_stride) ||
|
|
1316
|
-
!ip_checked_mul_size(row_stride, (size_t)cinfo.output_height, &size))
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
ctx->transient_decode_buf = (unsigned char *)ip_malloc_hot(size);
|
|
1323
|
-
if (!ctx->transient_decode_buf && size > 0) {
|
|
1324
|
-
jpeg_destroy_decompress(&cinfo);
|
|
1325
|
-
ctx->jmp_armed = 0;
|
|
1326
|
-
ip_context_set_error(ctx, IP_ERR_OOM, "failed to allocate decoded pixel buffer");
|
|
1327
|
-
return 0;
|
|
1328
|
-
}
|
|
1451
|
+
!ip_checked_mul_size(row_stride, (size_t)cinfo.output_height, &size))
|
|
1452
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "decoded image buffer size overflow");
|
|
1453
|
+
|
|
1454
|
+
ctx->transient_decode_buf = (unsigned char *)malloc(size);
|
|
1455
|
+
if (!ctx->transient_decode_buf && size > 0)
|
|
1456
|
+
IP_FAIL_GOTO(ctx, IP_ERR_OOM, "failed to allocate decoded pixel buffer");
|
|
1329
1457
|
|
|
1330
1458
|
while (cinfo.output_scanline < cinfo.output_height) {
|
|
1331
|
-
if (
|
|
1332
|
-
|
|
1333
|
-
jpeg_abort_decompress(&cinfo);
|
|
1334
|
-
jpeg_destroy_decompress(&cinfo);
|
|
1335
|
-
free(ctx->transient_decode_buf);
|
|
1336
|
-
ctx->transient_decode_buf = NULL;
|
|
1337
|
-
ctx->jmp_armed = 0;
|
|
1338
|
-
return 0;
|
|
1339
|
-
}
|
|
1459
|
+
if (atomic_load(&ctx->cancelled))
|
|
1460
|
+
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "JPEG decode cancelled");
|
|
1340
1461
|
|
|
1341
1462
|
JSAMPROW rows[16];
|
|
1342
1463
|
JDIMENSION batch = cinfo.output_height - cinfo.output_scanline;
|
|
1343
1464
|
if (batch > 16)
|
|
1344
1465
|
batch = 16;
|
|
1345
1466
|
|
|
1346
|
-
for (JDIMENSION i = 0; i < batch; i++)
|
|
1467
|
+
for (JDIMENSION i = 0; i < batch; i++)
|
|
1347
1468
|
rows[i] =
|
|
1348
1469
|
ctx->transient_decode_buf + ((size_t)(cinfo.output_scanline + i) * row_stride);
|
|
1349
|
-
}
|
|
1350
1470
|
|
|
1351
1471
|
jpeg_read_scanlines(&cinfo, rows, batch);
|
|
1352
1472
|
}
|
|
@@ -1366,6 +1486,15 @@ static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, i
|
|
|
1366
1486
|
free(buf);
|
|
1367
1487
|
return 0;
|
|
1368
1488
|
}
|
|
1489
|
+
|
|
1490
|
+
ctx->width = out_width;
|
|
1491
|
+
ctx->height = out_height;
|
|
1492
|
+
ctx->channels = ch;
|
|
1493
|
+
validate_limits_for_pixels(ctx);
|
|
1494
|
+
if (ctx->status != IP_OK) {
|
|
1495
|
+
free(buf);
|
|
1496
|
+
return 0;
|
|
1497
|
+
}
|
|
1369
1498
|
}
|
|
1370
1499
|
|
|
1371
1500
|
*pixels = buf;
|
|
@@ -1373,6 +1502,13 @@ static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, i
|
|
|
1373
1502
|
*height = out_height;
|
|
1374
1503
|
*channels = ch;
|
|
1375
1504
|
return 1;
|
|
1505
|
+
|
|
1506
|
+
fail:
|
|
1507
|
+
jpeg_destroy_decompress(&cinfo);
|
|
1508
|
+
free(ctx->transient_decode_buf);
|
|
1509
|
+
ctx->transient_decode_buf = NULL;
|
|
1510
|
+
ctx->jmp_armed = 0;
|
|
1511
|
+
return 0;
|
|
1376
1512
|
}
|
|
1377
1513
|
|
|
1378
1514
|
static int compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_size_mode) {
|
|
@@ -1407,7 +1543,6 @@ static void ip_clear_output_buffer(ip_context_t *ctx) {
|
|
|
1407
1543
|
|
|
1408
1544
|
ctx->output_data = NULL;
|
|
1409
1545
|
ctx->output_size = 0;
|
|
1410
|
-
ctx->output_capacity = 0;
|
|
1411
1546
|
ctx->output_owner = IP_OUTPUT_OWNER_NONE;
|
|
1412
1547
|
}
|
|
1413
1548
|
|
|
@@ -1417,49 +1552,29 @@ static int ip_decode_jpeg_to_luma_buffer(ip_context_t *ctx, const unsigned char
|
|
|
1417
1552
|
ip_jpeg_error_mgr jerr;
|
|
1418
1553
|
memset(&cinfo, 0, sizeof(cinfo));
|
|
1419
1554
|
memset(&jerr, 0, sizeof(jerr));
|
|
1420
|
-
|
|
1421
|
-
cinfo.err = jpeg_std_error(&jerr.pub);
|
|
1422
|
-
jerr.pub.error_exit = ip_jpeg_invalid_error_exit;
|
|
1423
|
-
jerr.ctx = ctx;
|
|
1555
|
+
cinfo.err = ip_use_error(&jerr, ctx, ip_jpeg_invalid_error_exit);
|
|
1424
1556
|
ctx->transient_decode_buf = NULL;
|
|
1425
1557
|
|
|
1426
1558
|
ctx->jmp_armed = 1;
|
|
1427
1559
|
if (setjmp(ctx->jmpbuf)) {
|
|
1428
|
-
ctx->jmp_armed = 0;
|
|
1429
|
-
jpeg_destroy_decompress(&cinfo);
|
|
1430
|
-
free(ctx->transient_decode_buf);
|
|
1431
|
-
ctx->transient_decode_buf = NULL;
|
|
1432
1560
|
if (ctx->status == IP_OK)
|
|
1433
1561
|
ip_context_set_error(ctx, IP_ERR_INVALID_IMAGE, "JPEG luma decode failed");
|
|
1434
|
-
|
|
1562
|
+
goto fail;
|
|
1435
1563
|
}
|
|
1436
1564
|
|
|
1437
1565
|
jpeg_create_decompress(&cinfo);
|
|
1438
1566
|
jpeg_mem_src(&cinfo, data, (unsigned long)size);
|
|
1439
1567
|
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
jpeg_destroy_decompress(&cinfo);
|
|
1443
|
-
ctx->jmp_armed = 0;
|
|
1444
|
-
ip_context_set_error(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1445
|
-
return 0;
|
|
1446
|
-
}
|
|
1568
|
+
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
|
|
1569
|
+
IP_FAIL_GOTO(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1447
1570
|
|
|
1448
|
-
if (cinfo.image_width > (JDIMENSION)INT_MAX || cinfo.image_height > (JDIMENSION)INT_MAX)
|
|
1449
|
-
|
|
1450
|
-
ctx->jmp_armed = 0;
|
|
1451
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "JPEG dimensions exceed native int range");
|
|
1452
|
-
return 0;
|
|
1453
|
-
}
|
|
1571
|
+
if (cinfo.image_width > (JDIMENSION)INT_MAX || cinfo.image_height > (JDIMENSION)INT_MAX)
|
|
1572
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "JPEG dimensions exceed native int range");
|
|
1454
1573
|
|
|
1455
1574
|
if (cinfo.num_components == 4 || cinfo.jpeg_color_space == JCS_CMYK ||
|
|
1456
|
-
cinfo.jpeg_color_space == JCS_YCCK)
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
1460
|
-
"CMYK/YCCK JPEG input is not supported in this release");
|
|
1461
|
-
return 0;
|
|
1462
|
-
}
|
|
1575
|
+
cinfo.jpeg_color_space == JCS_YCCK)
|
|
1576
|
+
IP_FAIL_GOTO(ctx, IP_ERR_UNSUPPORTED,
|
|
1577
|
+
"CMYK/YCCK JPEG input is not supported in this release");
|
|
1463
1578
|
|
|
1464
1579
|
int old_width = ctx->width;
|
|
1465
1580
|
int old_height = ctx->height;
|
|
@@ -1474,10 +1589,7 @@ static int ip_decode_jpeg_to_luma_buffer(ip_context_t *ctx, const unsigned char
|
|
|
1474
1589
|
ctx->height = old_height;
|
|
1475
1590
|
ctx->channels = old_channels;
|
|
1476
1591
|
ctx->decoded_bytes = old_decoded_bytes;
|
|
1477
|
-
|
|
1478
|
-
ctx->jmp_armed = 0;
|
|
1479
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "decoded luma buffer size overflows native size");
|
|
1480
|
-
return 0;
|
|
1592
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "decoded luma buffer size overflows native size");
|
|
1481
1593
|
}
|
|
1482
1594
|
validate_limits_for_pixels(ctx);
|
|
1483
1595
|
ctx->width = old_width;
|
|
@@ -1486,63 +1598,35 @@ static int ip_decode_jpeg_to_luma_buffer(ip_context_t *ctx, const unsigned char
|
|
|
1486
1598
|
size_t luma_size = ctx->decoded_bytes;
|
|
1487
1599
|
ctx->decoded_bytes = old_decoded_bytes;
|
|
1488
1600
|
|
|
1489
|
-
if (ctx->status != IP_OK)
|
|
1490
|
-
|
|
1491
|
-
ctx->jmp_armed = 0;
|
|
1492
|
-
return 0;
|
|
1493
|
-
}
|
|
1601
|
+
if (ctx->status != IP_OK)
|
|
1602
|
+
goto fail;
|
|
1494
1603
|
|
|
1495
1604
|
cinfo.out_color_space = JCS_GRAYSCALE;
|
|
1496
|
-
|
|
1497
|
-
cinfo.dct_method = JDCT_ISLOW;
|
|
1498
|
-
#else
|
|
1499
|
-
cinfo.dct_method = JDCT_FASTEST;
|
|
1500
|
-
#endif
|
|
1501
|
-
cinfo.do_fancy_upsampling = FALSE;
|
|
1502
|
-
cinfo.do_block_smoothing = FALSE;
|
|
1503
|
-
cinfo.quantize_colors = FALSE;
|
|
1504
|
-
cinfo.two_pass_quantize = FALSE;
|
|
1505
|
-
cinfo.dither_mode = JDITHER_NONE;
|
|
1605
|
+
ip_apply_fast_decode(&cinfo);
|
|
1506
1606
|
|
|
1507
1607
|
jpeg_start_decompress(&cinfo);
|
|
1508
1608
|
|
|
1509
1609
|
size_t luma_stride = (size_t)cinfo.output_width;
|
|
1510
1610
|
if (cinfo.output_components != 1 || luma_stride == 0 ||
|
|
1511
|
-
luma_size != luma_stride * (size_t)cinfo.output_height)
|
|
1512
|
-
|
|
1513
|
-
ctx->jmp_armed = 0;
|
|
1514
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "decoded luma buffer size mismatch");
|
|
1515
|
-
return 0;
|
|
1516
|
-
}
|
|
1611
|
+
luma_size != luma_stride * (size_t)cinfo.output_height)
|
|
1612
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "decoded luma buffer size mismatch");
|
|
1517
1613
|
|
|
1518
|
-
ctx->transient_decode_buf = (unsigned char *)
|
|
1519
|
-
if (!ctx->transient_decode_buf && luma_size > 0)
|
|
1520
|
-
|
|
1521
|
-
ctx->jmp_armed = 0;
|
|
1522
|
-
ip_context_set_error(ctx, IP_ERR_OOM, "failed to allocate luma buffer");
|
|
1523
|
-
return 0;
|
|
1524
|
-
}
|
|
1614
|
+
ctx->transient_decode_buf = (unsigned char *)malloc(luma_size);
|
|
1615
|
+
if (!ctx->transient_decode_buf && luma_size > 0)
|
|
1616
|
+
IP_FAIL_GOTO(ctx, IP_ERR_OOM, "failed to allocate luma buffer");
|
|
1525
1617
|
|
|
1526
1618
|
while (cinfo.output_scanline < cinfo.output_height) {
|
|
1527
|
-
if (
|
|
1528
|
-
|
|
1529
|
-
jpeg_abort_decompress(&cinfo);
|
|
1530
|
-
jpeg_destroy_decompress(&cinfo);
|
|
1531
|
-
free(ctx->transient_decode_buf);
|
|
1532
|
-
ctx->transient_decode_buf = NULL;
|
|
1533
|
-
ctx->jmp_armed = 0;
|
|
1534
|
-
return 0;
|
|
1535
|
-
}
|
|
1619
|
+
if (atomic_load(&ctx->cancelled))
|
|
1620
|
+
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "JPEG luma decode cancelled");
|
|
1536
1621
|
|
|
1537
1622
|
JSAMPROW rows[16];
|
|
1538
1623
|
JDIMENSION batch = cinfo.output_height - cinfo.output_scanline;
|
|
1539
1624
|
if (batch > 16)
|
|
1540
1625
|
batch = 16;
|
|
1541
1626
|
|
|
1542
|
-
for (JDIMENSION i = 0; i < batch; i++)
|
|
1627
|
+
for (JDIMENSION i = 0; i < batch; i++)
|
|
1543
1628
|
rows[i] =
|
|
1544
1629
|
ctx->transient_decode_buf + ((size_t)(cinfo.output_scanline + i) * luma_stride);
|
|
1545
|
-
}
|
|
1546
1630
|
|
|
1547
1631
|
jpeg_read_scanlines(&cinfo, rows, batch);
|
|
1548
1632
|
}
|
|
@@ -1559,6 +1643,13 @@ static int ip_decode_jpeg_to_luma_buffer(ip_context_t *ctx, const unsigned char
|
|
|
1559
1643
|
*width = out_width;
|
|
1560
1644
|
*height = out_height;
|
|
1561
1645
|
return 1;
|
|
1646
|
+
|
|
1647
|
+
fail:
|
|
1648
|
+
jpeg_destroy_decompress(&cinfo);
|
|
1649
|
+
free(ctx->transient_decode_buf);
|
|
1650
|
+
ctx->transient_decode_buf = NULL;
|
|
1651
|
+
ctx->jmp_armed = 0;
|
|
1652
|
+
return 0;
|
|
1562
1653
|
}
|
|
1563
1654
|
|
|
1564
1655
|
static unsigned char *ip_build_luma_buffer(ip_context_t *ctx, const unsigned char *pixels,
|
|
@@ -1569,7 +1660,7 @@ static unsigned char *ip_build_luma_buffer(ip_context_t *ctx, const unsigned cha
|
|
|
1569
1660
|
return NULL;
|
|
1570
1661
|
}
|
|
1571
1662
|
|
|
1572
|
-
unsigned char *luma = (unsigned char *)
|
|
1663
|
+
unsigned char *luma = (unsigned char *)malloc(count);
|
|
1573
1664
|
if (!luma) {
|
|
1574
1665
|
ip_context_set_error(ctx, IP_ERR_OOM, "failed to allocate luma buffer");
|
|
1575
1666
|
return NULL;
|
|
@@ -1588,7 +1679,7 @@ static unsigned char *ip_build_luma_buffer(ip_context_t *ctx, const unsigned cha
|
|
|
1588
1679
|
unsigned int r = src[i * 3 + 0];
|
|
1589
1680
|
unsigned int g = src[i * 3 + 1];
|
|
1590
1681
|
unsigned int b = src[i * 3 + 2];
|
|
1591
|
-
dst[i] = (unsigned char)((
|
|
1682
|
+
dst[i] = (unsigned char)((19595u * r + 38470u * g + 7471u * b + 32768u) >> 16);
|
|
1592
1683
|
}
|
|
1593
1684
|
return luma;
|
|
1594
1685
|
}
|
|
@@ -1597,15 +1688,15 @@ static unsigned char *ip_build_luma_buffer(ip_context_t *ctx, const unsigned cha
|
|
|
1597
1688
|
unsigned int r = src[i * 4 + 0];
|
|
1598
1689
|
unsigned int g = src[i * 4 + 1];
|
|
1599
1690
|
unsigned int b = src[i * 4 + 2];
|
|
1600
|
-
dst[i] = (unsigned char)((
|
|
1691
|
+
dst[i] = (unsigned char)((19595u * r + 38470u * g + 7471u * b + 32768u) >> 16);
|
|
1601
1692
|
}
|
|
1602
1693
|
return luma;
|
|
1603
1694
|
}
|
|
1604
1695
|
|
|
1605
1696
|
static double ip_ssim_window_score_double(int32_t n, int32_t sum_a, int32_t sum_b, int32_t sum_a2,
|
|
1606
1697
|
int32_t sum_b2, int32_t sum_ab) {
|
|
1607
|
-
const double c1 = 6.5025;
|
|
1608
|
-
const double c2 = 58.5225;
|
|
1698
|
+
const double c1 = 6.5025;
|
|
1699
|
+
const double c2 = 58.5225;
|
|
1609
1700
|
|
|
1610
1701
|
double inv_n = 1.0 / (double)n;
|
|
1611
1702
|
double mean_a = (double)sum_a * inv_n;
|
|
@@ -1758,8 +1849,7 @@ static int guarded_compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_
|
|
|
1758
1849
|
}
|
|
1759
1850
|
|
|
1760
1851
|
if (reference_channels == 4) {
|
|
1761
|
-
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
1762
|
-
"min_ssim is not supported for RGBA input in v0.2.2");
|
|
1852
|
+
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED, "min_ssim is not supported for RGBA input");
|
|
1763
1853
|
return 0;
|
|
1764
1854
|
}
|
|
1765
1855
|
|
|
@@ -1778,7 +1868,7 @@ static int guarded_compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_
|
|
|
1778
1868
|
int best_seen_quality = 0;
|
|
1779
1869
|
|
|
1780
1870
|
while (search_low <= search_high) {
|
|
1781
|
-
if (
|
|
1871
|
+
if (atomic_load(&ctx->cancelled)) {
|
|
1782
1872
|
free(reference_luma);
|
|
1783
1873
|
free(best_jpeg);
|
|
1784
1874
|
ip_context_set_error(ctx, IP_ERR_CANCELLED, "SSIM-guarded JPEG encode cancelled");
|
|
@@ -1799,7 +1889,6 @@ static int guarded_compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_
|
|
|
1799
1889
|
size_t candidate_jpeg_size = ctx->output_size;
|
|
1800
1890
|
ctx->output_data = NULL;
|
|
1801
1891
|
ctx->output_size = 0;
|
|
1802
|
-
ctx->output_capacity = 0;
|
|
1803
1892
|
ctx->output_owner = IP_OUTPUT_OWNER_NONE;
|
|
1804
1893
|
|
|
1805
1894
|
unsigned char *candidate_luma = NULL;
|
|
@@ -1865,7 +1954,6 @@ static int guarded_compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_
|
|
|
1865
1954
|
|
|
1866
1955
|
ctx->output_data = best_jpeg;
|
|
1867
1956
|
ctx->output_size = best_jpeg_size;
|
|
1868
|
-
ctx->output_capacity = best_jpeg_size;
|
|
1869
1957
|
ctx->output_owner = IP_OUTPUT_OWNER_MALLOC;
|
|
1870
1958
|
return 1;
|
|
1871
1959
|
}
|
|
@@ -1896,20 +1984,7 @@ static int ip_validate_lossless_optimize_header(ip_context_t *ctx,
|
|
|
1896
1984
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "max_width/max_height must be >= 0");
|
|
1897
1985
|
return 0;
|
|
1898
1986
|
}
|
|
1899
|
-
|
|
1900
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "image width exceeds max_width");
|
|
1901
|
-
return 0;
|
|
1902
|
-
}
|
|
1903
|
-
if (ctx->max_height > 0 && ctx->height > ctx->max_height) {
|
|
1904
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "image height exceeds max_height");
|
|
1905
|
-
return 0;
|
|
1906
|
-
}
|
|
1907
|
-
if (ctx->max_pixels > 0 && (uint64_t)ctx->width * (uint64_t)ctx->height > ctx->max_pixels) {
|
|
1908
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "image pixels exceed max_pixels");
|
|
1909
|
-
return 0;
|
|
1910
|
-
}
|
|
1911
|
-
|
|
1912
|
-
return 1;
|
|
1987
|
+
return ip_check_max_dimension_limits(ctx);
|
|
1913
1988
|
}
|
|
1914
1989
|
|
|
1915
1990
|
static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
@@ -1926,24 +2001,14 @@ static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
|
1926
2001
|
memset(&dsterr, 0, sizeof(dsterr));
|
|
1927
2002
|
ctx->transient_jpeg_buf = NULL;
|
|
1928
2003
|
|
|
1929
|
-
srcinfo.err =
|
|
1930
|
-
|
|
1931
|
-
srcerr.ctx = ctx;
|
|
1932
|
-
|
|
1933
|
-
dstinfo.err = jpeg_std_error(&dsterr.pub);
|
|
1934
|
-
dsterr.pub.error_exit = ip_jpeg_encode_error_exit;
|
|
1935
|
-
dsterr.ctx = ctx;
|
|
2004
|
+
srcinfo.err = ip_use_error(&srcerr, ctx, ip_jpeg_invalid_error_exit);
|
|
2005
|
+
dstinfo.err = ip_use_error(&dsterr, ctx, ip_jpeg_encode_error_exit);
|
|
1936
2006
|
|
|
1937
2007
|
ctx->jmp_armed = 1;
|
|
1938
2008
|
if (setjmp(ctx->jmpbuf)) {
|
|
1939
|
-
ctx->jmp_armed = 0;
|
|
1940
|
-
jpeg_destroy_compress(&dstinfo);
|
|
1941
|
-
jpeg_destroy_decompress(&srcinfo);
|
|
1942
|
-
free(ctx->transient_jpeg_buf);
|
|
1943
|
-
ctx->transient_jpeg_buf = NULL;
|
|
1944
2009
|
if (ctx->status == IP_OK)
|
|
1945
2010
|
ip_context_set_error(ctx, IP_ERR_ENCODE, "lossless JPEG optimize failed");
|
|
1946
|
-
|
|
2011
|
+
goto fail;
|
|
1947
2012
|
}
|
|
1948
2013
|
|
|
1949
2014
|
jpeg_create_decompress(&srcinfo);
|
|
@@ -1951,47 +2016,23 @@ static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
|
1951
2016
|
jpeg_mem_src(&srcinfo, ctx->input_data, (unsigned long)ctx->input_size);
|
|
1952
2017
|
ip_setup_marker_saving(&srcinfo, ctx->strip_metadata);
|
|
1953
2018
|
|
|
1954
|
-
if (
|
|
1955
|
-
|
|
1956
|
-
jpeg_destroy_compress(&dstinfo);
|
|
1957
|
-
jpeg_destroy_decompress(&srcinfo);
|
|
1958
|
-
ctx->jmp_armed = 0;
|
|
1959
|
-
return 0;
|
|
1960
|
-
}
|
|
2019
|
+
if (atomic_load(&ctx->cancelled))
|
|
2020
|
+
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "lossless JPEG optimize cancelled");
|
|
1961
2021
|
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
jpeg_destroy_compress(&dstinfo);
|
|
1965
|
-
jpeg_destroy_decompress(&srcinfo);
|
|
1966
|
-
ctx->jmp_armed = 0;
|
|
1967
|
-
ip_context_set_error(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1968
|
-
return 0;
|
|
1969
|
-
}
|
|
2022
|
+
if (jpeg_read_header(&srcinfo, TRUE) != JPEG_HEADER_OK)
|
|
2023
|
+
IP_FAIL_GOTO(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1970
2024
|
|
|
1971
2025
|
ctx->source_orientation = ip_read_exif_orientation_from_decompress(&srcinfo);
|
|
1972
|
-
if (ctx->strip_metadata && ctx->source_orientation > 1)
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
1977
|
-
"lossless optimize cannot strip EXIF Orientation without changing "
|
|
1978
|
-
"visual orientation; use strip_metadata: false or ImagePack.compress");
|
|
1979
|
-
return 0;
|
|
1980
|
-
}
|
|
2026
|
+
if (ctx->strip_metadata && ctx->source_orientation > 1)
|
|
2027
|
+
IP_FAIL_GOTO(ctx, IP_ERR_UNSUPPORTED,
|
|
2028
|
+
"lossless optimize cannot strip EXIF Orientation without changing "
|
|
2029
|
+
"visual orientation; use strip_metadata: false or ImagePack.compress");
|
|
1981
2030
|
|
|
1982
|
-
if (!ip_validate_lossless_optimize_header(ctx, &srcinfo))
|
|
1983
|
-
|
|
1984
|
-
jpeg_destroy_decompress(&srcinfo);
|
|
1985
|
-
ctx->jmp_armed = 0;
|
|
1986
|
-
return 0;
|
|
1987
|
-
}
|
|
2031
|
+
if (!ip_validate_lossless_optimize_header(ctx, &srcinfo))
|
|
2032
|
+
goto fail;
|
|
1988
2033
|
|
|
1989
|
-
if (!ctx->strip_metadata && !ip_save_markers_from_decompress(ctx, &srcinfo))
|
|
1990
|
-
|
|
1991
|
-
jpeg_destroy_decompress(&srcinfo);
|
|
1992
|
-
ctx->jmp_armed = 0;
|
|
1993
|
-
return 0;
|
|
1994
|
-
}
|
|
2034
|
+
if (!ctx->strip_metadata && !ip_save_markers_from_decompress(ctx, &srcinfo))
|
|
2035
|
+
goto fail;
|
|
1995
2036
|
|
|
1996
2037
|
coef_arrays = jpeg_read_coefficients(&srcinfo);
|
|
1997
2038
|
jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
|
|
@@ -2005,15 +2046,8 @@ static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
|
2005
2046
|
|
|
2006
2047
|
jpeg_mem_dest(&dstinfo, &ctx->transient_jpeg_buf, &jpeg_size);
|
|
2007
2048
|
|
|
2008
|
-
if (
|
|
2009
|
-
|
|
2010
|
-
jpeg_destroy_compress(&dstinfo);
|
|
2011
|
-
jpeg_destroy_decompress(&srcinfo);
|
|
2012
|
-
free(ctx->transient_jpeg_buf);
|
|
2013
|
-
ctx->transient_jpeg_buf = NULL;
|
|
2014
|
-
ctx->jmp_armed = 0;
|
|
2015
|
-
return 0;
|
|
2016
|
-
}
|
|
2049
|
+
if (atomic_load(&ctx->cancelled))
|
|
2050
|
+
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "lossless JPEG optimize cancelled");
|
|
2017
2051
|
|
|
2018
2052
|
jpeg_write_coefficients(&dstinfo, coef_arrays);
|
|
2019
2053
|
if (!ctx->strip_metadata)
|
|
@@ -2021,23 +2055,27 @@ static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
|
2021
2055
|
|
|
2022
2056
|
jpeg_finish_compress(&dstinfo);
|
|
2023
2057
|
jpeg_finish_decompress(&srcinfo);
|
|
2058
|
+
|
|
2059
|
+
if (ctx->max_output_size > 0 && (size_t)jpeg_size > ctx->max_output_size)
|
|
2060
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "output exceeds max_output_size");
|
|
2061
|
+
|
|
2024
2062
|
jpeg_destroy_compress(&dstinfo);
|
|
2025
2063
|
jpeg_destroy_decompress(&srcinfo);
|
|
2026
2064
|
ctx->jmp_armed = 0;
|
|
2027
2065
|
|
|
2028
|
-
if (ctx->max_output_size > 0 && (size_t)jpeg_size > ctx->max_output_size) {
|
|
2029
|
-
free(ctx->transient_jpeg_buf);
|
|
2030
|
-
ctx->transient_jpeg_buf = NULL;
|
|
2031
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "output exceeds max_output_size");
|
|
2032
|
-
return 0;
|
|
2033
|
-
}
|
|
2034
|
-
|
|
2035
2066
|
ctx->output_data = ctx->transient_jpeg_buf;
|
|
2036
2067
|
ctx->transient_jpeg_buf = NULL;
|
|
2037
2068
|
ctx->output_size = (size_t)jpeg_size;
|
|
2038
|
-
ctx->output_capacity = (size_t)jpeg_size;
|
|
2039
2069
|
ctx->output_owner = IP_OUTPUT_OWNER_MALLOC;
|
|
2040
2070
|
return 1;
|
|
2071
|
+
|
|
2072
|
+
fail:
|
|
2073
|
+
jpeg_destroy_compress(&dstinfo);
|
|
2074
|
+
jpeg_destroy_decompress(&srcinfo);
|
|
2075
|
+
free(ctx->transient_jpeg_buf);
|
|
2076
|
+
ctx->transient_jpeg_buf = NULL;
|
|
2077
|
+
ctx->jmp_armed = 0;
|
|
2078
|
+
return 0;
|
|
2041
2079
|
}
|
|
2042
2080
|
|
|
2043
2081
|
static int ip_jpeg_turbo_compress(ip_context_t *ctx) {
|
|
@@ -2052,6 +2090,15 @@ static int ip_mozjpeg_compress(ip_context_t *ctx) {
|
|
|
2052
2090
|
return compress_jpeg_input_with_mode(ctx, 1);
|
|
2053
2091
|
}
|
|
2054
2092
|
|
|
2093
|
+
static ip_execution_t ip_async_execution(const ip_context_t *ctx) {
|
|
2094
|
+
#if IMAGE_PACK_HAS_OFFLOAD_SAFE
|
|
2095
|
+
return (ip_offload_runtime_enabled && ctx->has_scheduler) ? IP_EXEC_OFFLOAD : IP_EXEC_NOGVL;
|
|
2096
|
+
#else
|
|
2097
|
+
(void)ctx;
|
|
2098
|
+
return IP_EXEC_NOGVL;
|
|
2099
|
+
#endif
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2055
2102
|
static void ip_resolve_execution(ip_context_t *ctx) {
|
|
2056
2103
|
if (ctx->requested_execution != IP_EXEC_AUTO) {
|
|
2057
2104
|
ctx->resolved_execution = ctx->requested_execution;
|
|
@@ -2059,12 +2106,12 @@ static void ip_resolve_execution(ip_context_t *ctx) {
|
|
|
2059
2106
|
}
|
|
2060
2107
|
|
|
2061
2108
|
if (ctx->cancellable_requested) {
|
|
2062
|
-
ctx->resolved_execution = ctx
|
|
2109
|
+
ctx->resolved_execution = ip_async_execution(ctx);
|
|
2063
2110
|
return;
|
|
2064
2111
|
}
|
|
2065
2112
|
|
|
2066
2113
|
if (ctx->ssim_guard_enabled) {
|
|
2067
|
-
ctx->resolved_execution = ctx
|
|
2114
|
+
ctx->resolved_execution = ip_async_execution(ctx);
|
|
2068
2115
|
return;
|
|
2069
2116
|
}
|
|
2070
2117
|
|
|
@@ -2074,7 +2121,7 @@ static void ip_resolve_execution(ip_context_t *ctx) {
|
|
|
2074
2121
|
return;
|
|
2075
2122
|
}
|
|
2076
2123
|
|
|
2077
|
-
ctx->resolved_execution = ctx
|
|
2124
|
+
ctx->resolved_execution = ip_async_execution(ctx);
|
|
2078
2125
|
}
|
|
2079
2126
|
|
|
2080
2127
|
static void ip_unblock_function(void *data) {
|
|
@@ -2111,11 +2158,20 @@ static int ip_run_context(ip_context_t *ctx) {
|
|
|
2111
2158
|
} else if (ctx->resolved_execution == IP_EXEC_NOGVL) {
|
|
2112
2159
|
rb_nogvl(ip_run_encode_nogvl, ctx, ip_unblock_function, ctx, 0);
|
|
2113
2160
|
} else if (ctx->resolved_execution == IP_EXEC_OFFLOAD) {
|
|
2161
|
+
#if IMAGE_PACK_HAS_OFFLOAD_SAFE
|
|
2114
2162
|
rb_nogvl(ip_run_encode_nogvl, ctx, ip_unblock_function, ctx, RB_NOGVL_OFFLOAD_SAFE);
|
|
2163
|
+
#else
|
|
2164
|
+
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
2165
|
+
"offload execution is unavailable in this runtime; it requires Ruby "
|
|
2166
|
+
">= 3.4 and IMAGE_PACK_DISABLE_OFFLOAD must not be set");
|
|
2167
|
+
#endif
|
|
2115
2168
|
} else {
|
|
2116
2169
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "invalid resolved execution mode");
|
|
2117
2170
|
}
|
|
2118
2171
|
|
|
2172
|
+
if (ctx->resolved_execution != IP_EXEC_DIRECT)
|
|
2173
|
+
rb_thread_check_ints();
|
|
2174
|
+
|
|
2119
2175
|
return ctx->status == IP_OK;
|
|
2120
2176
|
}
|
|
2121
2177
|
|
|
@@ -2134,39 +2190,57 @@ static int ip_run_optimize_context(ip_context_t *ctx) {
|
|
|
2134
2190
|
} else if (ctx->resolved_execution == IP_EXEC_NOGVL) {
|
|
2135
2191
|
rb_nogvl(ip_run_optimize_nogvl, ctx, ip_unblock_function, ctx, 0);
|
|
2136
2192
|
} else if (ctx->resolved_execution == IP_EXEC_OFFLOAD) {
|
|
2193
|
+
#if IMAGE_PACK_HAS_OFFLOAD_SAFE
|
|
2137
2194
|
rb_nogvl(ip_run_optimize_nogvl, ctx, ip_unblock_function, ctx, RB_NOGVL_OFFLOAD_SAFE);
|
|
2195
|
+
#else
|
|
2196
|
+
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
2197
|
+
"offload execution is unavailable in this runtime; it requires Ruby "
|
|
2198
|
+
">= 3.4 and IMAGE_PACK_DISABLE_OFFLOAD must not be set");
|
|
2199
|
+
#endif
|
|
2138
2200
|
} else {
|
|
2139
2201
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "invalid resolved execution mode");
|
|
2140
2202
|
}
|
|
2141
2203
|
|
|
2204
|
+
if (ctx->resolved_execution != IP_EXEC_DIRECT)
|
|
2205
|
+
rb_thread_check_ints();
|
|
2206
|
+
|
|
2142
2207
|
return ctx->status == IP_OK;
|
|
2143
2208
|
}
|
|
2144
2209
|
|
|
2145
|
-
static size_t config_size_value(VALUE config, ID id, size_t fallback) {
|
|
2210
|
+
static size_t config_size_value(VALUE config, ID id, size_t fallback, const char *name) {
|
|
2146
2211
|
VALUE value = rb_funcall(config, id, 0);
|
|
2147
2212
|
if (NIL_P(value))
|
|
2148
2213
|
return fallback;
|
|
2214
|
+
if (!RB_INTEGER_TYPE_P(value) || ip_value_negative(value)) {
|
|
2215
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "%s must be an Integer >= 0", name);
|
|
2216
|
+
}
|
|
2149
2217
|
return NUM2SIZET(value);
|
|
2150
2218
|
}
|
|
2151
2219
|
|
|
2152
|
-
static int config_int_value(VALUE config, ID id, int fallback) {
|
|
2220
|
+
static int config_int_value(VALUE config, ID id, int fallback, const char *name) {
|
|
2153
2221
|
VALUE value = rb_funcall(config, id, 0);
|
|
2154
2222
|
if (NIL_P(value))
|
|
2155
2223
|
return fallback;
|
|
2224
|
+
if (!RB_INTEGER_TYPE_P(value) || ip_value_negative(value)) {
|
|
2225
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "%s must be an Integer >= 0", name);
|
|
2226
|
+
}
|
|
2156
2227
|
return NUM2INT(value);
|
|
2157
2228
|
}
|
|
2158
2229
|
|
|
2159
2230
|
static void apply_configuration(VALUE self, ip_context_t *ctx) {
|
|
2160
2231
|
VALUE config = rb_funcall(self, id_configuration, 0);
|
|
2161
|
-
ctx->direct_input_threshold =
|
|
2162
|
-
|
|
2163
|
-
ctx->direct_pixel_threshold =
|
|
2164
|
-
|
|
2165
|
-
ctx->max_pixels =
|
|
2166
|
-
|
|
2167
|
-
ctx->
|
|
2168
|
-
ctx->
|
|
2169
|
-
ctx->
|
|
2232
|
+
ctx->direct_input_threshold = config_size_value(
|
|
2233
|
+
config, id_direct_input_threshold, ctx->direct_input_threshold, "direct_input_threshold");
|
|
2234
|
+
ctx->direct_pixel_threshold = config_size_value(
|
|
2235
|
+
config, id_direct_pixel_threshold, ctx->direct_pixel_threshold, "direct_pixel_threshold");
|
|
2236
|
+
ctx->max_pixels =
|
|
2237
|
+
(uint64_t)config_size_value(config, id_max_pixels, (size_t)ctx->max_pixels, "max_pixels");
|
|
2238
|
+
ctx->max_width = config_int_value(config, id_max_width, ctx->max_width, "max_width");
|
|
2239
|
+
ctx->max_height = config_int_value(config, id_max_height, ctx->max_height, "max_height");
|
|
2240
|
+
ctx->max_output_size =
|
|
2241
|
+
config_size_value(config, id_max_output_size, ctx->max_output_size, "max_output_size");
|
|
2242
|
+
ctx->max_input_size =
|
|
2243
|
+
config_size_value(config, id_max_input_size, ctx->max_input_size, "max_input_size");
|
|
2170
2244
|
}
|
|
2171
2245
|
|
|
2172
2246
|
static void validate_limits_for_pixels(ip_context_t *ctx) {
|
|
@@ -2180,18 +2254,8 @@ static void validate_limits_for_pixels(ip_context_t *ctx) {
|
|
|
2180
2254
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "max_width/max_height must be >= 0");
|
|
2181
2255
|
return;
|
|
2182
2256
|
}
|
|
2183
|
-
if (ctx
|
|
2184
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "image width exceeds max_width");
|
|
2257
|
+
if (!ip_check_max_dimension_limits(ctx))
|
|
2185
2258
|
return;
|
|
2186
|
-
}
|
|
2187
|
-
if (ctx->max_height > 0 && ctx->height > ctx->max_height) {
|
|
2188
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "image height exceeds max_height");
|
|
2189
|
-
return;
|
|
2190
|
-
}
|
|
2191
|
-
if (ctx->max_pixels > 0 && (uint64_t)ctx->width * (uint64_t)ctx->height > ctx->max_pixels) {
|
|
2192
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "image pixels exceed max_pixels");
|
|
2193
|
-
return;
|
|
2194
|
-
}
|
|
2195
2259
|
if (!ip_checked_image_size(ctx->width, ctx->height, ctx->channels, &decoded_bytes)) {
|
|
2196
2260
|
ip_context_set_error(ctx, IP_ERR_LIMIT, "decoded image size overflows native size");
|
|
2197
2261
|
return;
|
|
@@ -2221,32 +2285,46 @@ static VALUE ip_compress_jpeg_entry_body(VALUE ptr) {
|
|
|
2221
2285
|
ctx->requested_execution = ip_parse_execution(call->execution);
|
|
2222
2286
|
ctx->cancellable_requested = ip_bool_value(call->cancellable);
|
|
2223
2287
|
ctx->has_scheduler = ip_bool_value(call->has_scheduler);
|
|
2288
|
+
ctx->strict = ip_bool_value(call->strict);
|
|
2224
2289
|
apply_configuration(call->self, ctx);
|
|
2225
2290
|
|
|
2226
|
-
|
|
2227
|
-
|
|
2291
|
+
ip_input_kind_t in_kind = ip_parse_input_kind(call->input_kind);
|
|
2292
|
+
if (!ip_prepare_output_path(ctx, call->output, out_kind) ||
|
|
2293
|
+
!ip_prepare_input_bytes(ctx, call->input, in_kind)) {
|
|
2228
2294
|
ip_raise_for_status(ctx);
|
|
2229
2295
|
rb_raise(rb_eImagePackInvalidArgumentError, "invalid JPEG input");
|
|
2230
2296
|
}
|
|
2231
2297
|
|
|
2232
2298
|
if (ctx->requested_execution == IP_EXEC_AUTO && ctx->input_size < ctx->direct_input_threshold &&
|
|
2233
|
-
!ip_inspect_jpeg_header(ctx)) {
|
|
2299
|
+
!ip_inspect_jpeg_header(ctx, 0)) {
|
|
2234
2300
|
ip_raise_for_status(ctx);
|
|
2235
2301
|
rb_raise(rb_eImagePackInvalidImageError, "invalid JPEG input");
|
|
2236
2302
|
}
|
|
2237
2303
|
|
|
2304
|
+
ip_resolve_execution(ctx);
|
|
2305
|
+
if (!ip_ensure_owned_input_for_async(ctx, call->input, in_kind)) {
|
|
2306
|
+
ip_raise_for_status(ctx);
|
|
2307
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "invalid JPEG input");
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2238
2310
|
ip_run_context(ctx);
|
|
2311
|
+
if (ip_bool_value(call->report)) {
|
|
2312
|
+
VALUE output_value = ip_finish_output(ctx, out_kind);
|
|
2313
|
+
return ip_build_report(ctx, output_value);
|
|
2314
|
+
}
|
|
2239
2315
|
return ip_finish_output(ctx, out_kind);
|
|
2240
2316
|
}
|
|
2241
2317
|
|
|
2242
2318
|
static VALUE ip_compress_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
|
|
2243
2319
|
VALUE output_kind, VALUE algo, VALUE quality, VALUE min_ssim,
|
|
2244
2320
|
VALUE mozjpeg_trellis, VALUE progressive, VALUE strip_metadata,
|
|
2245
|
-
VALUE execution, VALUE cancellable, VALUE has_scheduler
|
|
2321
|
+
VALUE execution, VALUE cancellable, VALUE has_scheduler,
|
|
2322
|
+
VALUE report, VALUE strict) {
|
|
2246
2323
|
ip_compress_jpeg_call_t call = {
|
|
2247
2324
|
self, input, input_kind, output, output_kind,
|
|
2248
2325
|
algo, quality, min_ssim, mozjpeg_trellis, progressive,
|
|
2249
|
-
strip_metadata, execution, cancellable, has_scheduler,
|
|
2326
|
+
strip_metadata, execution, cancellable, has_scheduler, report,
|
|
2327
|
+
strict, NULL};
|
|
2250
2328
|
return rb_ensure(ip_compress_jpeg_entry_body, (VALUE)&call, ip_call_cleanup, (VALUE)&call.ctx);
|
|
2251
2329
|
}
|
|
2252
2330
|
|
|
@@ -2265,16 +2343,18 @@ static VALUE ip_compress_pixels_entry_body(VALUE ptr) {
|
|
|
2265
2343
|
ctx->min_ssim = NUM2DBL(call->min_ssim);
|
|
2266
2344
|
ctx->ssim_guard_enabled = ctx->min_ssim > 0.0;
|
|
2267
2345
|
ip_validate_min_ssim_or_raise(ctx);
|
|
2346
|
+
ctx->mozjpeg_trellis_enabled = ip_bool_value(call->mozjpeg_trellis);
|
|
2268
2347
|
ctx->progressive = ip_bool_value(call->progressive);
|
|
2269
2348
|
ctx->strip_metadata = 1;
|
|
2270
2349
|
ctx->requested_execution = ip_parse_execution(call->execution);
|
|
2271
2350
|
ctx->cancellable_requested = ip_bool_value(call->cancellable);
|
|
2272
2351
|
ctx->has_scheduler = ip_bool_value(call->has_scheduler);
|
|
2352
|
+
ctx->strict = ip_bool_value(call->strict);
|
|
2273
2353
|
apply_configuration(call->self, ctx);
|
|
2274
2354
|
|
|
2275
|
-
if (!
|
|
2276
|
-
|
|
2277
|
-
|
|
2355
|
+
if (!ip_prepare_output_path(ctx, call->output, out_kind) ||
|
|
2356
|
+
!ip_prepare_pixels(ctx, call->buffer, NUM2INT(call->width), NUM2INT(call->height),
|
|
2357
|
+
NUM2INT(call->channels), ip_bool_value(call->exact_size))) {
|
|
2278
2358
|
ip_raise_for_status(ctx);
|
|
2279
2359
|
rb_raise(rb_eImagePackInvalidArgumentError, "invalid pixel input");
|
|
2280
2360
|
}
|
|
@@ -2283,17 +2363,26 @@ static VALUE ip_compress_pixels_entry_body(VALUE ptr) {
|
|
|
2283
2363
|
if (ctx->status != IP_OK)
|
|
2284
2364
|
ip_raise_for_status(ctx);
|
|
2285
2365
|
|
|
2366
|
+
ip_resolve_execution(ctx);
|
|
2367
|
+
if (!ip_ensure_owned_pixels_for_async(ctx, call->buffer)) {
|
|
2368
|
+
ip_raise_for_status(ctx);
|
|
2369
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "invalid pixel input");
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2286
2372
|
ip_run_context(ctx);
|
|
2373
|
+
if (ip_bool_value(call->report)) {
|
|
2374
|
+
VALUE output_value = ip_finish_output(ctx, out_kind);
|
|
2375
|
+
return ip_build_report(ctx, output_value);
|
|
2376
|
+
}
|
|
2287
2377
|
return ip_finish_output(ctx, out_kind);
|
|
2288
2378
|
}
|
|
2289
2379
|
|
|
2290
|
-
static VALUE ip_compress_pixels_entry(
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
quality, min_ssim, progressive, execution, cancellable, has_scheduler, NULL};
|
|
2380
|
+
static VALUE ip_compress_pixels_entry(int argc, VALUE *argv, VALUE self) {
|
|
2381
|
+
rb_check_arity(argc, 17, 17);
|
|
2382
|
+
ip_compress_pixels_call_t call = {self, argv[0], argv[1], argv[2], argv[3],
|
|
2383
|
+
argv[4], argv[5], argv[6], argv[7], argv[8],
|
|
2384
|
+
argv[9], argv[10], argv[11], argv[12], argv[13],
|
|
2385
|
+
argv[14], argv[15], argv[16], NULL};
|
|
2297
2386
|
return rb_ensure(ip_compress_pixels_entry_body, (VALUE)&call, ip_call_cleanup,
|
|
2298
2387
|
(VALUE)&call.ctx);
|
|
2299
2388
|
}
|
|
@@ -2311,11 +2400,25 @@ static VALUE ip_optimize_jpeg_entry_body(VALUE ptr) {
|
|
|
2311
2400
|
ctx->requested_execution = ip_parse_execution(call->execution);
|
|
2312
2401
|
ctx->cancellable_requested = ip_bool_value(call->cancellable);
|
|
2313
2402
|
ctx->has_scheduler = ip_bool_value(call->has_scheduler);
|
|
2403
|
+
ctx->strict = ip_bool_value(call->strict);
|
|
2314
2404
|
ctx->ssim_guard_enabled = 0;
|
|
2315
2405
|
apply_configuration(call->self, ctx);
|
|
2316
2406
|
|
|
2317
|
-
|
|
2318
|
-
|
|
2407
|
+
ip_input_kind_t in_kind = ip_parse_input_kind(call->input_kind);
|
|
2408
|
+
if (!ip_prepare_output_path(ctx, call->output, out_kind) ||
|
|
2409
|
+
!ip_prepare_input_bytes(ctx, call->input, in_kind)) {
|
|
2410
|
+
ip_raise_for_status(ctx);
|
|
2411
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "invalid JPEG input");
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
if (ctx->requested_execution == IP_EXEC_AUTO && ctx->input_size < ctx->direct_input_threshold &&
|
|
2415
|
+
!ip_inspect_jpeg_header(ctx, 1)) {
|
|
2416
|
+
ip_raise_for_status(ctx);
|
|
2417
|
+
rb_raise(rb_eImagePackInvalidImageError, "invalid JPEG input");
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
ip_resolve_execution(ctx);
|
|
2421
|
+
if (!ip_ensure_owned_input_for_async(ctx, call->input, in_kind)) {
|
|
2319
2422
|
ip_raise_for_status(ctx);
|
|
2320
2423
|
rb_raise(rb_eImagePackInvalidArgumentError, "invalid JPEG input");
|
|
2321
2424
|
}
|
|
@@ -2326,63 +2429,82 @@ static VALUE ip_optimize_jpeg_entry_body(VALUE ptr) {
|
|
|
2326
2429
|
|
|
2327
2430
|
static VALUE ip_optimize_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
|
|
2328
2431
|
VALUE output_kind, VALUE progressive, VALUE strip_metadata,
|
|
2329
|
-
VALUE execution, VALUE cancellable, VALUE has_scheduler
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2432
|
+
VALUE execution, VALUE cancellable, VALUE has_scheduler,
|
|
2433
|
+
VALUE strict) {
|
|
2434
|
+
ip_optimize_jpeg_call_t call = {self, input, input_kind, output,
|
|
2435
|
+
output_kind, progressive, strip_metadata, execution,
|
|
2436
|
+
cancellable, has_scheduler, strict, NULL};
|
|
2333
2437
|
return rb_ensure(ip_optimize_jpeg_entry_body, (VALUE)&call, ip_call_cleanup, (VALUE)&call.ctx);
|
|
2334
2438
|
}
|
|
2335
2439
|
|
|
2336
2440
|
IMAGE_PACK_INIT_EXPORT void Init_image_pack(void) {
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2441
|
+
static const struct {
|
|
2442
|
+
ID *slot;
|
|
2443
|
+
const char *name;
|
|
2444
|
+
} ids[] = {{&id_jpeg_turbo, "jpeg_turbo"},
|
|
2445
|
+
{&id_mozjpeg, "mozjpeg"},
|
|
2446
|
+
{&id_direct, "direct"},
|
|
2447
|
+
{&id_nogvl, "nogvl"},
|
|
2448
|
+
{&id_offload, "offload"},
|
|
2449
|
+
{&id_auto, "auto"},
|
|
2450
|
+
{&id_bytes, "bytes"},
|
|
2451
|
+
{&id_path, "path"},
|
|
2452
|
+
{&id_io_buffer, "io_buffer"},
|
|
2453
|
+
{&id_return_string, "return_string"},
|
|
2454
|
+
{&id_configuration, "configuration"},
|
|
2455
|
+
{&id_direct_input_threshold, "direct_input_threshold"},
|
|
2456
|
+
{&id_direct_pixel_threshold, "direct_pixel_threshold"},
|
|
2457
|
+
{&id_max_pixels, "max_pixels"},
|
|
2458
|
+
{&id_max_width, "max_width"},
|
|
2459
|
+
{&id_max_height, "max_height"},
|
|
2460
|
+
{&id_max_output_size, "max_output_size"},
|
|
2461
|
+
{&id_max_input_size, "max_input_size"}};
|
|
2462
|
+
for (size_t i = 0; i < IP_ARRAY_LEN(ids); i++)
|
|
2463
|
+
*ids[i].slot = rb_intern(ids[i].name);
|
|
2355
2464
|
|
|
2356
2465
|
rb_mImagePack = rb_define_module("ImagePack");
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2466
|
+
|
|
2467
|
+
static const struct {
|
|
2468
|
+
VALUE *slot;
|
|
2469
|
+
const char *name;
|
|
2470
|
+
} exceptions[] = {{&rb_eImagePackError, "Error"},
|
|
2471
|
+
{&rb_eImagePackInvalidArgumentError, "InvalidArgumentError"},
|
|
2472
|
+
{&rb_eImagePackInvalidImageError, "InvalidImageError"},
|
|
2473
|
+
{&rb_eImagePackUnsupportedError, "UnsupportedError"},
|
|
2474
|
+
{&rb_eImagePackLimitExceededError, "LimitExceededError"},
|
|
2475
|
+
{&rb_eImagePackEncodeError, "EncodeError"},
|
|
2476
|
+
{&rb_eImagePackQualityConstraintError, "QualityConstraintError"},
|
|
2477
|
+
{&rb_eImagePackOutOfMemoryError, "OutOfMemoryError"},
|
|
2478
|
+
{&rb_eImagePackCancelledError, "CancelledError"}};
|
|
2479
|
+
for (size_t i = 0; i < IP_ARRAY_LEN(exceptions); i++)
|
|
2480
|
+
*exceptions[i].slot = rb_const_get(rb_mImagePack, rb_intern(exceptions[i].name));
|
|
2481
|
+
|
|
2482
|
+
rb_define_const(rb_mImagePack, "NATIVE_MOZJPEG_VERSION",
|
|
2483
|
+
rb_str_new_cstr(IMAGE_PACK_MOZJPEG_VERSION));
|
|
2370
2484
|
#if defined(IMAGE_PACK_HAS_SIMD)
|
|
2371
2485
|
rb_define_const(rb_mImagePack, "NATIVE_SIMD", Qtrue);
|
|
2372
2486
|
#else
|
|
2373
2487
|
rb_define_const(rb_mImagePack, "NATIVE_SIMD", Qfalse);
|
|
2374
2488
|
#endif
|
|
2489
|
+
ip_offload_runtime_enabled = (getenv("IMAGE_PACK_DISABLE_OFFLOAD") == NULL) ? 1 : 0;
|
|
2490
|
+
#if IMAGE_PACK_HAS_OFFLOAD_SAFE
|
|
2491
|
+
rb_define_const(rb_mImagePack, "NATIVE_OFFLOAD_SAFE",
|
|
2492
|
+
ip_offload_runtime_enabled ? Qtrue : Qfalse);
|
|
2493
|
+
#else
|
|
2494
|
+
rb_define_const(rb_mImagePack, "NATIVE_OFFLOAD_SAFE", Qfalse);
|
|
2495
|
+
#endif
|
|
2375
2496
|
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2497
|
+
static const struct {
|
|
2498
|
+
const char *name;
|
|
2499
|
+
VALUE (*fn)(ANYARGS);
|
|
2500
|
+
int arity;
|
|
2501
|
+
} methods[] = {{"__compress_jpeg", (VALUE (*)(ANYARGS))ip_compress_jpeg_entry, 15},
|
|
2502
|
+
{"__compress_pixels", (VALUE (*)(ANYARGS))ip_compress_pixels_entry, -1},
|
|
2503
|
+
{"__optimize_jpeg", (VALUE (*)(ANYARGS))ip_optimize_jpeg_entry, 10},
|
|
2504
|
+
{"__inspect_image", (VALUE (*)(ANYARGS))ip_inspect_image_entry, 2}};
|
|
2505
|
+
for (size_t i = 0; i < IP_ARRAY_LEN(methods); i++) {
|
|
2506
|
+
rb_define_singleton_method(rb_mImagePack, methods[i].name, methods[i].fn, methods[i].arity);
|
|
2507
|
+
rb_funcall(rb_mImagePack, rb_intern("private_class_method"), 1,
|
|
2508
|
+
ID2SYM(rb_intern(methods[i].name)));
|
|
2509
|
+
}
|
|
2388
2510
|
}
|