image_pack 0.2.2 → 0.2.3
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 +1 -0
- data/README.md +23 -4
- data/ext/image_pack/extconf.rb +35 -124
- data/ext/image_pack/image_pack.c +638 -595
- 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 +65 -18
- metadata +13 -78
- 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,10 +36,6 @@
|
|
|
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
|
|
@@ -48,6 +52,32 @@
|
|
|
48
52
|
#define IP_RESTRICT
|
|
49
53
|
#endif
|
|
50
54
|
|
|
55
|
+
#if defined(IMAGE_PACK_HAS_SIMD)
|
|
56
|
+
#define IP_FAST_DCT JDCT_ISLOW
|
|
57
|
+
#else
|
|
58
|
+
#define IP_FAST_DCT JDCT_FASTEST
|
|
59
|
+
#endif
|
|
60
|
+
|
|
61
|
+
#define IP_FAIL(ctx, st, msg) \
|
|
62
|
+
do { \
|
|
63
|
+
ip_context_set_error((ctx), (st), (msg)); \
|
|
64
|
+
return 0; \
|
|
65
|
+
} while (0)
|
|
66
|
+
|
|
67
|
+
#define IP_FAIL_NULL(ctx, st, msg) \
|
|
68
|
+
do { \
|
|
69
|
+
ip_context_set_error((ctx), (st), (msg)); \
|
|
70
|
+
return NULL; \
|
|
71
|
+
} while (0)
|
|
72
|
+
|
|
73
|
+
#define IP_FAIL_GOTO(ctx, st, msg) \
|
|
74
|
+
do { \
|
|
75
|
+
ip_context_set_error((ctx), (st), (msg)); \
|
|
76
|
+
goto fail; \
|
|
77
|
+
} while (0)
|
|
78
|
+
|
|
79
|
+
#define IP_ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
|
|
80
|
+
|
|
51
81
|
typedef enum { IP_ALGO_JPEG_TURBO = 1, IP_ALGO_MOZJPEG = 2 } ip_algo_t;
|
|
52
82
|
|
|
53
83
|
typedef enum {
|
|
@@ -87,11 +117,11 @@ typedef struct {
|
|
|
87
117
|
int height;
|
|
88
118
|
int channels;
|
|
89
119
|
int bit_depth;
|
|
120
|
+
int jpeg_color_space;
|
|
90
121
|
size_t decoded_bytes;
|
|
91
122
|
|
|
92
123
|
unsigned char *output_data;
|
|
93
124
|
size_t output_size;
|
|
94
|
-
size_t output_capacity;
|
|
95
125
|
ip_output_owner_t output_owner;
|
|
96
126
|
char *output_path;
|
|
97
127
|
|
|
@@ -125,9 +155,6 @@ typedef struct {
|
|
|
125
155
|
jmp_buf jmpbuf;
|
|
126
156
|
int jmp_armed;
|
|
127
157
|
|
|
128
|
-
unsigned char *scratch_row;
|
|
129
|
-
size_t scratch_row_size;
|
|
130
|
-
|
|
131
158
|
struct {
|
|
132
159
|
int marker;
|
|
133
160
|
unsigned char *data;
|
|
@@ -138,7 +165,6 @@ typedef struct {
|
|
|
138
165
|
|
|
139
166
|
unsigned char *transient_jpeg_buf;
|
|
140
167
|
unsigned char *transient_decode_buf;
|
|
141
|
-
unsigned char *transient_scratch_buf;
|
|
142
168
|
int source_orientation;
|
|
143
169
|
} ip_context_t;
|
|
144
170
|
|
|
@@ -188,6 +214,9 @@ static int ip_checked_image_size(int width, int height, int channels, size_t *ou
|
|
|
188
214
|
static void ip_validate_quality_or_raise(ip_context_t *ctx);
|
|
189
215
|
static void ip_validate_min_ssim_or_raise(ip_context_t *ctx);
|
|
190
216
|
static int ip_bool_value(VALUE value);
|
|
217
|
+
static int ip_value_negative(VALUE value);
|
|
218
|
+
static char *ip_strdup(const char *source);
|
|
219
|
+
static int ip_replace_file(const char *tmp_path, const char *output_path);
|
|
191
220
|
|
|
192
221
|
static ip_algo_t ip_parse_algo(VALUE sym);
|
|
193
222
|
static ip_execution_t ip_parse_execution(VALUE sym);
|
|
@@ -195,11 +224,15 @@ static ip_input_kind_t ip_parse_input_kind(VALUE sym);
|
|
|
195
224
|
static ip_output_kind_t ip_parse_output_kind(VALUE sym);
|
|
196
225
|
|
|
197
226
|
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
|
|
227
|
+
static int ip_prepare_pixels(ip_context_t *ctx, VALUE buffer, int width, int height, int channels,
|
|
228
|
+
int exact_size);
|
|
229
|
+
static int ip_ensure_owned_input_for_async(ip_context_t *ctx, VALUE input, ip_input_kind_t kind);
|
|
230
|
+
static int ip_ensure_owned_pixels_for_async(ip_context_t *ctx, VALUE buffer);
|
|
199
231
|
static int ip_prepare_output_path(ip_context_t *ctx, VALUE output, ip_output_kind_t kind);
|
|
200
232
|
static VALUE ip_finish_output(ip_context_t *ctx, ip_output_kind_t kind);
|
|
233
|
+
static void apply_configuration(VALUE self, ip_context_t *ctx);
|
|
201
234
|
|
|
202
|
-
static int ip_inspect_jpeg_header(ip_context_t *ctx);
|
|
235
|
+
static int ip_inspect_jpeg_header(ip_context_t *ctx, int allow_cmyk_ycck);
|
|
203
236
|
static VALUE ip_inspect_image_entry(VALUE self, VALUE input, VALUE input_kind);
|
|
204
237
|
|
|
205
238
|
static void ip_resolve_execution(ip_context_t *ctx);
|
|
@@ -227,7 +260,7 @@ typedef struct {
|
|
|
227
260
|
|
|
228
261
|
typedef struct {
|
|
229
262
|
VALUE self, buffer, width, height, channels, output, output_kind, algo, quality, min_ssim;
|
|
230
|
-
VALUE progressive, execution, cancellable, has_scheduler;
|
|
263
|
+
VALUE mozjpeg_trellis, progressive, exact_size, execution, cancellable, has_scheduler;
|
|
231
264
|
ip_context_t *ctx;
|
|
232
265
|
} ip_compress_pixels_call_t;
|
|
233
266
|
|
|
@@ -257,8 +290,9 @@ static VALUE ip_compress_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, V
|
|
|
257
290
|
VALUE execution, VALUE cancellable, VALUE has_scheduler);
|
|
258
291
|
static VALUE ip_compress_pixels_entry(VALUE self, VALUE buffer, VALUE width, VALUE height,
|
|
259
292
|
VALUE channels, VALUE output, VALUE output_kind, VALUE algo,
|
|
260
|
-
VALUE quality, VALUE min_ssim, VALUE
|
|
261
|
-
VALUE
|
|
293
|
+
VALUE quality, VALUE min_ssim, VALUE mozjpeg_trellis,
|
|
294
|
+
VALUE progressive, VALUE exact_size, VALUE execution,
|
|
295
|
+
VALUE cancellable, VALUE has_scheduler);
|
|
262
296
|
static VALUE ip_optimize_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
|
|
263
297
|
VALUE output_kind, VALUE progressive, VALUE strip_metadata,
|
|
264
298
|
VALUE execution, VALUE cancellable, VALUE has_scheduler);
|
|
@@ -310,10 +344,6 @@ static int ip_checked_image_size(int width, int height, int channels, size_t *ou
|
|
|
310
344
|
return ip_checked_mul_size(pixels, (size_t)channels, out);
|
|
311
345
|
}
|
|
312
346
|
|
|
313
|
-
static void *ip_malloc_hot(size_t size) {
|
|
314
|
-
return malloc(size);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
347
|
static void ip_validate_quality_or_raise(ip_context_t *ctx) {
|
|
318
348
|
if (ctx->quality >= 1 && ctx->quality <= 100)
|
|
319
349
|
return;
|
|
@@ -333,6 +363,67 @@ static void ip_validate_min_ssim_or_raise(ip_context_t *ctx) {
|
|
|
333
363
|
"min_ssim must be Numeric > 0.0 and <= 1.0, got: %.17g", min_ssim);
|
|
334
364
|
}
|
|
335
365
|
|
|
366
|
+
static int ip_value_negative(VALUE value) {
|
|
367
|
+
return RTEST(rb_funcall(value, rb_intern("<"), 1, INT2FIX(0)));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
static char *ip_strdup(const char *source) {
|
|
371
|
+
size_t len = strlen(source) + 1;
|
|
372
|
+
char *copy = (char *)malloc(len);
|
|
373
|
+
if (!copy)
|
|
374
|
+
return NULL;
|
|
375
|
+
memcpy(copy, source, len);
|
|
376
|
+
return copy;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
#if defined(_WIN32)
|
|
380
|
+
static int ip_replace_file(const char *tmp_path, const char *output_path) {
|
|
381
|
+
return MoveFileExA(tmp_path, output_path, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)
|
|
382
|
+
? 0
|
|
383
|
+
: -1;
|
|
384
|
+
}
|
|
385
|
+
#else
|
|
386
|
+
static int ip_replace_file(const char *tmp_path, const char *output_path) {
|
|
387
|
+
return rename(tmp_path, output_path);
|
|
388
|
+
}
|
|
389
|
+
#endif
|
|
390
|
+
|
|
391
|
+
static int ip_file_seek_end(FILE *fp) {
|
|
392
|
+
#if defined(_WIN32)
|
|
393
|
+
return _fseeki64(fp, 0, SEEK_END);
|
|
394
|
+
#else
|
|
395
|
+
return fseeko(fp, 0, SEEK_END);
|
|
396
|
+
#endif
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
static int ip_file_rewind(FILE *fp) {
|
|
400
|
+
#if defined(_WIN32)
|
|
401
|
+
return _fseeki64(fp, 0, SEEK_SET);
|
|
402
|
+
#else
|
|
403
|
+
return fseeko(fp, 0, SEEK_SET);
|
|
404
|
+
#endif
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
static int ip_file_tell(FILE *fp, size_t *out) {
|
|
408
|
+
#if defined(_WIN32)
|
|
409
|
+
__int64 pos = _ftelli64(fp);
|
|
410
|
+
if (pos < 0)
|
|
411
|
+
return 0;
|
|
412
|
+
if ((unsigned long long)pos > (unsigned long long)SIZE_MAX)
|
|
413
|
+
return 0;
|
|
414
|
+
*out = (size_t)pos;
|
|
415
|
+
return 1;
|
|
416
|
+
#else
|
|
417
|
+
off_t pos = ftello(fp);
|
|
418
|
+
if (pos < 0)
|
|
419
|
+
return 0;
|
|
420
|
+
if ((uintmax_t)pos > (uintmax_t)SIZE_MAX)
|
|
421
|
+
return 0;
|
|
422
|
+
*out = (size_t)pos;
|
|
423
|
+
return 1;
|
|
424
|
+
#endif
|
|
425
|
+
}
|
|
426
|
+
|
|
336
427
|
static int ip_bool_value(VALUE value) {
|
|
337
428
|
if (NIL_P(value) || value == Qfalse)
|
|
338
429
|
return 0;
|
|
@@ -371,10 +462,8 @@ static void ip_context_free(ip_context_t *ctx) {
|
|
|
371
462
|
free(ctx->owned_input_data);
|
|
372
463
|
free(ctx->owned_pixel_data);
|
|
373
464
|
free(ctx->output_path);
|
|
374
|
-
free(ctx->scratch_row);
|
|
375
465
|
free(ctx->transient_jpeg_buf);
|
|
376
466
|
free(ctx->transient_decode_buf);
|
|
377
|
-
free(ctx->transient_scratch_buf);
|
|
378
467
|
|
|
379
468
|
if (ctx->preserved_markers) {
|
|
380
469
|
for (size_t i = 0; i < ctx->preserved_marker_count; i++) {
|
|
@@ -407,46 +496,87 @@ static ID symbol_id(VALUE sym, const char *kind) {
|
|
|
407
496
|
return SYM2ID(sym);
|
|
408
497
|
}
|
|
409
498
|
|
|
499
|
+
typedef struct {
|
|
500
|
+
const ID *id;
|
|
501
|
+
int value;
|
|
502
|
+
} ip_symbol_entry;
|
|
503
|
+
|
|
504
|
+
static int ip_map_symbol(VALUE sym, const char *kind, const ip_symbol_entry *table) {
|
|
505
|
+
ID id = symbol_id(sym, kind);
|
|
506
|
+
for (; table->id != NULL; table++) {
|
|
507
|
+
if (id == *table->id)
|
|
508
|
+
return table->value;
|
|
509
|
+
}
|
|
510
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "unknown %s", kind);
|
|
511
|
+
}
|
|
512
|
+
|
|
410
513
|
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");
|
|
514
|
+
static const ip_symbol_entry table[] = {
|
|
515
|
+
{&id_jpeg_turbo, IP_ALGO_JPEG_TURBO}, {&id_mozjpeg, IP_ALGO_MOZJPEG}, {NULL, 0}};
|
|
516
|
+
return (ip_algo_t)ip_map_symbol(sym, "algo", table);
|
|
417
517
|
}
|
|
418
518
|
|
|
419
519
|
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");
|
|
520
|
+
static const ip_symbol_entry table[] = {{&id_direct, IP_EXEC_DIRECT},
|
|
521
|
+
{&id_nogvl, IP_EXEC_NOGVL},
|
|
522
|
+
{&id_offload, IP_EXEC_OFFLOAD},
|
|
523
|
+
{&id_auto, IP_EXEC_AUTO},
|
|
524
|
+
{NULL, 0}};
|
|
525
|
+
return (ip_execution_t)ip_map_symbol(sym, "execution", table);
|
|
430
526
|
}
|
|
431
527
|
|
|
432
528
|
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");
|
|
529
|
+
static const ip_symbol_entry table[] = {{&id_bytes, IP_INPUT_BYTES},
|
|
530
|
+
{&id_path, IP_INPUT_PATH},
|
|
531
|
+
{&id_io_buffer, IP_INPUT_IO_BUFFER},
|
|
532
|
+
{NULL, 0}};
|
|
533
|
+
return (ip_input_kind_t)ip_map_symbol(sym, "input kind", table);
|
|
441
534
|
}
|
|
442
535
|
|
|
443
536
|
static ip_output_kind_t ip_parse_output_kind(VALUE sym) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
537
|
+
static const ip_symbol_entry table[] = {
|
|
538
|
+
{&id_return_string, IP_OUTPUT_RETURN_STRING}, {&id_path, IP_OUTPUT_PATH}, {NULL, 0}};
|
|
539
|
+
return (ip_output_kind_t)ip_map_symbol(sym, "output kind", table);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
static VALUE ip_sym(const char *name) {
|
|
543
|
+
return ID2SYM(rb_intern(name));
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
static struct jpeg_error_mgr *ip_use_error(ip_jpeg_error_mgr *jerr, ip_context_t *ctx,
|
|
547
|
+
void (*handler)(j_common_ptr)) {
|
|
548
|
+
struct jpeg_error_mgr *base = jpeg_std_error(&jerr->pub);
|
|
549
|
+
jerr->pub.error_exit = handler;
|
|
550
|
+
jerr->ctx = ctx;
|
|
551
|
+
return base;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
static void ip_apply_fast_decode(struct jpeg_decompress_struct *cinfo) {
|
|
555
|
+
cinfo->dct_method = IP_FAST_DCT;
|
|
556
|
+
cinfo->do_fancy_upsampling = FALSE;
|
|
557
|
+
cinfo->do_block_smoothing = FALSE;
|
|
558
|
+
cinfo->quantize_colors = FALSE;
|
|
559
|
+
cinfo->two_pass_quantize = FALSE;
|
|
560
|
+
cinfo->dither_mode = JDITHER_NONE;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
static void ip_disable_mozjpeg_trellis(struct jpeg_compress_struct *cinfo) {
|
|
564
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_QUANT, FALSE);
|
|
565
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_QUANT_DC, FALSE);
|
|
566
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_EOB_OPT, FALSE);
|
|
567
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_USE_SCANS_IN_TRELLIS, FALSE);
|
|
568
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_Q_OPT, FALSE);
|
|
569
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_OVERSHOOT_DERINGING, FALSE);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
static int ip_check_max_dimension_limits(ip_context_t *ctx) {
|
|
573
|
+
if (ctx->max_width > 0 && ctx->width > ctx->max_width)
|
|
574
|
+
IP_FAIL(ctx, IP_ERR_LIMIT, "image width exceeds max_width");
|
|
575
|
+
if (ctx->max_height > 0 && ctx->height > ctx->max_height)
|
|
576
|
+
IP_FAIL(ctx, IP_ERR_LIMIT, "image height exceeds max_height");
|
|
577
|
+
if (ctx->max_pixels > 0 && (uint64_t)ctx->width * (uint64_t)ctx->height > ctx->max_pixels)
|
|
578
|
+
IP_FAIL(ctx, IP_ERR_LIMIT, "image pixels exceed max_pixels");
|
|
579
|
+
return 1;
|
|
450
580
|
}
|
|
451
581
|
|
|
452
582
|
static VALUE pathname_to_s(VALUE object) {
|
|
@@ -464,36 +594,41 @@ static int read_file_to_owned_buffer(ip_context_t *ctx, const char *path) {
|
|
|
464
594
|
return 0;
|
|
465
595
|
}
|
|
466
596
|
|
|
467
|
-
if (
|
|
597
|
+
if (ip_file_seek_end(fp) != 0) {
|
|
468
598
|
fclose(fp);
|
|
469
599
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "failed to seek input path");
|
|
470
600
|
return 0;
|
|
471
601
|
}
|
|
472
602
|
|
|
473
|
-
|
|
474
|
-
if (size
|
|
603
|
+
size_t size = 0;
|
|
604
|
+
if (!ip_file_tell(fp, &size)) {
|
|
475
605
|
fclose(fp);
|
|
476
606
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "failed to determine input size");
|
|
477
607
|
return 0;
|
|
478
608
|
}
|
|
479
|
-
rewind(fp);
|
|
480
609
|
|
|
481
|
-
if (
|
|
610
|
+
if (ip_file_rewind(fp) != 0) {
|
|
611
|
+
fclose(fp);
|
|
612
|
+
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "failed to rewind input path");
|
|
613
|
+
return 0;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (ctx->max_input_size > 0 && size > ctx->max_input_size) {
|
|
482
617
|
fclose(fp);
|
|
483
618
|
ip_context_set_error(ctx, IP_ERR_LIMIT, "input file exceeds max_input_size");
|
|
484
619
|
return 0;
|
|
485
620
|
}
|
|
486
621
|
|
|
487
|
-
unsigned char *data = (unsigned char *)malloc(
|
|
622
|
+
unsigned char *data = (unsigned char *)malloc(size);
|
|
488
623
|
if (!data && size > 0) {
|
|
489
624
|
fclose(fp);
|
|
490
625
|
ip_context_set_error(ctx, IP_ERR_OOM, "failed to allocate input file buffer");
|
|
491
626
|
return 0;
|
|
492
627
|
}
|
|
493
628
|
|
|
494
|
-
size_t read_size = fread(data, 1,
|
|
629
|
+
size_t read_size = fread(data, 1, size, fp);
|
|
495
630
|
fclose(fp);
|
|
496
|
-
if (read_size !=
|
|
631
|
+
if (read_size != size) {
|
|
497
632
|
free(data);
|
|
498
633
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "failed to read input path");
|
|
499
634
|
return 0;
|
|
@@ -505,9 +640,36 @@ static int read_file_to_owned_buffer(ip_context_t *ctx, const char *path) {
|
|
|
505
640
|
return 1;
|
|
506
641
|
}
|
|
507
642
|
|
|
508
|
-
static
|
|
509
|
-
VALUE
|
|
510
|
-
|
|
643
|
+
static size_t io_buffer_size_or_raise(VALUE buffer) {
|
|
644
|
+
VALUE size_value = rb_funcall(buffer, rb_intern("size"), 0);
|
|
645
|
+
if (!RB_INTEGER_TYPE_P(size_value) || ip_value_negative(size_value))
|
|
646
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "IO::Buffer#size must return Integer >= 0");
|
|
647
|
+
return NUM2SIZET(size_value);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
static VALUE io_buffer_to_string_slice(VALUE buffer, size_t len) {
|
|
651
|
+
return rb_funcall(buffer, rb_intern("get_string"), 2, LONG2NUM(0), SIZET2NUM(len));
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
static int ip_copy_string_to_owned_input(ip_context_t *ctx, VALUE input) {
|
|
655
|
+
Check_Type(input, T_STRING);
|
|
656
|
+
size_t len = (size_t)RSTRING_LEN(input);
|
|
657
|
+
if (ctx->max_input_size > 0 && len > ctx->max_input_size) {
|
|
658
|
+
ip_context_set_error(ctx, IP_ERR_LIMIT, "input bytes exceed max_input_size");
|
|
659
|
+
return 0;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
unsigned char *copy = (unsigned char *)malloc(len);
|
|
663
|
+
if (!copy && len > 0) {
|
|
664
|
+
ip_context_set_error(ctx, IP_ERR_OOM, "failed to copy binary String input");
|
|
665
|
+
return 0;
|
|
666
|
+
}
|
|
667
|
+
if (len > 0)
|
|
668
|
+
memcpy(copy, RSTRING_PTR(input), len);
|
|
669
|
+
ctx->owned_input_data = copy;
|
|
670
|
+
ctx->input_data = copy;
|
|
671
|
+
ctx->input_size = len;
|
|
672
|
+
return 1;
|
|
511
673
|
}
|
|
512
674
|
|
|
513
675
|
static int ip_prepare_input_bytes(ip_context_t *ctx, VALUE input, ip_input_kind_t kind) {
|
|
@@ -518,33 +680,32 @@ static int ip_prepare_input_bytes(ip_context_t *ctx, VALUE input, ip_input_kind_
|
|
|
518
680
|
ip_context_set_error(ctx, IP_ERR_LIMIT, "input bytes exceed max_input_size");
|
|
519
681
|
return 0;
|
|
520
682
|
}
|
|
521
|
-
|
|
683
|
+
|
|
684
|
+
if (ctx->requested_execution == IP_EXEC_DIRECT ||
|
|
685
|
+
ctx->requested_execution == IP_EXEC_AUTO) {
|
|
522
686
|
ctx->input_data = (const unsigned char *)RSTRING_PTR(input);
|
|
523
687
|
ctx->input_size = len;
|
|
524
688
|
return 1;
|
|
525
689
|
}
|
|
526
690
|
|
|
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;
|
|
691
|
+
return ip_copy_string_to_owned_input(ctx, input);
|
|
538
692
|
}
|
|
539
693
|
|
|
540
694
|
if (kind == IP_INPUT_IO_BUFFER) {
|
|
541
|
-
|
|
542
|
-
StringValue(str);
|
|
543
|
-
size_t len = (size_t)RSTRING_LEN(str);
|
|
695
|
+
size_t len = io_buffer_size_or_raise(input);
|
|
544
696
|
if (ctx->max_input_size > 0 && len > ctx->max_input_size) {
|
|
545
697
|
ip_context_set_error(ctx, IP_ERR_LIMIT, "input IO::Buffer exceeds max_input_size");
|
|
546
698
|
return 0;
|
|
547
699
|
}
|
|
700
|
+
|
|
701
|
+
VALUE str = io_buffer_to_string_slice(input, len);
|
|
702
|
+
StringValue(str);
|
|
703
|
+
if ((size_t)RSTRING_LEN(str) != len) {
|
|
704
|
+
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT,
|
|
705
|
+
"IO::Buffer#get_string returned unexpected size");
|
|
706
|
+
return 0;
|
|
707
|
+
}
|
|
708
|
+
|
|
548
709
|
unsigned char *copy = (unsigned char *)malloc(len);
|
|
549
710
|
if (!copy && len > 0) {
|
|
550
711
|
ip_context_set_error(ctx, IP_ERR_OOM, "failed to copy IO::Buffer input");
|
|
@@ -563,15 +724,31 @@ static int ip_prepare_input_bytes(ip_context_t *ctx, VALUE input, ip_input_kind_
|
|
|
563
724
|
return read_file_to_owned_buffer(ctx, StringValueCStr(path_value));
|
|
564
725
|
}
|
|
565
726
|
|
|
566
|
-
static int
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
727
|
+
static int ip_ensure_owned_input_for_async(ip_context_t *ctx, VALUE input, ip_input_kind_t kind) {
|
|
728
|
+
if (ctx->resolved_execution == IP_EXEC_DIRECT)
|
|
729
|
+
return 1;
|
|
730
|
+
if (kind != IP_INPUT_BYTES || ctx->owned_input_data)
|
|
731
|
+
return 1;
|
|
732
|
+
return ip_copy_string_to_owned_input(ctx, input);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
static int ip_copy_string_to_owned_pixels(ip_context_t *ctx, VALUE str, size_t expected) {
|
|
736
|
+
unsigned char *copy = (unsigned char *)malloc(expected);
|
|
737
|
+
if (!copy && expected > 0) {
|
|
738
|
+
ip_context_set_error(ctx, IP_ERR_OOM, "failed to copy pixel buffer");
|
|
739
|
+
return 0;
|
|
572
740
|
}
|
|
573
741
|
|
|
574
|
-
|
|
742
|
+
if (expected > 0)
|
|
743
|
+
memcpy(copy, RSTRING_PTR(str), expected);
|
|
744
|
+
ctx->owned_pixel_data = copy;
|
|
745
|
+
ctx->pixel_data = copy;
|
|
746
|
+
ctx->pixel_size = expected;
|
|
747
|
+
return 1;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
static int ip_prepare_pixels(ip_context_t *ctx, VALUE buffer, int width, int height, int channels,
|
|
751
|
+
int exact_size) {
|
|
575
752
|
size_t expected = 0;
|
|
576
753
|
if (!ip_checked_image_size(width, height, channels, &expected)) {
|
|
577
754
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT,
|
|
@@ -579,34 +756,49 @@ static int ip_prepare_pixels(ip_context_t *ctx, VALUE buffer, int width, int hei
|
|
|
579
756
|
return 0;
|
|
580
757
|
}
|
|
581
758
|
|
|
582
|
-
|
|
759
|
+
VALUE str = Qnil;
|
|
760
|
+
size_t actual = 0;
|
|
761
|
+
int buffer_is_string = RB_TYPE_P(buffer, T_STRING);
|
|
762
|
+
|
|
763
|
+
if (buffer_is_string) {
|
|
764
|
+
str = buffer;
|
|
765
|
+
actual = (size_t)RSTRING_LEN(str);
|
|
766
|
+
} else {
|
|
767
|
+
actual = io_buffer_size_or_raise(buffer);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if (actual < expected) {
|
|
583
771
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT,
|
|
584
772
|
"pixel buffer is smaller than width * height * channels");
|
|
585
773
|
return 0;
|
|
586
774
|
}
|
|
587
775
|
|
|
588
|
-
if (
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
ctx->channels = channels;
|
|
594
|
-
ctx->bit_depth = 8;
|
|
595
|
-
ctx->decoded_bytes = expected;
|
|
596
|
-
return 1;
|
|
776
|
+
if (exact_size && actual != expected) {
|
|
777
|
+
ip_context_set_error(
|
|
778
|
+
ctx, IP_ERR_INVALID_ARGUMENT,
|
|
779
|
+
"pixel buffer size must equal width * height * channels when exact_size is true");
|
|
780
|
+
return 0;
|
|
597
781
|
}
|
|
598
782
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
783
|
+
if (!buffer_is_string) {
|
|
784
|
+
str = io_buffer_to_string_slice(buffer, expected);
|
|
785
|
+
StringValue(str);
|
|
786
|
+
if ((size_t)RSTRING_LEN(str) != expected) {
|
|
787
|
+
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT,
|
|
788
|
+
"IO::Buffer#get_string returned unexpected pixel size");
|
|
789
|
+
return 0;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
if ((ctx->requested_execution == IP_EXEC_DIRECT || ctx->requested_execution == IP_EXEC_AUTO) &&
|
|
794
|
+
buffer_is_string) {
|
|
795
|
+
ctx->pixel_data = (const unsigned char *)RSTRING_PTR(str);
|
|
796
|
+
ctx->pixel_size = expected;
|
|
797
|
+
} else {
|
|
798
|
+
if (!ip_copy_string_to_owned_pixels(ctx, str, expected))
|
|
799
|
+
return 0;
|
|
603
800
|
}
|
|
604
801
|
|
|
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
802
|
ctx->width = width;
|
|
611
803
|
ctx->height = height;
|
|
612
804
|
ctx->channels = channels;
|
|
@@ -615,6 +807,14 @@ static int ip_prepare_pixels(ip_context_t *ctx, VALUE buffer, int width, int hei
|
|
|
615
807
|
return 1;
|
|
616
808
|
}
|
|
617
809
|
|
|
810
|
+
static int ip_ensure_owned_pixels_for_async(ip_context_t *ctx, VALUE buffer) {
|
|
811
|
+
if (ctx->resolved_execution == IP_EXEC_DIRECT)
|
|
812
|
+
return 1;
|
|
813
|
+
if (!RB_TYPE_P(buffer, T_STRING) || ctx->owned_pixel_data)
|
|
814
|
+
return 1;
|
|
815
|
+
return ip_copy_string_to_owned_pixels(ctx, buffer, ctx->pixel_size);
|
|
816
|
+
}
|
|
817
|
+
|
|
618
818
|
static int ip_prepare_output_path(ip_context_t *ctx, VALUE output, ip_output_kind_t kind) {
|
|
619
819
|
if (kind == IP_OUTPUT_RETURN_STRING)
|
|
620
820
|
return 1;
|
|
@@ -622,7 +822,7 @@ static int ip_prepare_output_path(ip_context_t *ctx, VALUE output, ip_output_kin
|
|
|
622
822
|
VALUE path_value = pathname_to_s(output);
|
|
623
823
|
StringValue(path_value);
|
|
624
824
|
const char *path = StringValueCStr(path_value);
|
|
625
|
-
ctx->output_path =
|
|
825
|
+
ctx->output_path = ip_strdup(path);
|
|
626
826
|
if (!ctx->output_path) {
|
|
627
827
|
ip_context_set_error(ctx, IP_ERR_OOM, "failed to copy output path");
|
|
628
828
|
return 0;
|
|
@@ -666,7 +866,7 @@ static VALUE ip_finish_output(ip_context_t *ctx, ip_output_kind_t kind) {
|
|
|
666
866
|
rb_raise(rb_eImagePackEncodeError, "failed to write full JPEG output");
|
|
667
867
|
}
|
|
668
868
|
|
|
669
|
-
if (
|
|
869
|
+
if (ip_replace_file(tmp_path, ctx->output_path) != 0) {
|
|
670
870
|
remove(tmp_path);
|
|
671
871
|
free(tmp_path);
|
|
672
872
|
rb_raise(rb_eImagePackEncodeError, "failed to move temporary JPEG output into place");
|
|
@@ -679,8 +879,12 @@ static VALUE ip_finish_output(ip_context_t *ctx, ip_output_kind_t kind) {
|
|
|
679
879
|
static int ip_save_marker(ip_context_t *ctx, int marker, const unsigned char *data,
|
|
680
880
|
unsigned int len) {
|
|
681
881
|
if (ctx->preserved_marker_count == ctx->preserved_marker_capacity) {
|
|
882
|
+
if (ctx->preserved_marker_capacity > SIZE_MAX / 2)
|
|
883
|
+
return 0;
|
|
682
884
|
size_t new_cap =
|
|
683
885
|
ctx->preserved_marker_capacity == 0 ? 4 : ctx->preserved_marker_capacity * 2;
|
|
886
|
+
if (new_cap > SIZE_MAX / sizeof(*ctx->preserved_markers))
|
|
887
|
+
return 0;
|
|
684
888
|
void *new_buf = realloc(ctx->preserved_markers, new_cap * sizeof(*ctx->preserved_markers));
|
|
685
889
|
if (!new_buf)
|
|
686
890
|
return 0;
|
|
@@ -812,7 +1016,7 @@ static int ip_transform_pixels_for_orientation(ip_context_t *ctx, unsigned char
|
|
|
812
1016
|
}
|
|
813
1017
|
|
|
814
1018
|
unsigned char *src = *pixels;
|
|
815
|
-
unsigned char *dst = (unsigned char *)
|
|
1019
|
+
unsigned char *dst = (unsigned char *)malloc(out_size);
|
|
816
1020
|
if (!dst && out_size > 0) {
|
|
817
1021
|
ip_context_set_error(ctx, IP_ERR_OOM, "failed to allocate EXIF-oriented pixel buffer");
|
|
818
1022
|
return 0;
|
|
@@ -894,82 +1098,71 @@ static void ip_jpeg_encode_error_exit(j_common_ptr cinfo) {
|
|
|
894
1098
|
longjmp(err->ctx->jmpbuf, 1);
|
|
895
1099
|
}
|
|
896
1100
|
|
|
897
|
-
static
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1101
|
+
static const char *ip_jpeg_color_space_name(int color_space) {
|
|
1102
|
+
switch ((J_COLOR_SPACE)color_space) {
|
|
1103
|
+
case JCS_GRAYSCALE:
|
|
1104
|
+
return "grayscale";
|
|
1105
|
+
case JCS_RGB:
|
|
1106
|
+
return "rgb";
|
|
1107
|
+
case JCS_YCbCr:
|
|
1108
|
+
return "ycbcr";
|
|
1109
|
+
case JCS_CMYK:
|
|
1110
|
+
return "cmyk";
|
|
1111
|
+
case JCS_YCCK:
|
|
1112
|
+
return "ycck";
|
|
1113
|
+
default:
|
|
1114
|
+
return "unknown";
|
|
902
1115
|
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
static int ip_inspect_jpeg_header(ip_context_t *ctx, int allow_cmyk_ycck) {
|
|
1119
|
+
if (!ctx->input_data || ctx->input_size < 2 || ctx->input_data[0] != 0xFF ||
|
|
1120
|
+
ctx->input_data[1] != 0xD8)
|
|
1121
|
+
IP_FAIL(ctx, IP_ERR_INVALID_IMAGE, "input is not a JPEG image");
|
|
903
1122
|
|
|
904
1123
|
struct jpeg_decompress_struct cinfo;
|
|
905
1124
|
ip_jpeg_error_mgr jerr;
|
|
906
1125
|
memset(&cinfo, 0, sizeof(cinfo));
|
|
907
1126
|
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;
|
|
1127
|
+
cinfo.err = ip_use_error(&jerr, ctx, ip_jpeg_invalid_error_exit);
|
|
912
1128
|
|
|
913
1129
|
ctx->jmp_armed = 1;
|
|
914
|
-
if (setjmp(ctx->jmpbuf))
|
|
915
|
-
|
|
916
|
-
jpeg_destroy_decompress(&cinfo);
|
|
917
|
-
return 0;
|
|
918
|
-
}
|
|
1130
|
+
if (setjmp(ctx->jmpbuf))
|
|
1131
|
+
goto fail;
|
|
919
1132
|
|
|
920
1133
|
jpeg_create_decompress(&cinfo);
|
|
921
1134
|
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
|
-
}
|
|
1135
|
+
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
|
|
1136
|
+
IP_FAIL_GOTO(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
929
1137
|
|
|
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
|
-
}
|
|
1138
|
+
if (!allow_cmyk_ycck && (cinfo.num_components == 4 || cinfo.jpeg_color_space == JCS_CMYK ||
|
|
1139
|
+
cinfo.jpeg_color_space == JCS_YCCK))
|
|
1140
|
+
IP_FAIL_GOTO(ctx, IP_ERR_UNSUPPORTED,
|
|
1141
|
+
"CMYK/YCCK JPEG input is not supported for pixel recompression");
|
|
938
1142
|
|
|
939
1143
|
ctx->width = (int)cinfo.image_width;
|
|
940
1144
|
ctx->height = (int)cinfo.image_height;
|
|
941
1145
|
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;
|
|
1146
|
+
ctx->jpeg_color_space = (int)cinfo.jpeg_color_space;
|
|
950
1147
|
|
|
951
1148
|
jpeg_destroy_decompress(&cinfo);
|
|
952
1149
|
ctx->jmp_armed = 0;
|
|
953
1150
|
|
|
954
|
-
if (ctx->width <= 0 || ctx->height <= 0)
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1151
|
+
if (ctx->width <= 0 || ctx->height <= 0)
|
|
1152
|
+
IP_FAIL(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG dimensions");
|
|
1153
|
+
if (ctx->channels != 1 && ctx->channels != 3 && ctx->channels != 4)
|
|
1154
|
+
IP_FAIL(ctx, IP_ERR_UNSUPPORTED, "JPEG component count is not supported");
|
|
958
1155
|
|
|
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
|
-
}
|
|
1156
|
+
ctx->bit_depth = 8;
|
|
1157
|
+
if (!ip_checked_image_size(ctx->width, ctx->height, ctx->channels, &ctx->decoded_bytes))
|
|
1158
|
+
IP_FAIL(ctx, IP_ERR_LIMIT, "decoded image size overflows native size");
|
|
971
1159
|
|
|
972
|
-
return
|
|
1160
|
+
return ip_check_max_dimension_limits(ctx);
|
|
1161
|
+
|
|
1162
|
+
fail:
|
|
1163
|
+
jpeg_destroy_decompress(&cinfo);
|
|
1164
|
+
ctx->jmp_armed = 0;
|
|
1165
|
+
return 0;
|
|
973
1166
|
}
|
|
974
1167
|
|
|
975
1168
|
static VALUE ip_inspect_image_entry_body(VALUE ptr) {
|
|
@@ -979,8 +1172,10 @@ static VALUE ip_inspect_image_entry_body(VALUE ptr) {
|
|
|
979
1172
|
rb_raise(rb_eImagePackOutOfMemoryError, "failed to allocate native context");
|
|
980
1173
|
call->ctx = ctx;
|
|
981
1174
|
|
|
1175
|
+
apply_configuration(call->self, ctx);
|
|
1176
|
+
|
|
982
1177
|
if (!ip_prepare_input_bytes(ctx, call->input, ip_parse_input_kind(call->input_kind)) ||
|
|
983
|
-
!ip_inspect_jpeg_header(ctx)) {
|
|
1178
|
+
!ip_inspect_jpeg_header(ctx, 1)) {
|
|
984
1179
|
ip_raise_for_status(ctx);
|
|
985
1180
|
rb_raise(rb_eImagePackInvalidImageError, "failed to inspect JPEG image");
|
|
986
1181
|
}
|
|
@@ -989,18 +1184,20 @@ static VALUE ip_inspect_image_entry_body(VALUE ptr) {
|
|
|
989
1184
|
int height = ctx->height;
|
|
990
1185
|
int channels = ctx->channels;
|
|
991
1186
|
int bit_depth = ctx->bit_depth;
|
|
1187
|
+
int color_space = ctx->jpeg_color_space;
|
|
992
1188
|
size_t decoded_bytes = ctx->decoded_bytes;
|
|
993
1189
|
|
|
994
1190
|
ip_context_free(ctx);
|
|
995
1191
|
call->ctx = NULL;
|
|
996
1192
|
|
|
997
1193
|
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,
|
|
1194
|
+
rb_hash_aset(hash, ip_sym("format"), ip_sym("jpeg"));
|
|
1195
|
+
rb_hash_aset(hash, ip_sym("width"), INT2NUM(width));
|
|
1196
|
+
rb_hash_aset(hash, ip_sym("height"), INT2NUM(height));
|
|
1197
|
+
rb_hash_aset(hash, ip_sym("channels"), INT2NUM(channels));
|
|
1198
|
+
rb_hash_aset(hash, ip_sym("bit_depth"), INT2NUM(bit_depth));
|
|
1199
|
+
rb_hash_aset(hash, ip_sym("color_space"), ip_sym(ip_jpeg_color_space_name(color_space)));
|
|
1200
|
+
rb_hash_aset(hash, ip_sym("decoded_bytes"), SIZET2NUM(decoded_bytes));
|
|
1004
1201
|
return hash;
|
|
1005
1202
|
}
|
|
1006
1203
|
|
|
@@ -1035,32 +1232,17 @@ static void configure_mozjpeg_features_after_defaults(struct jpeg_compress_struc
|
|
|
1035
1232
|
jpeg_c_set_bool_param(cinfo, JBOOLEAN_OPTIMIZE_SCANS, FALSE);
|
|
1036
1233
|
}
|
|
1037
1234
|
|
|
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
|
-
}
|
|
1235
|
+
if (!mozjpeg_trellis_enabled)
|
|
1236
|
+
ip_disable_mozjpeg_trellis(cinfo);
|
|
1046
1237
|
|
|
1047
1238
|
return;
|
|
1048
1239
|
}
|
|
1049
1240
|
|
|
1050
1241
|
cinfo->optimize_coding = FALSE;
|
|
1051
|
-
|
|
1052
|
-
cinfo->dct_method = JDCT_ISLOW;
|
|
1053
|
-
#else
|
|
1054
|
-
cinfo->dct_method = JDCT_FASTEST;
|
|
1055
|
-
#endif
|
|
1242
|
+
cinfo->dct_method = IP_FAST_DCT;
|
|
1056
1243
|
cinfo->smoothing_factor = 0;
|
|
1057
1244
|
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);
|
|
1245
|
+
ip_disable_mozjpeg_trellis(cinfo);
|
|
1064
1246
|
|
|
1065
1247
|
if (progressive_requested) {
|
|
1066
1248
|
jpeg_simple_progression(cinfo);
|
|
@@ -1068,73 +1250,20 @@ static void configure_mozjpeg_features_after_defaults(struct jpeg_compress_struc
|
|
|
1068
1250
|
}
|
|
1069
1251
|
}
|
|
1070
1252
|
|
|
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
1253
|
static int encode_pixels_with_libjpeg(ip_context_t *ctx, int mozjpeg_size_mode) {
|
|
1118
1254
|
struct jpeg_compress_struct cinfo;
|
|
1119
1255
|
ip_jpeg_error_mgr jerr;
|
|
1120
1256
|
unsigned long jpeg_size = 0;
|
|
1121
1257
|
memset(&cinfo, 0, sizeof(cinfo));
|
|
1122
1258
|
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;
|
|
1259
|
+
cinfo.err = ip_use_error(&jerr, ctx, ip_jpeg_encode_error_exit);
|
|
1127
1260
|
ctx->transient_jpeg_buf = NULL;
|
|
1128
1261
|
|
|
1129
1262
|
ctx->jmp_armed = 1;
|
|
1130
1263
|
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
1264
|
if (ctx->status == IP_OK)
|
|
1136
1265
|
ip_context_set_error(ctx, IP_ERR_ENCODE, "JPEG encode failed");
|
|
1137
|
-
|
|
1266
|
+
goto fail;
|
|
1138
1267
|
}
|
|
1139
1268
|
|
|
1140
1269
|
jpeg_create_compress(&cinfo);
|
|
@@ -1142,8 +1271,9 @@ static int encode_pixels_with_libjpeg(ip_context_t *ctx, int mozjpeg_size_mode)
|
|
|
1142
1271
|
|
|
1143
1272
|
cinfo.image_width = (JDIMENSION)ctx->width;
|
|
1144
1273
|
cinfo.image_height = (JDIMENSION)ctx->height;
|
|
1145
|
-
cinfo.input_components = ctx->channels
|
|
1146
|
-
cinfo.in_color_space =
|
|
1274
|
+
cinfo.input_components = ctx->channels;
|
|
1275
|
+
cinfo.in_color_space =
|
|
1276
|
+
ctx->channels == 4 ? JCS_EXT_RGBA : color_space_for_channels(ctx->channels);
|
|
1147
1277
|
|
|
1148
1278
|
configure_mozjpeg_profile_before_defaults(&cinfo, mozjpeg_size_mode);
|
|
1149
1279
|
jpeg_set_defaults(&cinfo);
|
|
@@ -1153,20 +1283,12 @@ static int encode_pixels_with_libjpeg(ip_context_t *ctx, int mozjpeg_size_mode)
|
|
|
1153
1283
|
|
|
1154
1284
|
jpeg_start_compress(&cinfo, TRUE);
|
|
1155
1285
|
|
|
1156
|
-
if (!ctx->strip_metadata)
|
|
1286
|
+
if (!ctx->strip_metadata)
|
|
1157
1287
|
ip_write_preserved_markers(ctx, &cinfo);
|
|
1158
|
-
}
|
|
1159
1288
|
|
|
1160
1289
|
while (cinfo.next_scanline < cinfo.image_height) {
|
|
1161
|
-
if (ctx->cancellable_requested && atomic_load(&ctx->cancelled))
|
|
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
|
-
}
|
|
1290
|
+
if (ctx->cancellable_requested && atomic_load(&ctx->cancelled))
|
|
1291
|
+
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "JPEG encode cancelled");
|
|
1170
1292
|
|
|
1171
1293
|
JSAMPROW rows[16];
|
|
1172
1294
|
JDIMENSION start_scanline = cinfo.next_scanline;
|
|
@@ -1174,35 +1296,33 @@ static int encode_pixels_with_libjpeg(ip_context_t *ctx, int mozjpeg_size_mode)
|
|
|
1174
1296
|
if (batch > 16)
|
|
1175
1297
|
batch = 16;
|
|
1176
1298
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
free(ctx->transient_jpeg_buf);
|
|
1181
|
-
ctx->transient_jpeg_buf = NULL;
|
|
1182
|
-
ctx->jmp_armed = 0;
|
|
1183
|
-
return 0;
|
|
1184
|
-
}
|
|
1299
|
+
for (JDIMENSION i = 0; i < batch; i++)
|
|
1300
|
+
rows[i] = (JSAMPROW)(ctx->pixel_data + ((size_t)(start_scanline + i) *
|
|
1301
|
+
(size_t)ctx->width * (size_t)ctx->channels));
|
|
1185
1302
|
|
|
1186
1303
|
jpeg_write_scanlines(&cinfo, rows, batch);
|
|
1187
1304
|
}
|
|
1188
1305
|
|
|
1189
1306
|
jpeg_finish_compress(&cinfo);
|
|
1307
|
+
|
|
1308
|
+
if (ctx->max_output_size > 0 && (size_t)jpeg_size > ctx->max_output_size)
|
|
1309
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "output exceeds max_output_size");
|
|
1310
|
+
|
|
1190
1311
|
jpeg_destroy_compress(&cinfo);
|
|
1191
1312
|
ctx->jmp_armed = 0;
|
|
1192
1313
|
|
|
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
1314
|
ctx->output_data = ctx->transient_jpeg_buf;
|
|
1201
1315
|
ctx->transient_jpeg_buf = NULL;
|
|
1202
1316
|
ctx->output_size = (size_t)jpeg_size;
|
|
1203
|
-
ctx->output_capacity = (size_t)jpeg_size;
|
|
1204
1317
|
ctx->output_owner = IP_OUTPUT_OWNER_MALLOC;
|
|
1205
1318
|
return 1;
|
|
1319
|
+
|
|
1320
|
+
fail:
|
|
1321
|
+
jpeg_destroy_compress(&cinfo);
|
|
1322
|
+
free(ctx->transient_jpeg_buf);
|
|
1323
|
+
ctx->transient_jpeg_buf = NULL;
|
|
1324
|
+
ctx->jmp_armed = 0;
|
|
1325
|
+
return 0;
|
|
1206
1326
|
}
|
|
1207
1327
|
|
|
1208
1328
|
static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, int *width,
|
|
@@ -1211,22 +1331,15 @@ static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, i
|
|
|
1211
1331
|
ip_jpeg_error_mgr jerr;
|
|
1212
1332
|
memset(&cinfo, 0, sizeof(cinfo));
|
|
1213
1333
|
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;
|
|
1334
|
+
cinfo.err = ip_use_error(&jerr, ctx, ip_jpeg_invalid_error_exit);
|
|
1218
1335
|
ctx->transient_decode_buf = NULL;
|
|
1219
1336
|
ctx->source_orientation = 1;
|
|
1220
1337
|
|
|
1221
1338
|
ctx->jmp_armed = 1;
|
|
1222
1339
|
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
1340
|
if (ctx->status == IP_OK)
|
|
1228
1341
|
ip_context_set_error(ctx, IP_ERR_INVALID_IMAGE, "JPEG decode failed");
|
|
1229
|
-
|
|
1342
|
+
goto fail;
|
|
1230
1343
|
}
|
|
1231
1344
|
|
|
1232
1345
|
jpeg_create_decompress(&cinfo);
|
|
@@ -1235,118 +1348,67 @@ static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, i
|
|
|
1235
1348
|
jpeg_save_markers(&cinfo, JPEG_APP0 + 1, 0xFFFF);
|
|
1236
1349
|
if (!ctx->strip_metadata) {
|
|
1237
1350
|
jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
|
|
1238
|
-
for (int app = 2; app < 16; app++)
|
|
1351
|
+
for (int app = 2; app < 16; app++)
|
|
1239
1352
|
jpeg_save_markers(&cinfo, JPEG_APP0 + app, 0xFFFF);
|
|
1240
|
-
}
|
|
1241
1353
|
}
|
|
1242
1354
|
|
|
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
|
-
}
|
|
1355
|
+
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
|
|
1356
|
+
IP_FAIL_GOTO(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1250
1357
|
|
|
1251
1358
|
ctx->source_orientation = ip_read_exif_orientation_from_decompress(&cinfo);
|
|
1252
1359
|
|
|
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
|
-
}
|
|
1360
|
+
if (cinfo.image_width > (JDIMENSION)INT_MAX || cinfo.image_height > (JDIMENSION)INT_MAX)
|
|
1361
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "JPEG dimensions exceed native int range");
|
|
1259
1362
|
|
|
1260
1363
|
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
|
-
}
|
|
1364
|
+
cinfo.jpeg_color_space == JCS_YCCK)
|
|
1365
|
+
IP_FAIL_GOTO(ctx, IP_ERR_UNSUPPORTED,
|
|
1366
|
+
"CMYK/YCCK JPEG input is not supported in this release");
|
|
1268
1367
|
|
|
1269
1368
|
int ch = cinfo.num_components == 1 ? 1 : 3;
|
|
1270
1369
|
|
|
1271
1370
|
ctx->width = (int)cinfo.image_width;
|
|
1272
1371
|
ctx->height = (int)cinfo.image_height;
|
|
1273
1372
|
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
|
-
}
|
|
1373
|
+
if (!ip_checked_image_size(ctx->width, ctx->height, ctx->channels, &ctx->decoded_bytes))
|
|
1374
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "decoded image size overflows native size");
|
|
1375
|
+
|
|
1280
1376
|
validate_limits_for_pixels(ctx);
|
|
1281
|
-
if (ctx->status != IP_OK)
|
|
1282
|
-
|
|
1283
|
-
ctx->jmp_armed = 0;
|
|
1284
|
-
return 0;
|
|
1285
|
-
}
|
|
1377
|
+
if (ctx->status != IP_OK)
|
|
1378
|
+
goto fail;
|
|
1286
1379
|
|
|
1287
1380
|
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
|
-
}
|
|
1381
|
+
if (fast_decode_mode)
|
|
1382
|
+
ip_apply_fast_decode(&cinfo);
|
|
1301
1383
|
|
|
1302
1384
|
jpeg_start_decompress(&cinfo);
|
|
1303
1385
|
|
|
1304
|
-
if (!ctx->strip_metadata)
|
|
1305
|
-
|
|
1306
|
-
jpeg_destroy_decompress(&cinfo);
|
|
1307
|
-
ctx->jmp_armed = 0;
|
|
1308
|
-
return 0;
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1386
|
+
if (!ctx->strip_metadata && !ip_save_markers_from_decompress(ctx, &cinfo))
|
|
1387
|
+
goto fail;
|
|
1311
1388
|
|
|
1312
1389
|
size_t row_stride = 0;
|
|
1313
1390
|
size_t size = 0;
|
|
1314
1391
|
if (!ip_checked_mul_size((size_t)cinfo.output_width, (size_t)cinfo.output_components,
|
|
1315
1392
|
&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
|
-
}
|
|
1393
|
+
!ip_checked_mul_size(row_stride, (size_t)cinfo.output_height, &size))
|
|
1394
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "decoded image buffer size overflow");
|
|
1395
|
+
|
|
1396
|
+
ctx->transient_decode_buf = (unsigned char *)malloc(size);
|
|
1397
|
+
if (!ctx->transient_decode_buf && size > 0)
|
|
1398
|
+
IP_FAIL_GOTO(ctx, IP_ERR_OOM, "failed to allocate decoded pixel buffer");
|
|
1329
1399
|
|
|
1330
1400
|
while (cinfo.output_scanline < cinfo.output_height) {
|
|
1331
|
-
if (ctx->cancellable_requested && atomic_load(&ctx->cancelled))
|
|
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
|
-
}
|
|
1401
|
+
if (ctx->cancellable_requested && atomic_load(&ctx->cancelled))
|
|
1402
|
+
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "JPEG decode cancelled");
|
|
1340
1403
|
|
|
1341
1404
|
JSAMPROW rows[16];
|
|
1342
1405
|
JDIMENSION batch = cinfo.output_height - cinfo.output_scanline;
|
|
1343
1406
|
if (batch > 16)
|
|
1344
1407
|
batch = 16;
|
|
1345
1408
|
|
|
1346
|
-
for (JDIMENSION i = 0; i < batch; i++)
|
|
1409
|
+
for (JDIMENSION i = 0; i < batch; i++)
|
|
1347
1410
|
rows[i] =
|
|
1348
1411
|
ctx->transient_decode_buf + ((size_t)(cinfo.output_scanline + i) * row_stride);
|
|
1349
|
-
}
|
|
1350
1412
|
|
|
1351
1413
|
jpeg_read_scanlines(&cinfo, rows, batch);
|
|
1352
1414
|
}
|
|
@@ -1366,6 +1428,15 @@ static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, i
|
|
|
1366
1428
|
free(buf);
|
|
1367
1429
|
return 0;
|
|
1368
1430
|
}
|
|
1431
|
+
|
|
1432
|
+
ctx->width = out_width;
|
|
1433
|
+
ctx->height = out_height;
|
|
1434
|
+
ctx->channels = ch;
|
|
1435
|
+
validate_limits_for_pixels(ctx);
|
|
1436
|
+
if (ctx->status != IP_OK) {
|
|
1437
|
+
free(buf);
|
|
1438
|
+
return 0;
|
|
1439
|
+
}
|
|
1369
1440
|
}
|
|
1370
1441
|
|
|
1371
1442
|
*pixels = buf;
|
|
@@ -1373,6 +1444,13 @@ static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, i
|
|
|
1373
1444
|
*height = out_height;
|
|
1374
1445
|
*channels = ch;
|
|
1375
1446
|
return 1;
|
|
1447
|
+
|
|
1448
|
+
fail:
|
|
1449
|
+
jpeg_destroy_decompress(&cinfo);
|
|
1450
|
+
free(ctx->transient_decode_buf);
|
|
1451
|
+
ctx->transient_decode_buf = NULL;
|
|
1452
|
+
ctx->jmp_armed = 0;
|
|
1453
|
+
return 0;
|
|
1376
1454
|
}
|
|
1377
1455
|
|
|
1378
1456
|
static int compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_size_mode) {
|
|
@@ -1407,7 +1485,6 @@ static void ip_clear_output_buffer(ip_context_t *ctx) {
|
|
|
1407
1485
|
|
|
1408
1486
|
ctx->output_data = NULL;
|
|
1409
1487
|
ctx->output_size = 0;
|
|
1410
|
-
ctx->output_capacity = 0;
|
|
1411
1488
|
ctx->output_owner = IP_OUTPUT_OWNER_NONE;
|
|
1412
1489
|
}
|
|
1413
1490
|
|
|
@@ -1417,49 +1494,29 @@ static int ip_decode_jpeg_to_luma_buffer(ip_context_t *ctx, const unsigned char
|
|
|
1417
1494
|
ip_jpeg_error_mgr jerr;
|
|
1418
1495
|
memset(&cinfo, 0, sizeof(cinfo));
|
|
1419
1496
|
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;
|
|
1497
|
+
cinfo.err = ip_use_error(&jerr, ctx, ip_jpeg_invalid_error_exit);
|
|
1424
1498
|
ctx->transient_decode_buf = NULL;
|
|
1425
1499
|
|
|
1426
1500
|
ctx->jmp_armed = 1;
|
|
1427
1501
|
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
1502
|
if (ctx->status == IP_OK)
|
|
1433
1503
|
ip_context_set_error(ctx, IP_ERR_INVALID_IMAGE, "JPEG luma decode failed");
|
|
1434
|
-
|
|
1504
|
+
goto fail;
|
|
1435
1505
|
}
|
|
1436
1506
|
|
|
1437
1507
|
jpeg_create_decompress(&cinfo);
|
|
1438
1508
|
jpeg_mem_src(&cinfo, data, (unsigned long)size);
|
|
1439
1509
|
|
|
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
|
-
}
|
|
1510
|
+
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
|
|
1511
|
+
IP_FAIL_GOTO(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1447
1512
|
|
|
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
|
-
}
|
|
1513
|
+
if (cinfo.image_width > (JDIMENSION)INT_MAX || cinfo.image_height > (JDIMENSION)INT_MAX)
|
|
1514
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "JPEG dimensions exceed native int range");
|
|
1454
1515
|
|
|
1455
1516
|
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
|
-
}
|
|
1517
|
+
cinfo.jpeg_color_space == JCS_YCCK)
|
|
1518
|
+
IP_FAIL_GOTO(ctx, IP_ERR_UNSUPPORTED,
|
|
1519
|
+
"CMYK/YCCK JPEG input is not supported in this release");
|
|
1463
1520
|
|
|
1464
1521
|
int old_width = ctx->width;
|
|
1465
1522
|
int old_height = ctx->height;
|
|
@@ -1474,10 +1531,7 @@ static int ip_decode_jpeg_to_luma_buffer(ip_context_t *ctx, const unsigned char
|
|
|
1474
1531
|
ctx->height = old_height;
|
|
1475
1532
|
ctx->channels = old_channels;
|
|
1476
1533
|
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;
|
|
1534
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "decoded luma buffer size overflows native size");
|
|
1481
1535
|
}
|
|
1482
1536
|
validate_limits_for_pixels(ctx);
|
|
1483
1537
|
ctx->width = old_width;
|
|
@@ -1486,63 +1540,35 @@ static int ip_decode_jpeg_to_luma_buffer(ip_context_t *ctx, const unsigned char
|
|
|
1486
1540
|
size_t luma_size = ctx->decoded_bytes;
|
|
1487
1541
|
ctx->decoded_bytes = old_decoded_bytes;
|
|
1488
1542
|
|
|
1489
|
-
if (ctx->status != IP_OK)
|
|
1490
|
-
|
|
1491
|
-
ctx->jmp_armed = 0;
|
|
1492
|
-
return 0;
|
|
1493
|
-
}
|
|
1543
|
+
if (ctx->status != IP_OK)
|
|
1544
|
+
goto fail;
|
|
1494
1545
|
|
|
1495
1546
|
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;
|
|
1547
|
+
ip_apply_fast_decode(&cinfo);
|
|
1506
1548
|
|
|
1507
1549
|
jpeg_start_decompress(&cinfo);
|
|
1508
1550
|
|
|
1509
1551
|
size_t luma_stride = (size_t)cinfo.output_width;
|
|
1510
1552
|
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
|
-
}
|
|
1553
|
+
luma_size != luma_stride * (size_t)cinfo.output_height)
|
|
1554
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "decoded luma buffer size mismatch");
|
|
1517
1555
|
|
|
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
|
-
}
|
|
1556
|
+
ctx->transient_decode_buf = (unsigned char *)malloc(luma_size);
|
|
1557
|
+
if (!ctx->transient_decode_buf && luma_size > 0)
|
|
1558
|
+
IP_FAIL_GOTO(ctx, IP_ERR_OOM, "failed to allocate luma buffer");
|
|
1525
1559
|
|
|
1526
1560
|
while (cinfo.output_scanline < cinfo.output_height) {
|
|
1527
|
-
if (ctx->cancellable_requested && atomic_load(&ctx->cancelled))
|
|
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
|
-
}
|
|
1561
|
+
if (ctx->cancellable_requested && atomic_load(&ctx->cancelled))
|
|
1562
|
+
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "JPEG luma decode cancelled");
|
|
1536
1563
|
|
|
1537
1564
|
JSAMPROW rows[16];
|
|
1538
1565
|
JDIMENSION batch = cinfo.output_height - cinfo.output_scanline;
|
|
1539
1566
|
if (batch > 16)
|
|
1540
1567
|
batch = 16;
|
|
1541
1568
|
|
|
1542
|
-
for (JDIMENSION i = 0; i < batch; i++)
|
|
1569
|
+
for (JDIMENSION i = 0; i < batch; i++)
|
|
1543
1570
|
rows[i] =
|
|
1544
1571
|
ctx->transient_decode_buf + ((size_t)(cinfo.output_scanline + i) * luma_stride);
|
|
1545
|
-
}
|
|
1546
1572
|
|
|
1547
1573
|
jpeg_read_scanlines(&cinfo, rows, batch);
|
|
1548
1574
|
}
|
|
@@ -1559,6 +1585,13 @@ static int ip_decode_jpeg_to_luma_buffer(ip_context_t *ctx, const unsigned char
|
|
|
1559
1585
|
*width = out_width;
|
|
1560
1586
|
*height = out_height;
|
|
1561
1587
|
return 1;
|
|
1588
|
+
|
|
1589
|
+
fail:
|
|
1590
|
+
jpeg_destroy_decompress(&cinfo);
|
|
1591
|
+
free(ctx->transient_decode_buf);
|
|
1592
|
+
ctx->transient_decode_buf = NULL;
|
|
1593
|
+
ctx->jmp_armed = 0;
|
|
1594
|
+
return 0;
|
|
1562
1595
|
}
|
|
1563
1596
|
|
|
1564
1597
|
static unsigned char *ip_build_luma_buffer(ip_context_t *ctx, const unsigned char *pixels,
|
|
@@ -1569,7 +1602,7 @@ static unsigned char *ip_build_luma_buffer(ip_context_t *ctx, const unsigned cha
|
|
|
1569
1602
|
return NULL;
|
|
1570
1603
|
}
|
|
1571
1604
|
|
|
1572
|
-
unsigned char *luma = (unsigned char *)
|
|
1605
|
+
unsigned char *luma = (unsigned char *)malloc(count);
|
|
1573
1606
|
if (!luma) {
|
|
1574
1607
|
ip_context_set_error(ctx, IP_ERR_OOM, "failed to allocate luma buffer");
|
|
1575
1608
|
return NULL;
|
|
@@ -1588,7 +1621,7 @@ static unsigned char *ip_build_luma_buffer(ip_context_t *ctx, const unsigned cha
|
|
|
1588
1621
|
unsigned int r = src[i * 3 + 0];
|
|
1589
1622
|
unsigned int g = src[i * 3 + 1];
|
|
1590
1623
|
unsigned int b = src[i * 3 + 2];
|
|
1591
|
-
dst[i] = (unsigned char)((
|
|
1624
|
+
dst[i] = (unsigned char)((19595u * r + 38470u * g + 7471u * b + 32768u) >> 16);
|
|
1592
1625
|
}
|
|
1593
1626
|
return luma;
|
|
1594
1627
|
}
|
|
@@ -1597,15 +1630,15 @@ static unsigned char *ip_build_luma_buffer(ip_context_t *ctx, const unsigned cha
|
|
|
1597
1630
|
unsigned int r = src[i * 4 + 0];
|
|
1598
1631
|
unsigned int g = src[i * 4 + 1];
|
|
1599
1632
|
unsigned int b = src[i * 4 + 2];
|
|
1600
|
-
dst[i] = (unsigned char)((
|
|
1633
|
+
dst[i] = (unsigned char)((19595u * r + 38470u * g + 7471u * b + 32768u) >> 16);
|
|
1601
1634
|
}
|
|
1602
1635
|
return luma;
|
|
1603
1636
|
}
|
|
1604
1637
|
|
|
1605
1638
|
static double ip_ssim_window_score_double(int32_t n, int32_t sum_a, int32_t sum_b, int32_t sum_a2,
|
|
1606
1639
|
int32_t sum_b2, int32_t sum_ab) {
|
|
1607
|
-
const double c1 = 6.5025;
|
|
1608
|
-
const double c2 = 58.5225;
|
|
1640
|
+
const double c1 = 6.5025;
|
|
1641
|
+
const double c2 = 58.5225;
|
|
1609
1642
|
|
|
1610
1643
|
double inv_n = 1.0 / (double)n;
|
|
1611
1644
|
double mean_a = (double)sum_a * inv_n;
|
|
@@ -1799,7 +1832,6 @@ static int guarded_compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_
|
|
|
1799
1832
|
size_t candidate_jpeg_size = ctx->output_size;
|
|
1800
1833
|
ctx->output_data = NULL;
|
|
1801
1834
|
ctx->output_size = 0;
|
|
1802
|
-
ctx->output_capacity = 0;
|
|
1803
1835
|
ctx->output_owner = IP_OUTPUT_OWNER_NONE;
|
|
1804
1836
|
|
|
1805
1837
|
unsigned char *candidate_luma = NULL;
|
|
@@ -1865,7 +1897,6 @@ static int guarded_compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_
|
|
|
1865
1897
|
|
|
1866
1898
|
ctx->output_data = best_jpeg;
|
|
1867
1899
|
ctx->output_size = best_jpeg_size;
|
|
1868
|
-
ctx->output_capacity = best_jpeg_size;
|
|
1869
1900
|
ctx->output_owner = IP_OUTPUT_OWNER_MALLOC;
|
|
1870
1901
|
return 1;
|
|
1871
1902
|
}
|
|
@@ -1896,20 +1927,7 @@ static int ip_validate_lossless_optimize_header(ip_context_t *ctx,
|
|
|
1896
1927
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "max_width/max_height must be >= 0");
|
|
1897
1928
|
return 0;
|
|
1898
1929
|
}
|
|
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;
|
|
1930
|
+
return ip_check_max_dimension_limits(ctx);
|
|
1913
1931
|
}
|
|
1914
1932
|
|
|
1915
1933
|
static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
@@ -1926,24 +1944,14 @@ static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
|
1926
1944
|
memset(&dsterr, 0, sizeof(dsterr));
|
|
1927
1945
|
ctx->transient_jpeg_buf = NULL;
|
|
1928
1946
|
|
|
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;
|
|
1947
|
+
srcinfo.err = ip_use_error(&srcerr, ctx, ip_jpeg_invalid_error_exit);
|
|
1948
|
+
dstinfo.err = ip_use_error(&dsterr, ctx, ip_jpeg_encode_error_exit);
|
|
1936
1949
|
|
|
1937
1950
|
ctx->jmp_armed = 1;
|
|
1938
1951
|
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
1952
|
if (ctx->status == IP_OK)
|
|
1945
1953
|
ip_context_set_error(ctx, IP_ERR_ENCODE, "lossless JPEG optimize failed");
|
|
1946
|
-
|
|
1954
|
+
goto fail;
|
|
1947
1955
|
}
|
|
1948
1956
|
|
|
1949
1957
|
jpeg_create_decompress(&srcinfo);
|
|
@@ -1951,47 +1959,23 @@ static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
|
1951
1959
|
jpeg_mem_src(&srcinfo, ctx->input_data, (unsigned long)ctx->input_size);
|
|
1952
1960
|
ip_setup_marker_saving(&srcinfo, ctx->strip_metadata);
|
|
1953
1961
|
|
|
1954
|
-
if (ctx->cancellable_requested && atomic_load(&ctx->cancelled))
|
|
1955
|
-
|
|
1956
|
-
jpeg_destroy_compress(&dstinfo);
|
|
1957
|
-
jpeg_destroy_decompress(&srcinfo);
|
|
1958
|
-
ctx->jmp_armed = 0;
|
|
1959
|
-
return 0;
|
|
1960
|
-
}
|
|
1962
|
+
if (ctx->cancellable_requested && atomic_load(&ctx->cancelled))
|
|
1963
|
+
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "lossless JPEG optimize cancelled");
|
|
1961
1964
|
|
|
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
|
-
}
|
|
1965
|
+
if (jpeg_read_header(&srcinfo, TRUE) != JPEG_HEADER_OK)
|
|
1966
|
+
IP_FAIL_GOTO(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1970
1967
|
|
|
1971
1968
|
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
|
-
}
|
|
1969
|
+
if (ctx->strip_metadata && ctx->source_orientation > 1)
|
|
1970
|
+
IP_FAIL_GOTO(ctx, IP_ERR_UNSUPPORTED,
|
|
1971
|
+
"lossless optimize cannot strip EXIF Orientation without changing "
|
|
1972
|
+
"visual orientation; use strip_metadata: false or ImagePack.compress");
|
|
1981
1973
|
|
|
1982
|
-
if (!ip_validate_lossless_optimize_header(ctx, &srcinfo))
|
|
1983
|
-
|
|
1984
|
-
jpeg_destroy_decompress(&srcinfo);
|
|
1985
|
-
ctx->jmp_armed = 0;
|
|
1986
|
-
return 0;
|
|
1987
|
-
}
|
|
1974
|
+
if (!ip_validate_lossless_optimize_header(ctx, &srcinfo))
|
|
1975
|
+
goto fail;
|
|
1988
1976
|
|
|
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
|
-
}
|
|
1977
|
+
if (!ctx->strip_metadata && !ip_save_markers_from_decompress(ctx, &srcinfo))
|
|
1978
|
+
goto fail;
|
|
1995
1979
|
|
|
1996
1980
|
coef_arrays = jpeg_read_coefficients(&srcinfo);
|
|
1997
1981
|
jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
|
|
@@ -2005,15 +1989,8 @@ static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
|
2005
1989
|
|
|
2006
1990
|
jpeg_mem_dest(&dstinfo, &ctx->transient_jpeg_buf, &jpeg_size);
|
|
2007
1991
|
|
|
2008
|
-
if (ctx->cancellable_requested && atomic_load(&ctx->cancelled))
|
|
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
|
-
}
|
|
1992
|
+
if (ctx->cancellable_requested && atomic_load(&ctx->cancelled))
|
|
1993
|
+
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "lossless JPEG optimize cancelled");
|
|
2017
1994
|
|
|
2018
1995
|
jpeg_write_coefficients(&dstinfo, coef_arrays);
|
|
2019
1996
|
if (!ctx->strip_metadata)
|
|
@@ -2021,23 +1998,27 @@ static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
|
2021
1998
|
|
|
2022
1999
|
jpeg_finish_compress(&dstinfo);
|
|
2023
2000
|
jpeg_finish_decompress(&srcinfo);
|
|
2001
|
+
|
|
2002
|
+
if (ctx->max_output_size > 0 && (size_t)jpeg_size > ctx->max_output_size)
|
|
2003
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "output exceeds max_output_size");
|
|
2004
|
+
|
|
2024
2005
|
jpeg_destroy_compress(&dstinfo);
|
|
2025
2006
|
jpeg_destroy_decompress(&srcinfo);
|
|
2026
2007
|
ctx->jmp_armed = 0;
|
|
2027
2008
|
|
|
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
2009
|
ctx->output_data = ctx->transient_jpeg_buf;
|
|
2036
2010
|
ctx->transient_jpeg_buf = NULL;
|
|
2037
2011
|
ctx->output_size = (size_t)jpeg_size;
|
|
2038
|
-
ctx->output_capacity = (size_t)jpeg_size;
|
|
2039
2012
|
ctx->output_owner = IP_OUTPUT_OWNER_MALLOC;
|
|
2040
2013
|
return 1;
|
|
2014
|
+
|
|
2015
|
+
fail:
|
|
2016
|
+
jpeg_destroy_compress(&dstinfo);
|
|
2017
|
+
jpeg_destroy_decompress(&srcinfo);
|
|
2018
|
+
free(ctx->transient_jpeg_buf);
|
|
2019
|
+
ctx->transient_jpeg_buf = NULL;
|
|
2020
|
+
ctx->jmp_armed = 0;
|
|
2021
|
+
return 0;
|
|
2041
2022
|
}
|
|
2042
2023
|
|
|
2043
2024
|
static int ip_jpeg_turbo_compress(ip_context_t *ctx) {
|
|
@@ -2052,6 +2033,15 @@ static int ip_mozjpeg_compress(ip_context_t *ctx) {
|
|
|
2052
2033
|
return compress_jpeg_input_with_mode(ctx, 1);
|
|
2053
2034
|
}
|
|
2054
2035
|
|
|
2036
|
+
static ip_execution_t ip_async_execution(const ip_context_t *ctx) {
|
|
2037
|
+
#if IMAGE_PACK_HAS_OFFLOAD_SAFE
|
|
2038
|
+
return ctx->has_scheduler ? IP_EXEC_OFFLOAD : IP_EXEC_NOGVL;
|
|
2039
|
+
#else
|
|
2040
|
+
(void)ctx;
|
|
2041
|
+
return IP_EXEC_NOGVL;
|
|
2042
|
+
#endif
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2055
2045
|
static void ip_resolve_execution(ip_context_t *ctx) {
|
|
2056
2046
|
if (ctx->requested_execution != IP_EXEC_AUTO) {
|
|
2057
2047
|
ctx->resolved_execution = ctx->requested_execution;
|
|
@@ -2059,12 +2049,12 @@ static void ip_resolve_execution(ip_context_t *ctx) {
|
|
|
2059
2049
|
}
|
|
2060
2050
|
|
|
2061
2051
|
if (ctx->cancellable_requested) {
|
|
2062
|
-
ctx->resolved_execution = ctx
|
|
2052
|
+
ctx->resolved_execution = ip_async_execution(ctx);
|
|
2063
2053
|
return;
|
|
2064
2054
|
}
|
|
2065
2055
|
|
|
2066
2056
|
if (ctx->ssim_guard_enabled) {
|
|
2067
|
-
ctx->resolved_execution = ctx
|
|
2057
|
+
ctx->resolved_execution = ip_async_execution(ctx);
|
|
2068
2058
|
return;
|
|
2069
2059
|
}
|
|
2070
2060
|
|
|
@@ -2074,7 +2064,7 @@ static void ip_resolve_execution(ip_context_t *ctx) {
|
|
|
2074
2064
|
return;
|
|
2075
2065
|
}
|
|
2076
2066
|
|
|
2077
|
-
ctx->resolved_execution = ctx
|
|
2067
|
+
ctx->resolved_execution = ip_async_execution(ctx);
|
|
2078
2068
|
}
|
|
2079
2069
|
|
|
2080
2070
|
static void ip_unblock_function(void *data) {
|
|
@@ -2111,7 +2101,12 @@ static int ip_run_context(ip_context_t *ctx) {
|
|
|
2111
2101
|
} else if (ctx->resolved_execution == IP_EXEC_NOGVL) {
|
|
2112
2102
|
rb_nogvl(ip_run_encode_nogvl, ctx, ip_unblock_function, ctx, 0);
|
|
2113
2103
|
} else if (ctx->resolved_execution == IP_EXEC_OFFLOAD) {
|
|
2104
|
+
#if IMAGE_PACK_HAS_OFFLOAD_SAFE
|
|
2114
2105
|
rb_nogvl(ip_run_encode_nogvl, ctx, ip_unblock_function, ctx, RB_NOGVL_OFFLOAD_SAFE);
|
|
2106
|
+
#else
|
|
2107
|
+
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
2108
|
+
"offload execution requires Ruby >= 3.4; use :nogvl or :auto");
|
|
2109
|
+
#endif
|
|
2115
2110
|
} else {
|
|
2116
2111
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "invalid resolved execution mode");
|
|
2117
2112
|
}
|
|
@@ -2134,7 +2129,12 @@ static int ip_run_optimize_context(ip_context_t *ctx) {
|
|
|
2134
2129
|
} else if (ctx->resolved_execution == IP_EXEC_NOGVL) {
|
|
2135
2130
|
rb_nogvl(ip_run_optimize_nogvl, ctx, ip_unblock_function, ctx, 0);
|
|
2136
2131
|
} else if (ctx->resolved_execution == IP_EXEC_OFFLOAD) {
|
|
2132
|
+
#if IMAGE_PACK_HAS_OFFLOAD_SAFE
|
|
2137
2133
|
rb_nogvl(ip_run_optimize_nogvl, ctx, ip_unblock_function, ctx, RB_NOGVL_OFFLOAD_SAFE);
|
|
2134
|
+
#else
|
|
2135
|
+
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
2136
|
+
"offload execution requires Ruby >= 3.4; use :nogvl or :auto");
|
|
2137
|
+
#endif
|
|
2138
2138
|
} else {
|
|
2139
2139
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "invalid resolved execution mode");
|
|
2140
2140
|
}
|
|
@@ -2142,31 +2142,40 @@ static int ip_run_optimize_context(ip_context_t *ctx) {
|
|
|
2142
2142
|
return ctx->status == IP_OK;
|
|
2143
2143
|
}
|
|
2144
2144
|
|
|
2145
|
-
static size_t config_size_value(VALUE config, ID id, size_t fallback) {
|
|
2145
|
+
static size_t config_size_value(VALUE config, ID id, size_t fallback, const char *name) {
|
|
2146
2146
|
VALUE value = rb_funcall(config, id, 0);
|
|
2147
2147
|
if (NIL_P(value))
|
|
2148
2148
|
return fallback;
|
|
2149
|
+
if (!RB_INTEGER_TYPE_P(value) || ip_value_negative(value)) {
|
|
2150
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "%s must be an Integer >= 0", name);
|
|
2151
|
+
}
|
|
2149
2152
|
return NUM2SIZET(value);
|
|
2150
2153
|
}
|
|
2151
2154
|
|
|
2152
|
-
static int config_int_value(VALUE config, ID id, int fallback) {
|
|
2155
|
+
static int config_int_value(VALUE config, ID id, int fallback, const char *name) {
|
|
2153
2156
|
VALUE value = rb_funcall(config, id, 0);
|
|
2154
2157
|
if (NIL_P(value))
|
|
2155
2158
|
return fallback;
|
|
2159
|
+
if (!RB_INTEGER_TYPE_P(value) || ip_value_negative(value)) {
|
|
2160
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "%s must be an Integer >= 0", name);
|
|
2161
|
+
}
|
|
2156
2162
|
return NUM2INT(value);
|
|
2157
2163
|
}
|
|
2158
2164
|
|
|
2159
2165
|
static void apply_configuration(VALUE self, ip_context_t *ctx) {
|
|
2160
2166
|
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->
|
|
2167
|
+
ctx->direct_input_threshold = config_size_value(
|
|
2168
|
+
config, id_direct_input_threshold, ctx->direct_input_threshold, "direct_input_threshold");
|
|
2169
|
+
ctx->direct_pixel_threshold = config_size_value(
|
|
2170
|
+
config, id_direct_pixel_threshold, ctx->direct_pixel_threshold, "direct_pixel_threshold");
|
|
2171
|
+
ctx->max_pixels =
|
|
2172
|
+
(uint64_t)config_size_value(config, id_max_pixels, (size_t)ctx->max_pixels, "max_pixels");
|
|
2173
|
+
ctx->max_width = config_int_value(config, id_max_width, ctx->max_width, "max_width");
|
|
2174
|
+
ctx->max_height = config_int_value(config, id_max_height, ctx->max_height, "max_height");
|
|
2175
|
+
ctx->max_output_size =
|
|
2176
|
+
config_size_value(config, id_max_output_size, ctx->max_output_size, "max_output_size");
|
|
2177
|
+
ctx->max_input_size =
|
|
2178
|
+
config_size_value(config, id_max_input_size, ctx->max_input_size, "max_input_size");
|
|
2170
2179
|
}
|
|
2171
2180
|
|
|
2172
2181
|
static void validate_limits_for_pixels(ip_context_t *ctx) {
|
|
@@ -2180,18 +2189,8 @@ static void validate_limits_for_pixels(ip_context_t *ctx) {
|
|
|
2180
2189
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "max_width/max_height must be >= 0");
|
|
2181
2190
|
return;
|
|
2182
2191
|
}
|
|
2183
|
-
if (ctx
|
|
2184
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "image width exceeds max_width");
|
|
2185
|
-
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");
|
|
2192
|
+
if (!ip_check_max_dimension_limits(ctx))
|
|
2189
2193
|
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
2194
|
if (!ip_checked_image_size(ctx->width, ctx->height, ctx->channels, &decoded_bytes)) {
|
|
2196
2195
|
ip_context_set_error(ctx, IP_ERR_LIMIT, "decoded image size overflows native size");
|
|
2197
2196
|
return;
|
|
@@ -2223,18 +2222,25 @@ static VALUE ip_compress_jpeg_entry_body(VALUE ptr) {
|
|
|
2223
2222
|
ctx->has_scheduler = ip_bool_value(call->has_scheduler);
|
|
2224
2223
|
apply_configuration(call->self, ctx);
|
|
2225
2224
|
|
|
2226
|
-
|
|
2227
|
-
|
|
2225
|
+
ip_input_kind_t in_kind = ip_parse_input_kind(call->input_kind);
|
|
2226
|
+
if (!ip_prepare_output_path(ctx, call->output, out_kind) ||
|
|
2227
|
+
!ip_prepare_input_bytes(ctx, call->input, in_kind)) {
|
|
2228
2228
|
ip_raise_for_status(ctx);
|
|
2229
2229
|
rb_raise(rb_eImagePackInvalidArgumentError, "invalid JPEG input");
|
|
2230
2230
|
}
|
|
2231
2231
|
|
|
2232
2232
|
if (ctx->requested_execution == IP_EXEC_AUTO && ctx->input_size < ctx->direct_input_threshold &&
|
|
2233
|
-
!ip_inspect_jpeg_header(ctx)) {
|
|
2233
|
+
!ip_inspect_jpeg_header(ctx, 0)) {
|
|
2234
2234
|
ip_raise_for_status(ctx);
|
|
2235
2235
|
rb_raise(rb_eImagePackInvalidImageError, "invalid JPEG input");
|
|
2236
2236
|
}
|
|
2237
2237
|
|
|
2238
|
+
ip_resolve_execution(ctx);
|
|
2239
|
+
if (!ip_ensure_owned_input_for_async(ctx, call->input, in_kind)) {
|
|
2240
|
+
ip_raise_for_status(ctx);
|
|
2241
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "invalid JPEG input");
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2238
2244
|
ip_run_context(ctx);
|
|
2239
2245
|
return ip_finish_output(ctx, out_kind);
|
|
2240
2246
|
}
|
|
@@ -2265,6 +2271,7 @@ static VALUE ip_compress_pixels_entry_body(VALUE ptr) {
|
|
|
2265
2271
|
ctx->min_ssim = NUM2DBL(call->min_ssim);
|
|
2266
2272
|
ctx->ssim_guard_enabled = ctx->min_ssim > 0.0;
|
|
2267
2273
|
ip_validate_min_ssim_or_raise(ctx);
|
|
2274
|
+
ctx->mozjpeg_trellis_enabled = ip_bool_value(call->mozjpeg_trellis);
|
|
2268
2275
|
ctx->progressive = ip_bool_value(call->progressive);
|
|
2269
2276
|
ctx->strip_metadata = 1;
|
|
2270
2277
|
ctx->requested_execution = ip_parse_execution(call->execution);
|
|
@@ -2272,9 +2279,9 @@ static VALUE ip_compress_pixels_entry_body(VALUE ptr) {
|
|
|
2272
2279
|
ctx->has_scheduler = ip_bool_value(call->has_scheduler);
|
|
2273
2280
|
apply_configuration(call->self, ctx);
|
|
2274
2281
|
|
|
2275
|
-
if (!
|
|
2276
|
-
|
|
2277
|
-
|
|
2282
|
+
if (!ip_prepare_output_path(ctx, call->output, out_kind) ||
|
|
2283
|
+
!ip_prepare_pixels(ctx, call->buffer, NUM2INT(call->width), NUM2INT(call->height),
|
|
2284
|
+
NUM2INT(call->channels), ip_bool_value(call->exact_size))) {
|
|
2278
2285
|
ip_raise_for_status(ctx);
|
|
2279
2286
|
rb_raise(rb_eImagePackInvalidArgumentError, "invalid pixel input");
|
|
2280
2287
|
}
|
|
@@ -2283,17 +2290,25 @@ static VALUE ip_compress_pixels_entry_body(VALUE ptr) {
|
|
|
2283
2290
|
if (ctx->status != IP_OK)
|
|
2284
2291
|
ip_raise_for_status(ctx);
|
|
2285
2292
|
|
|
2293
|
+
ip_resolve_execution(ctx);
|
|
2294
|
+
if (!ip_ensure_owned_pixels_for_async(ctx, call->buffer)) {
|
|
2295
|
+
ip_raise_for_status(ctx);
|
|
2296
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "invalid pixel input");
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2286
2299
|
ip_run_context(ctx);
|
|
2287
2300
|
return ip_finish_output(ctx, out_kind);
|
|
2288
2301
|
}
|
|
2289
2302
|
|
|
2290
2303
|
static VALUE ip_compress_pixels_entry(VALUE self, VALUE buffer, VALUE width, VALUE height,
|
|
2291
2304
|
VALUE channels, VALUE output, VALUE output_kind, VALUE algo,
|
|
2292
|
-
VALUE quality, VALUE min_ssim, VALUE
|
|
2293
|
-
VALUE
|
|
2305
|
+
VALUE quality, VALUE min_ssim, VALUE mozjpeg_trellis,
|
|
2306
|
+
VALUE progressive, VALUE exact_size, VALUE execution,
|
|
2307
|
+
VALUE cancellable, VALUE has_scheduler) {
|
|
2294
2308
|
ip_compress_pixels_call_t call = {
|
|
2295
|
-
self,
|
|
2296
|
-
quality,
|
|
2309
|
+
self, buffer, width, height, channels, output, output_kind,
|
|
2310
|
+
algo, quality, min_ssim, mozjpeg_trellis, progressive, exact_size, execution,
|
|
2311
|
+
cancellable, has_scheduler, NULL};
|
|
2297
2312
|
return rb_ensure(ip_compress_pixels_entry_body, (VALUE)&call, ip_call_cleanup,
|
|
2298
2313
|
(VALUE)&call.ctx);
|
|
2299
2314
|
}
|
|
@@ -2314,8 +2329,21 @@ static VALUE ip_optimize_jpeg_entry_body(VALUE ptr) {
|
|
|
2314
2329
|
ctx->ssim_guard_enabled = 0;
|
|
2315
2330
|
apply_configuration(call->self, ctx);
|
|
2316
2331
|
|
|
2317
|
-
|
|
2318
|
-
|
|
2332
|
+
ip_input_kind_t in_kind = ip_parse_input_kind(call->input_kind);
|
|
2333
|
+
if (!ip_prepare_output_path(ctx, call->output, out_kind) ||
|
|
2334
|
+
!ip_prepare_input_bytes(ctx, call->input, in_kind)) {
|
|
2335
|
+
ip_raise_for_status(ctx);
|
|
2336
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "invalid JPEG input");
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
if (ctx->requested_execution == IP_EXEC_AUTO && ctx->input_size < ctx->direct_input_threshold &&
|
|
2340
|
+
!ip_inspect_jpeg_header(ctx, 1)) {
|
|
2341
|
+
ip_raise_for_status(ctx);
|
|
2342
|
+
rb_raise(rb_eImagePackInvalidImageError, "invalid JPEG input");
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
ip_resolve_execution(ctx);
|
|
2346
|
+
if (!ip_ensure_owned_input_for_async(ctx, call->input, in_kind)) {
|
|
2319
2347
|
ip_raise_for_status(ctx);
|
|
2320
2348
|
rb_raise(rb_eImagePackInvalidArgumentError, "invalid JPEG input");
|
|
2321
2349
|
}
|
|
@@ -2334,37 +2362,46 @@ static VALUE ip_optimize_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, V
|
|
|
2334
2362
|
}
|
|
2335
2363
|
|
|
2336
2364
|
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
|
-
|
|
2365
|
+
static const struct {
|
|
2366
|
+
ID *slot;
|
|
2367
|
+
const char *name;
|
|
2368
|
+
} ids[] = {{&id_jpeg_turbo, "jpeg_turbo"},
|
|
2369
|
+
{&id_mozjpeg, "mozjpeg"},
|
|
2370
|
+
{&id_direct, "direct"},
|
|
2371
|
+
{&id_nogvl, "nogvl"},
|
|
2372
|
+
{&id_offload, "offload"},
|
|
2373
|
+
{&id_auto, "auto"},
|
|
2374
|
+
{&id_bytes, "bytes"},
|
|
2375
|
+
{&id_path, "path"},
|
|
2376
|
+
{&id_io_buffer, "io_buffer"},
|
|
2377
|
+
{&id_return_string, "return_string"},
|
|
2378
|
+
{&id_configuration, "configuration"},
|
|
2379
|
+
{&id_direct_input_threshold, "direct_input_threshold"},
|
|
2380
|
+
{&id_direct_pixel_threshold, "direct_pixel_threshold"},
|
|
2381
|
+
{&id_max_pixels, "max_pixels"},
|
|
2382
|
+
{&id_max_width, "max_width"},
|
|
2383
|
+
{&id_max_height, "max_height"},
|
|
2384
|
+
{&id_max_output_size, "max_output_size"},
|
|
2385
|
+
{&id_max_input_size, "max_input_size"}};
|
|
2386
|
+
for (size_t i = 0; i < IP_ARRAY_LEN(ids); i++)
|
|
2387
|
+
*ids[i].slot = rb_intern(ids[i].name);
|
|
2355
2388
|
|
|
2356
2389
|
rb_mImagePack = rb_define_module("ImagePack");
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2390
|
+
|
|
2391
|
+
static const struct {
|
|
2392
|
+
VALUE *slot;
|
|
2393
|
+
const char *name;
|
|
2394
|
+
} exceptions[] = {{&rb_eImagePackError, "Error"},
|
|
2395
|
+
{&rb_eImagePackInvalidArgumentError, "InvalidArgumentError"},
|
|
2396
|
+
{&rb_eImagePackInvalidImageError, "InvalidImageError"},
|
|
2397
|
+
{&rb_eImagePackUnsupportedError, "UnsupportedError"},
|
|
2398
|
+
{&rb_eImagePackLimitExceededError, "LimitExceededError"},
|
|
2399
|
+
{&rb_eImagePackEncodeError, "EncodeError"},
|
|
2400
|
+
{&rb_eImagePackQualityConstraintError, "QualityConstraintError"},
|
|
2401
|
+
{&rb_eImagePackOutOfMemoryError, "OutOfMemoryError"},
|
|
2402
|
+
{&rb_eImagePackCancelledError, "CancelledError"}};
|
|
2403
|
+
for (size_t i = 0; i < IP_ARRAY_LEN(exceptions); i++)
|
|
2404
|
+
*exceptions[i].slot = rb_const_get(rb_mImagePack, rb_intern(exceptions[i].name));
|
|
2368
2405
|
|
|
2369
2406
|
rb_define_const(rb_mImagePack, "NATIVE_MOZJPEG_VERSION", rb_str_new_cstr(VERSION));
|
|
2370
2407
|
#if defined(IMAGE_PACK_HAS_SIMD)
|
|
@@ -2372,17 +2409,23 @@ IMAGE_PACK_INIT_EXPORT void Init_image_pack(void) {
|
|
|
2372
2409
|
#else
|
|
2373
2410
|
rb_define_const(rb_mImagePack, "NATIVE_SIMD", Qfalse);
|
|
2374
2411
|
#endif
|
|
2412
|
+
#if IMAGE_PACK_HAS_OFFLOAD_SAFE
|
|
2413
|
+
rb_define_const(rb_mImagePack, "NATIVE_OFFLOAD_SAFE", Qtrue);
|
|
2414
|
+
#else
|
|
2415
|
+
rb_define_const(rb_mImagePack, "NATIVE_OFFLOAD_SAFE", Qfalse);
|
|
2416
|
+
#endif
|
|
2375
2417
|
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2418
|
+
static const struct {
|
|
2419
|
+
const char *name;
|
|
2420
|
+
VALUE (*fn)(ANYARGS);
|
|
2421
|
+
int arity;
|
|
2422
|
+
} methods[] = {{"__compress_jpeg", (VALUE (*)(ANYARGS))ip_compress_jpeg_entry, 13},
|
|
2423
|
+
{"__compress_pixels", (VALUE (*)(ANYARGS))ip_compress_pixels_entry, 15},
|
|
2424
|
+
{"__optimize_jpeg", (VALUE (*)(ANYARGS))ip_optimize_jpeg_entry, 9},
|
|
2425
|
+
{"__inspect_image", (VALUE (*)(ANYARGS))ip_inspect_image_entry, 2}};
|
|
2426
|
+
for (size_t i = 0; i < IP_ARRAY_LEN(methods); i++) {
|
|
2427
|
+
rb_define_singleton_method(rb_mImagePack, methods[i].name, methods[i].fn, methods[i].arity);
|
|
2428
|
+
rb_funcall(rb_mImagePack, rb_intern("private_class_method"), 1,
|
|
2429
|
+
ID2SYM(rb_intern(methods[i].name)));
|
|
2430
|
+
}
|
|
2388
2431
|
}
|