image_pack 0.2.5 → 0.2.7
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 +32 -0
- data/README.md +29 -5
- data/ext/image_pack/extconf.rb +2 -2
- data/ext/image_pack/image_pack.c +742 -263
- data/ext/image_pack/mozjpeg_sources.rb +5 -0
- data/ext/image_pack/vendor/mozjpeg/transupp.c +2373 -0
- data/lib/image_pack/version.rb +1 -1
- data/lib/image_pack.rb +121 -7
- metadata +6 -104
- data/ext/image_pack/vendor/mozjpeg/croptest.in +0 -95
- data/ext/image_pack/vendor/mozjpeg/libjpeg.map.in +0 -11
- data/ext/image_pack/vendor/mozjpeg/simd/arm/aarch32/jsimd_neon.S +0 -1200
- data/ext/image_pack/vendor/mozjpeg/simd/arm/aarch64/jsimd_neon.S +0 -2254
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jccolext-avx2.asm +0 -578
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jccolext-mmx.asm +0 -476
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jccolext-sse2.asm +0 -503
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jccolor-avx2.asm +0 -121
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jccolor-mmx.asm +0 -121
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jccolor-sse2.asm +0 -120
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jcgray-avx2.asm +0 -113
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jcgray-mmx.asm +0 -113
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jcgray-sse2.asm +0 -112
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jcgryext-avx2.asm +0 -457
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jcgryext-mmx.asm +0 -355
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jcgryext-sse2.asm +0 -382
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jchuff-sse2.asm +0 -761
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jcphuff-sse2.asm +0 -662
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jcsample-avx2.asm +0 -388
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jcsample-mmx.asm +0 -324
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jcsample-sse2.asm +0 -351
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdcolext-avx2.asm +0 -515
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdcolext-mmx.asm +0 -404
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdcolext-sse2.asm +0 -458
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdcolor-avx2.asm +0 -118
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdcolor-mmx.asm +0 -117
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdcolor-sse2.asm +0 -117
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdmerge-avx2.asm +0 -136
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdmerge-mmx.asm +0 -123
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdmerge-sse2.asm +0 -135
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdmrgext-avx2.asm +0 -575
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdmrgext-mmx.asm +0 -460
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdmrgext-sse2.asm +0 -517
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdsample-avx2.asm +0 -760
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdsample-mmx.asm +0 -731
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jdsample-sse2.asm +0 -724
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jfdctflt-3dn.asm +0 -318
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jfdctflt-sse.asm +0 -369
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jfdctfst-mmx.asm +0 -395
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jfdctfst-sse2.asm +0 -403
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jfdctint-avx2.asm +0 -331
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jfdctint-mmx.asm +0 -620
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jfdctint-sse2.asm +0 -633
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jidctflt-3dn.asm +0 -451
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jidctflt-sse.asm +0 -571
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jidctflt-sse2.asm +0 -497
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jidctfst-mmx.asm +0 -499
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jidctfst-sse2.asm +0 -501
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jidctint-avx2.asm +0 -453
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jidctint-mmx.asm +0 -851
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jidctint-sse2.asm +0 -858
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jidctred-mmx.asm +0 -704
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jidctred-sse2.asm +0 -592
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jquant-3dn.asm +0 -230
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jquant-mmx.asm +0 -276
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jquant-sse.asm +0 -208
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jquantf-sse2.asm +0 -168
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jquanti-avx2.asm +0 -188
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jquanti-sse2.asm +0 -201
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jsimdcpu.asm +0 -135
- data/ext/image_pack/vendor/mozjpeg/simd/mips/jsimd_dspr2.S +0 -4543
- data/ext/image_pack/vendor/mozjpeg/simd/mips/jsimd_dspr2_asm.h +0 -292
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jcsample.h +0 -28
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jsimd_mmi.h +0 -69
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/loongson-mmintrin.h +0 -1334
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jcsample.h +0 -28
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jsimd_altivec.h +0 -98
- data/ext/image_pack/vendor/mozjpeg/tjbenchtest.in +0 -256
- data/ext/image_pack/vendor/mozjpeg/tjbenchtest.java.in +0 -215
- data/ext/image_pack/vendor/mozjpeg/tjexampletest.in +0 -149
- data/ext/image_pack/vendor/mozjpeg/tjexampletest.java.in +0 -151
data/ext/image_pack/image_pack.c
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
#endif
|
|
26
26
|
#include <jpeglib.h>
|
|
27
27
|
#include <jconfigint.h>
|
|
28
|
+
#include <transupp.h>
|
|
28
29
|
|
|
29
30
|
#ifndef IMAGE_PACK_INIT_EXPORT
|
|
30
31
|
#if defined(_WIN32)
|
|
@@ -82,6 +83,21 @@
|
|
|
82
83
|
|
|
83
84
|
#define IP_ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
|
|
84
85
|
|
|
86
|
+
#define IP_MAX_MARKERS 64
|
|
87
|
+
#define IP_MAX_PROFILE_MARKERS 256
|
|
88
|
+
#define IP_MAX_MARKER_BYTES (8u * 1024 * 1024)
|
|
89
|
+
#define IP_MAX_ICC_BYTES (4u * 1024 * 1024)
|
|
90
|
+
#define IP_AUTO_444_QUALITY 90
|
|
91
|
+
|
|
92
|
+
typedef enum {
|
|
93
|
+
IP_SUBSAMP_AUTO = 0,
|
|
94
|
+
IP_SUBSAMP_420 = 1,
|
|
95
|
+
IP_SUBSAMP_422 = 2,
|
|
96
|
+
IP_SUBSAMP_444 = 3
|
|
97
|
+
} ip_subsamp_t;
|
|
98
|
+
typedef enum { IP_TUNE_HVS = 0, IP_TUNE_SSIM = 1, IP_TUNE_MS_SSIM = 2, IP_TUNE_PSNR = 3 } ip_tune_t;
|
|
99
|
+
typedef enum { IP_EFFORT_DEFAULT = 0, IP_EFFORT_FAST = 1, IP_EFFORT_MAX = 2 } ip_effort_t;
|
|
100
|
+
|
|
85
101
|
typedef enum { IP_ALGO_JPEG_TURBO = 1, IP_ALGO_MOZJPEG = 2 } ip_algo_t;
|
|
86
102
|
|
|
87
103
|
typedef enum {
|
|
@@ -136,6 +152,19 @@ typedef struct {
|
|
|
136
152
|
int selected_quality;
|
|
137
153
|
int progressive;
|
|
138
154
|
int strip_metadata;
|
|
155
|
+
int strip_icc;
|
|
156
|
+
int trim_partial_mcus;
|
|
157
|
+
int subsampling;
|
|
158
|
+
int subsampling_active;
|
|
159
|
+
int tune;
|
|
160
|
+
int effort;
|
|
161
|
+
unsigned int scale_num;
|
|
162
|
+
unsigned int scale_denom;
|
|
163
|
+
unsigned char *icc_profile;
|
|
164
|
+
unsigned int icc_len;
|
|
165
|
+
size_t preserved_marker_bytes;
|
|
166
|
+
size_t preserved_misc_marker_count;
|
|
167
|
+
size_t preserved_profile_marker_count;
|
|
139
168
|
int mozjpeg_trellis_enabled;
|
|
140
169
|
int mozjpeg_scan_opt_enabled;
|
|
141
170
|
ip_algo_t algo;
|
|
@@ -262,24 +291,38 @@ static int ip_jpeg_turbo_compress(ip_context_t *ctx);
|
|
|
262
291
|
static int ip_mozjpeg_compress(ip_context_t *ctx);
|
|
263
292
|
static int ip_lossless_optimize_jpeg(ip_context_t *ctx);
|
|
264
293
|
static int ip_run_optimize_context(ip_context_t *ctx);
|
|
294
|
+
static void configure_mozjpeg_profile_before_defaults(struct jpeg_compress_struct *cinfo,
|
|
295
|
+
int mozjpeg_size_mode);
|
|
296
|
+
static void configure_mozjpeg_features_after_defaults(struct jpeg_compress_struct *cinfo,
|
|
297
|
+
const ip_context_t *ctx,
|
|
298
|
+
int mozjpeg_size_mode, int measurement);
|
|
299
|
+
static void ip_configure_encoder(struct jpeg_compress_struct *cinfo, ip_context_t *ctx,
|
|
300
|
+
int mozjpeg_size_mode, int measurement);
|
|
301
|
+
static void ip_setup_marker_saving(struct jpeg_decompress_struct *cinfo, const ip_context_t *ctx);
|
|
302
|
+
static int ip_scan_jpeg_marker_budget(ip_context_t *ctx);
|
|
303
|
+
static int ip_capture_icc(ip_context_t *ctx, struct jpeg_decompress_struct *cinfo);
|
|
304
|
+
static void ip_write_sidecar_metadata(ip_context_t *ctx, struct jpeg_compress_struct *cinfo);
|
|
305
|
+
static int ip_open_source_jpeg(ip_context_t *ctx, struct jpeg_decompress_struct *cinfo);
|
|
306
|
+
static int ip_ingest_source_metadata(ip_context_t *ctx, struct jpeg_decompress_struct *cinfo);
|
|
265
307
|
|
|
266
308
|
typedef struct {
|
|
267
309
|
VALUE self, input, input_kind, output, output_kind, algo, quality, min_ssim;
|
|
268
310
|
VALUE mozjpeg_trellis, progressive, strip_metadata, execution, cancellable, has_scheduler;
|
|
269
|
-
VALUE report, strict, mozjpeg_scan_opt;
|
|
311
|
+
VALUE report, strict, mozjpeg_scan_opt, strip_icc, subsampling, tune, effort;
|
|
312
|
+
VALUE scale_num, scale_denom;
|
|
270
313
|
ip_context_t *ctx;
|
|
271
314
|
} ip_compress_jpeg_call_t;
|
|
272
315
|
|
|
273
316
|
typedef struct {
|
|
274
317
|
VALUE self, buffer, width, height, channels, output, output_kind, algo, quality, min_ssim;
|
|
275
318
|
VALUE mozjpeg_trellis, progressive, exact_size, execution, cancellable, has_scheduler;
|
|
276
|
-
VALUE report, strict, mozjpeg_scan_opt;
|
|
319
|
+
VALUE report, strict, mozjpeg_scan_opt, subsampling, tune, effort;
|
|
277
320
|
ip_context_t *ctx;
|
|
278
321
|
} ip_compress_pixels_call_t;
|
|
279
322
|
|
|
280
323
|
typedef struct {
|
|
281
324
|
VALUE self, input, input_kind, output, output_kind, progressive, strip_metadata;
|
|
282
|
-
VALUE execution, cancellable, has_scheduler, strict;
|
|
325
|
+
VALUE execution, cancellable, has_scheduler, strict, strip_icc, trim;
|
|
283
326
|
ip_context_t *ctx;
|
|
284
327
|
} ip_optimize_jpeg_call_t;
|
|
285
328
|
|
|
@@ -302,7 +345,7 @@ static VALUE ip_compress_pixels_entry(int argc, VALUE *argv, VALUE self);
|
|
|
302
345
|
static VALUE ip_optimize_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
|
|
303
346
|
VALUE output_kind, VALUE progressive, VALUE strip_metadata,
|
|
304
347
|
VALUE execution, VALUE cancellable, VALUE has_scheduler,
|
|
305
|
-
VALUE strict);
|
|
348
|
+
VALUE strict, VALUE strip_icc, VALUE trim);
|
|
306
349
|
|
|
307
350
|
static VALUE ip_status_to_exception(ip_status_t status) {
|
|
308
351
|
switch (status) {
|
|
@@ -448,6 +491,11 @@ static ip_context_t *ip_context_new(void) {
|
|
|
448
491
|
ctx->quality = 82;
|
|
449
492
|
ctx->mozjpeg_trellis_enabled = 1;
|
|
450
493
|
ctx->mozjpeg_scan_opt_enabled = 1;
|
|
494
|
+
ctx->scale_num = 1;
|
|
495
|
+
ctx->scale_denom = 1;
|
|
496
|
+
ctx->subsampling = IP_SUBSAMP_AUTO;
|
|
497
|
+
ctx->tune = IP_TUNE_HVS;
|
|
498
|
+
ctx->effort = IP_EFFORT_DEFAULT;
|
|
451
499
|
ctx->selected_quality = 82;
|
|
452
500
|
ctx->requested_execution = IP_EXEC_AUTO;
|
|
453
501
|
ctx->resolved_execution = IP_EXEC_AUTO;
|
|
@@ -472,6 +520,7 @@ static void ip_context_free(ip_context_t *ctx) {
|
|
|
472
520
|
free(ctx->output_path);
|
|
473
521
|
free(ctx->transient_jpeg_buf);
|
|
474
522
|
free(ctx->transient_decode_buf);
|
|
523
|
+
free(ctx->icc_profile);
|
|
475
524
|
|
|
476
525
|
if (ctx->preserved_markers) {
|
|
477
526
|
for (size_t i = 0; i < ctx->preserved_marker_count; i++) {
|
|
@@ -592,6 +641,96 @@ static struct jpeg_error_mgr *ip_use_error(ip_jpeg_error_mgr *jerr, ip_context_t
|
|
|
592
641
|
return base;
|
|
593
642
|
}
|
|
594
643
|
|
|
644
|
+
static void ip_jpeg_longjmp_error(j_common_ptr cinfo, ip_status_t status) {
|
|
645
|
+
ip_jpeg_error_mgr *err = (ip_jpeg_error_mgr *)cinfo->err;
|
|
646
|
+
char buffer[JMSG_LENGTH_MAX];
|
|
647
|
+
(*cinfo->err->format_message)(cinfo, buffer);
|
|
648
|
+
ip_context_set_error(err->ctx, status, buffer);
|
|
649
|
+
if (err->ctx->jmp_armed)
|
|
650
|
+
longjmp(err->ctx->jmpbuf, 1);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
static void ip_jpeg_invalid_error_exit(j_common_ptr cinfo) {
|
|
654
|
+
ip_jpeg_longjmp_error(cinfo, IP_ERR_INVALID_IMAGE);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
static void ip_jpeg_encode_error_exit(j_common_ptr cinfo) {
|
|
658
|
+
ip_jpeg_longjmp_error(cinfo, IP_ERR_ENCODE);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
static void ip_init_decompress(struct jpeg_decompress_struct *cinfo, ip_jpeg_error_mgr *jerr,
|
|
662
|
+
ip_context_t *ctx) {
|
|
663
|
+
memset(cinfo, 0, sizeof(*cinfo));
|
|
664
|
+
memset(jerr, 0, sizeof(*jerr));
|
|
665
|
+
cinfo->err = ip_use_error(jerr, ctx, ip_jpeg_invalid_error_exit);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
static void ip_init_compress(struct jpeg_compress_struct *cinfo, ip_jpeg_error_mgr *jerr,
|
|
669
|
+
ip_context_t *ctx) {
|
|
670
|
+
memset(cinfo, 0, sizeof(*cinfo));
|
|
671
|
+
memset(jerr, 0, sizeof(*jerr));
|
|
672
|
+
cinfo->err = ip_use_error(jerr, ctx, ip_jpeg_encode_error_exit);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
static void ip_open_mem_src(struct jpeg_decompress_struct *cinfo, const unsigned char *data,
|
|
676
|
+
size_t size) {
|
|
677
|
+
jpeg_create_decompress(cinfo);
|
|
678
|
+
jpeg_mem_src(cinfo, data, (unsigned long)size);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
static void ip_close_codec_pair(struct jpeg_compress_struct *dst,
|
|
682
|
+
struct jpeg_decompress_struct *src, ip_context_t *ctx) {
|
|
683
|
+
jpeg_destroy_compress(dst);
|
|
684
|
+
jpeg_destroy_decompress(src);
|
|
685
|
+
ctx->jmp_armed = 0;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
static void ip_claim_jpeg_output(ip_context_t *ctx, unsigned long jpeg_size) {
|
|
689
|
+
ctx->output_data = ctx->transient_jpeg_buf;
|
|
690
|
+
ctx->transient_jpeg_buf = NULL;
|
|
691
|
+
ctx->output_size = (size_t)jpeg_size;
|
|
692
|
+
ctx->output_owner = IP_OUTPUT_OWNER_MALLOC;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
static int ip_within_output_limit(ip_context_t *ctx, unsigned long jpeg_size) {
|
|
696
|
+
if (ctx->max_output_size > 0 && (size_t)jpeg_size > ctx->max_output_size)
|
|
697
|
+
IP_FAIL(ctx, IP_ERR_LIMIT, "output exceeds max_output_size");
|
|
698
|
+
return 1;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
static int ip_header_fits_int(ip_context_t *ctx, const struct jpeg_decompress_struct *cinfo) {
|
|
702
|
+
if (cinfo->image_width > (JDIMENSION)INT_MAX || cinfo->image_height > (JDIMENSION)INT_MAX)
|
|
703
|
+
IP_FAIL(ctx, IP_ERR_LIMIT, "JPEG dimensions exceed native int range");
|
|
704
|
+
return 1;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
static int ip_reject_cmyk(ip_context_t *ctx, const struct jpeg_decompress_struct *cinfo) {
|
|
708
|
+
if (cinfo->num_components == 4 || cinfo->jpeg_color_space == JCS_CMYK ||
|
|
709
|
+
cinfo->jpeg_color_space == JCS_YCCK)
|
|
710
|
+
IP_FAIL(ctx, IP_ERR_UNSUPPORTED, "CMYK/YCCK JPEG input is not supported in this release");
|
|
711
|
+
return 1;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
static void ip_apply_decode_scale(struct jpeg_decompress_struct *cinfo, const ip_context_t *ctx) {
|
|
715
|
+
if (ctx->scale_num > 0 && ctx->scale_denom > 0) {
|
|
716
|
+
cinfo->scale_num = ctx->scale_num;
|
|
717
|
+
cinfo->scale_denom = ctx->scale_denom;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
static void ip_luma_samp(int mode, int *h, int *v) {
|
|
722
|
+
if (mode == IP_SUBSAMP_444) {
|
|
723
|
+
*h = 1;
|
|
724
|
+
*v = 1;
|
|
725
|
+
} else if (mode == IP_SUBSAMP_422) {
|
|
726
|
+
*h = 2;
|
|
727
|
+
*v = 1;
|
|
728
|
+
} else {
|
|
729
|
+
*h = 2;
|
|
730
|
+
*v = 2;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
595
734
|
static void ip_apply_fast_decode(struct jpeg_decompress_struct *cinfo) {
|
|
596
735
|
cinfo->dct_method = IP_FAST_DCT;
|
|
597
736
|
cinfo->do_fancy_upsampling = FALSE;
|
|
@@ -607,7 +746,176 @@ static void ip_disable_mozjpeg_trellis(struct jpeg_compress_struct *cinfo) {
|
|
|
607
746
|
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_EOB_OPT, FALSE);
|
|
608
747
|
jpeg_c_set_bool_param(cinfo, JBOOLEAN_USE_SCANS_IN_TRELLIS, FALSE);
|
|
609
748
|
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_Q_OPT, FALSE);
|
|
610
|
-
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
static int ip_resolve_subsampling(const ip_context_t *ctx) {
|
|
752
|
+
if (ctx->subsampling != IP_SUBSAMP_AUTO)
|
|
753
|
+
return ctx->subsampling;
|
|
754
|
+
/* :fast / :jpeg_turbo keeps libjpeg 4:2:0 unless the caller opts in. */
|
|
755
|
+
if (ctx->algo == IP_ALGO_MOZJPEG && ctx->quality >= IP_AUTO_444_QUALITY)
|
|
756
|
+
return IP_SUBSAMP_444;
|
|
757
|
+
return IP_SUBSAMP_420;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
static void ip_apply_subsampling(struct jpeg_compress_struct *cinfo, int mode) {
|
|
761
|
+
int h;
|
|
762
|
+
int v;
|
|
763
|
+
int ci;
|
|
764
|
+
|
|
765
|
+
if (cinfo->jpeg_color_space != JCS_YCbCr || cinfo->num_components < 3)
|
|
766
|
+
return;
|
|
767
|
+
|
|
768
|
+
ip_luma_samp(mode, &h, &v);
|
|
769
|
+
cinfo->comp_info[0].h_samp_factor = h;
|
|
770
|
+
cinfo->comp_info[0].v_samp_factor = v;
|
|
771
|
+
for (ci = 1; ci < cinfo->num_components; ci++) {
|
|
772
|
+
cinfo->comp_info[ci].h_samp_factor = 1;
|
|
773
|
+
cinfo->comp_info[ci].v_samp_factor = 1;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
static void ip_set_tune_params(struct jpeg_compress_struct *cinfo, int tbl, float scale1,
|
|
778
|
+
float scale2, boolean use_weights) {
|
|
779
|
+
jpeg_c_set_int_param(cinfo, JINT_BASE_QUANT_TBL_IDX, tbl);
|
|
780
|
+
jpeg_c_set_float_param(cinfo, JFLOAT_LAMBDA_LOG_SCALE1, scale1);
|
|
781
|
+
jpeg_c_set_float_param(cinfo, JFLOAT_LAMBDA_LOG_SCALE2, scale2);
|
|
782
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_USE_LAMBDA_WEIGHT_TBL, use_weights);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
static void ip_apply_tune(struct jpeg_compress_struct *cinfo, int tune) {
|
|
786
|
+
switch (tune) {
|
|
787
|
+
case IP_TUNE_PSNR:
|
|
788
|
+
ip_set_tune_params(cinfo, 1, 9.0f, 0.0f, FALSE);
|
|
789
|
+
break;
|
|
790
|
+
case IP_TUNE_SSIM:
|
|
791
|
+
ip_set_tune_params(cinfo, 1, 11.5f, 12.75f, FALSE);
|
|
792
|
+
break;
|
|
793
|
+
case IP_TUNE_MS_SSIM:
|
|
794
|
+
ip_set_tune_params(cinfo, 3, 12.0f, 13.0f, TRUE);
|
|
795
|
+
break;
|
|
796
|
+
case IP_TUNE_HVS:
|
|
797
|
+
default:
|
|
798
|
+
ip_set_tune_params(cinfo, 3, 14.75f, 16.5f, TRUE);
|
|
799
|
+
break;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
static void ip_apply_effort(struct jpeg_compress_struct *cinfo, int effort, int mozjpeg_size_mode) {
|
|
804
|
+
if (!mozjpeg_size_mode)
|
|
805
|
+
return;
|
|
806
|
+
if (effort == IP_EFFORT_MAX) {
|
|
807
|
+
jpeg_c_set_int_param(cinfo, JINT_TRELLIS_NUM_LOOPS, 3);
|
|
808
|
+
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_Q_OPT, TRUE);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
static void ip_configure_encoder(struct jpeg_compress_struct *cinfo, ip_context_t *ctx,
|
|
813
|
+
int mozjpeg_size_mode, int measurement) {
|
|
814
|
+
configure_mozjpeg_profile_before_defaults(cinfo, mozjpeg_size_mode);
|
|
815
|
+
jpeg_set_defaults(cinfo);
|
|
816
|
+
if (mozjpeg_size_mode)
|
|
817
|
+
ip_apply_tune(cinfo, ctx->tune);
|
|
818
|
+
jpeg_set_quality(cinfo, ctx->quality, TRUE);
|
|
819
|
+
configure_mozjpeg_features_after_defaults(cinfo, ctx, mozjpeg_size_mode, measurement);
|
|
820
|
+
ip_apply_effort(cinfo, ctx->effort, mozjpeg_size_mode);
|
|
821
|
+
ip_apply_subsampling(cinfo, ctx->subsampling_active);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
static int ip_marker_is_saved(const ip_context_t *ctx, unsigned marker) {
|
|
825
|
+
if (marker == JPEG_APP0 + 1)
|
|
826
|
+
return 1;
|
|
827
|
+
if (marker == JPEG_APP0 + 2)
|
|
828
|
+
return !ctx->strip_icc;
|
|
829
|
+
if (ctx->strip_metadata)
|
|
830
|
+
return 0;
|
|
831
|
+
if (marker == JPEG_COM)
|
|
832
|
+
return 1;
|
|
833
|
+
if (marker >= JPEG_APP0 && marker <= JPEG_APP0 + 15)
|
|
834
|
+
return marker != JPEG_APP0;
|
|
835
|
+
return 0;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
static int ip_marker_is_profile_chain(unsigned marker) {
|
|
839
|
+
return marker == JPEG_APP0 + 1 || marker == JPEG_APP0 + 2;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
static int ip_scan_jpeg_marker_budget(ip_context_t *ctx) {
|
|
843
|
+
const unsigned char *p;
|
|
844
|
+
size_t n;
|
|
845
|
+
size_t i;
|
|
846
|
+
size_t misc_count = 0;
|
|
847
|
+
size_t profile_count = 0;
|
|
848
|
+
size_t bytes = 0;
|
|
849
|
+
|
|
850
|
+
if (!ctx->input_data || ctx->input_size < 2)
|
|
851
|
+
return 1;
|
|
852
|
+
|
|
853
|
+
p = ctx->input_data;
|
|
854
|
+
n = ctx->input_size;
|
|
855
|
+
if (p[0] != 0xFF || p[1] != 0xD8)
|
|
856
|
+
return 1;
|
|
857
|
+
|
|
858
|
+
i = 2;
|
|
859
|
+
while (i + 1 < n) {
|
|
860
|
+
unsigned marker;
|
|
861
|
+
unsigned len;
|
|
862
|
+
|
|
863
|
+
if (p[i] != 0xFF) {
|
|
864
|
+
i++;
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
while (i < n && p[i] == 0xFF)
|
|
868
|
+
i++;
|
|
869
|
+
if (i >= n)
|
|
870
|
+
break;
|
|
871
|
+
marker = p[i++];
|
|
872
|
+
if (marker == 0xD8)
|
|
873
|
+
continue;
|
|
874
|
+
if (marker == 0xD9 || marker == 0xDA)
|
|
875
|
+
break;
|
|
876
|
+
if (marker == 0x01 || (marker >= 0xD0 && marker <= 0xD7))
|
|
877
|
+
continue;
|
|
878
|
+
if (i + 2 > n)
|
|
879
|
+
break;
|
|
880
|
+
len = ((unsigned)p[i] << 8) | (unsigned)p[i + 1];
|
|
881
|
+
if (len < 2)
|
|
882
|
+
break;
|
|
883
|
+
if (ip_marker_is_saved(ctx, marker)) {
|
|
884
|
+
bytes += (size_t)(len - 2);
|
|
885
|
+
if (ip_marker_is_profile_chain(marker))
|
|
886
|
+
profile_count++;
|
|
887
|
+
else
|
|
888
|
+
misc_count++;
|
|
889
|
+
if (misc_count > IP_MAX_MARKERS || profile_count > IP_MAX_PROFILE_MARKERS ||
|
|
890
|
+
bytes > IP_MAX_MARKER_BYTES) {
|
|
891
|
+
ip_context_set_error(ctx, IP_ERR_LIMIT, "too many/too large JPEG metadata markers");
|
|
892
|
+
return 0;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
i += len;
|
|
896
|
+
}
|
|
897
|
+
return 1;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
static JXFORM_CODE ip_xform_for_orientation(int orientation) {
|
|
901
|
+
switch (orientation) {
|
|
902
|
+
case 2:
|
|
903
|
+
return JXFORM_FLIP_H;
|
|
904
|
+
case 3:
|
|
905
|
+
return JXFORM_ROT_180;
|
|
906
|
+
case 4:
|
|
907
|
+
return JXFORM_FLIP_V;
|
|
908
|
+
case 5:
|
|
909
|
+
return JXFORM_TRANSPOSE;
|
|
910
|
+
case 6:
|
|
911
|
+
return JXFORM_ROT_90;
|
|
912
|
+
case 7:
|
|
913
|
+
return JXFORM_TRANSVERSE;
|
|
914
|
+
case 8:
|
|
915
|
+
return JXFORM_ROT_270;
|
|
916
|
+
default:
|
|
917
|
+
return JXFORM_NONE;
|
|
918
|
+
}
|
|
611
919
|
}
|
|
612
920
|
|
|
613
921
|
static int ip_check_max_dimension_limits(ip_context_t *ctx) {
|
|
@@ -936,6 +1244,17 @@ static VALUE ip_build_report(ip_context_t *ctx, VALUE output_value) {
|
|
|
936
1244
|
|
|
937
1245
|
static int ip_save_marker(ip_context_t *ctx, int marker, const unsigned char *data,
|
|
938
1246
|
unsigned int len) {
|
|
1247
|
+
size_t next_bytes = ctx->preserved_marker_bytes + (size_t)len;
|
|
1248
|
+
int profile = ip_marker_is_profile_chain((unsigned)marker);
|
|
1249
|
+
size_t next_profile = ctx->preserved_profile_marker_count + (profile ? 1 : 0);
|
|
1250
|
+
size_t next_misc = ctx->preserved_misc_marker_count + (profile ? 0 : 1);
|
|
1251
|
+
|
|
1252
|
+
if (next_bytes > IP_MAX_MARKER_BYTES || next_profile > IP_MAX_PROFILE_MARKERS ||
|
|
1253
|
+
next_misc > IP_MAX_MARKERS) {
|
|
1254
|
+
ip_context_set_error(ctx, IP_ERR_LIMIT, "too many/too large JPEG metadata markers");
|
|
1255
|
+
return 0;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
939
1258
|
if (ctx->preserved_marker_count == ctx->preserved_marker_capacity) {
|
|
940
1259
|
if (ctx->preserved_marker_capacity > SIZE_MAX / 2)
|
|
941
1260
|
return 0;
|
|
@@ -960,6 +1279,11 @@ static int ip_save_marker(ip_context_t *ctx, int marker, const unsigned char *da
|
|
|
960
1279
|
ctx->preserved_markers[ctx->preserved_marker_count].data = copy;
|
|
961
1280
|
ctx->preserved_markers[ctx->preserved_marker_count].len = len;
|
|
962
1281
|
ctx->preserved_marker_count++;
|
|
1282
|
+
ctx->preserved_marker_bytes += (size_t)len;
|
|
1283
|
+
if (profile)
|
|
1284
|
+
ctx->preserved_profile_marker_count++;
|
|
1285
|
+
else
|
|
1286
|
+
ctx->preserved_misc_marker_count++;
|
|
963
1287
|
return 1;
|
|
964
1288
|
}
|
|
965
1289
|
|
|
@@ -971,7 +1295,8 @@ static int ip_save_markers_from_decompress(ip_context_t *ctx,
|
|
|
971
1295
|
continue;
|
|
972
1296
|
|
|
973
1297
|
if (!ip_save_marker(ctx, m->marker, m->data, m->data_length)) {
|
|
974
|
-
|
|
1298
|
+
if (ctx->status == IP_OK)
|
|
1299
|
+
ip_context_set_error(ctx, IP_ERR_OOM, "failed to preserve JPEG metadata marker");
|
|
975
1300
|
return 0;
|
|
976
1301
|
}
|
|
977
1302
|
}
|
|
@@ -1138,22 +1463,57 @@ static void ip_write_preserved_markers(ip_context_t *ctx, struct jpeg_compress_s
|
|
|
1138
1463
|
}
|
|
1139
1464
|
}
|
|
1140
1465
|
|
|
1141
|
-
static
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1466
|
+
static int ip_capture_icc(ip_context_t *ctx, struct jpeg_decompress_struct *cinfo) {
|
|
1467
|
+
JOCTET *icc = NULL;
|
|
1468
|
+
unsigned int len = 0;
|
|
1469
|
+
|
|
1470
|
+
if (ctx->strip_icc)
|
|
1471
|
+
return 1;
|
|
1472
|
+
if (!jpeg_read_icc_profile(cinfo, &icc, &len) || !icc || len == 0)
|
|
1473
|
+
return 1;
|
|
1474
|
+
if (len > IP_MAX_ICC_BYTES) {
|
|
1475
|
+
free(icc);
|
|
1476
|
+
return 1;
|
|
1477
|
+
}
|
|
1478
|
+
free(ctx->icc_profile);
|
|
1479
|
+
ctx->icc_profile = icc;
|
|
1480
|
+
ctx->icc_len = len;
|
|
1481
|
+
return 1;
|
|
1148
1482
|
}
|
|
1149
1483
|
|
|
1150
|
-
static void
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
if (
|
|
1156
|
-
|
|
1484
|
+
static void ip_write_sidecar_metadata(ip_context_t *ctx, struct jpeg_compress_struct *cinfo) {
|
|
1485
|
+
if (!ctx->strip_metadata) {
|
|
1486
|
+
ip_write_preserved_markers(ctx, cinfo);
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
if (ctx->icc_profile && ctx->icc_len > 0)
|
|
1490
|
+
jpeg_write_icc_profile(cinfo, ctx->icc_profile, ctx->icc_len);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
static int ip_prepare_source_markers(ip_context_t *ctx, struct jpeg_decompress_struct *cinfo) {
|
|
1494
|
+
if (!ip_scan_jpeg_marker_budget(ctx))
|
|
1495
|
+
return 0;
|
|
1496
|
+
ip_setup_marker_saving(cinfo, ctx);
|
|
1497
|
+
return 1;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
static int ip_read_source_header(ip_context_t *ctx, struct jpeg_decompress_struct *cinfo) {
|
|
1501
|
+
if (jpeg_read_header(cinfo, TRUE) != JPEG_HEADER_OK)
|
|
1502
|
+
IP_FAIL(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1503
|
+
ctx->source_orientation = ip_read_exif_orientation_from_decompress(cinfo);
|
|
1504
|
+
return 1;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
static int ip_open_source_jpeg(ip_context_t *ctx, struct jpeg_decompress_struct *cinfo) {
|
|
1508
|
+
return ip_prepare_source_markers(ctx, cinfo) && ip_read_source_header(ctx, cinfo);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
static int ip_ingest_source_metadata(ip_context_t *ctx, struct jpeg_decompress_struct *cinfo) {
|
|
1512
|
+
if (!ip_capture_icc(ctx, cinfo))
|
|
1513
|
+
return 0;
|
|
1514
|
+
if (!ctx->strip_metadata && !ip_save_markers_from_decompress(ctx, cinfo))
|
|
1515
|
+
return 0;
|
|
1516
|
+
return 1;
|
|
1157
1517
|
}
|
|
1158
1518
|
|
|
1159
1519
|
static const char *ip_jpeg_color_space_name(int color_space) {
|
|
@@ -1180,16 +1540,13 @@ static int ip_inspect_jpeg_header(ip_context_t *ctx, int allow_cmyk_ycck) {
|
|
|
1180
1540
|
|
|
1181
1541
|
struct jpeg_decompress_struct cinfo;
|
|
1182
1542
|
ip_jpeg_error_mgr jerr;
|
|
1183
|
-
|
|
1184
|
-
memset(&jerr, 0, sizeof(jerr));
|
|
1185
|
-
cinfo.err = ip_use_error(&jerr, ctx, ip_jpeg_invalid_error_exit);
|
|
1543
|
+
ip_init_decompress(&cinfo, &jerr, ctx);
|
|
1186
1544
|
|
|
1187
1545
|
ctx->jmp_armed = 1;
|
|
1188
1546
|
if (setjmp(ctx->jmpbuf))
|
|
1189
1547
|
goto fail;
|
|
1190
1548
|
|
|
1191
|
-
|
|
1192
|
-
jpeg_mem_src(&cinfo, ctx->input_data, (unsigned long)ctx->input_size);
|
|
1549
|
+
ip_open_mem_src(&cinfo, ctx->input_data, ctx->input_size);
|
|
1193
1550
|
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
|
|
1194
1551
|
IP_FAIL_GOTO(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1195
1552
|
|
|
@@ -1275,20 +1632,18 @@ static void configure_mozjpeg_profile_before_defaults(struct jpeg_compress_struc
|
|
|
1275
1632
|
}
|
|
1276
1633
|
|
|
1277
1634
|
static void configure_mozjpeg_features_after_defaults(struct jpeg_compress_struct *cinfo,
|
|
1278
|
-
|
|
1279
|
-
int
|
|
1280
|
-
int mozjpeg_trellis_enabled,
|
|
1281
|
-
int scan_opt_enabled, int measurement) {
|
|
1635
|
+
const ip_context_t *ctx,
|
|
1636
|
+
int mozjpeg_size_mode, int measurement) {
|
|
1282
1637
|
if (mozjpeg_size_mode) {
|
|
1283
1638
|
cinfo->optimize_coding = TRUE;
|
|
1284
1639
|
|
|
1285
|
-
if (
|
|
1286
|
-
if (mozjpeg_trellis_enabled) {
|
|
1640
|
+
if (ctx->progressive) {
|
|
1641
|
+
if (ctx->mozjpeg_trellis_enabled) {
|
|
1287
1642
|
jpeg_c_set_bool_param(cinfo, JBOOLEAN_USE_SCANS_IN_TRELLIS, TRUE);
|
|
1288
1643
|
jpeg_c_set_bool_param(cinfo, JBOOLEAN_TRELLIS_EOB_OPT, TRUE);
|
|
1289
1644
|
}
|
|
1290
1645
|
jpeg_c_set_int_param(cinfo, JINT_DC_SCAN_OPT_MODE, 2);
|
|
1291
|
-
int run_scan_search = (measurement || !
|
|
1646
|
+
int run_scan_search = (measurement || !ctx->mozjpeg_scan_opt_enabled) ? FALSE : TRUE;
|
|
1292
1647
|
jpeg_c_set_bool_param(cinfo, JBOOLEAN_OPTIMIZE_SCANS, run_scan_search);
|
|
1293
1648
|
jpeg_simple_progression(cinfo);
|
|
1294
1649
|
jpeg_c_set_bool_param(cinfo, JBOOLEAN_OPTIMIZE_SCANS, run_scan_search);
|
|
@@ -1298,7 +1653,7 @@ static void configure_mozjpeg_features_after_defaults(struct jpeg_compress_struc
|
|
|
1298
1653
|
jpeg_c_set_bool_param(cinfo, JBOOLEAN_OPTIMIZE_SCANS, FALSE);
|
|
1299
1654
|
}
|
|
1300
1655
|
|
|
1301
|
-
if (!mozjpeg_trellis_enabled)
|
|
1656
|
+
if (!ctx->mozjpeg_trellis_enabled)
|
|
1302
1657
|
ip_disable_mozjpeg_trellis(cinfo);
|
|
1303
1658
|
|
|
1304
1659
|
return;
|
|
@@ -1310,7 +1665,7 @@ static void configure_mozjpeg_features_after_defaults(struct jpeg_compress_struc
|
|
|
1310
1665
|
jpeg_c_set_bool_param(cinfo, JBOOLEAN_OPTIMIZE_SCANS, FALSE);
|
|
1311
1666
|
ip_disable_mozjpeg_trellis(cinfo);
|
|
1312
1667
|
|
|
1313
|
-
if (
|
|
1668
|
+
if (ctx->progressive) {
|
|
1314
1669
|
jpeg_simple_progression(cinfo);
|
|
1315
1670
|
cinfo->optimize_coding = TRUE;
|
|
1316
1671
|
}
|
|
@@ -1320,9 +1675,7 @@ static int encode_pixels_with_libjpeg(ip_context_t *ctx, int mozjpeg_size_mode,
|
|
|
1320
1675
|
struct jpeg_compress_struct cinfo;
|
|
1321
1676
|
ip_jpeg_error_mgr jerr;
|
|
1322
1677
|
unsigned long jpeg_size = 0;
|
|
1323
|
-
|
|
1324
|
-
memset(&jerr, 0, sizeof(jerr));
|
|
1325
|
-
cinfo.err = ip_use_error(&jerr, ctx, ip_jpeg_encode_error_exit);
|
|
1678
|
+
ip_init_compress(&cinfo, &jerr, ctx);
|
|
1326
1679
|
ctx->transient_jpeg_buf = NULL;
|
|
1327
1680
|
|
|
1328
1681
|
ctx->jmp_armed = 1;
|
|
@@ -1345,17 +1698,10 @@ static int encode_pixels_with_libjpeg(ip_context_t *ctx, int mozjpeg_size_mode,
|
|
|
1345
1698
|
ctx->channels == 4 ? JCS_EXT_RGBA : color_space_for_channels(ctx->channels);
|
|
1346
1699
|
}
|
|
1347
1700
|
|
|
1348
|
-
|
|
1349
|
-
jpeg_set_defaults(&cinfo);
|
|
1350
|
-
jpeg_set_quality(&cinfo, ctx->quality, TRUE);
|
|
1351
|
-
configure_mozjpeg_features_after_defaults(&cinfo, mozjpeg_size_mode, ctx->progressive,
|
|
1352
|
-
ctx->mozjpeg_trellis_enabled,
|
|
1353
|
-
ctx->mozjpeg_scan_opt_enabled, measurement);
|
|
1701
|
+
ip_configure_encoder(&cinfo, ctx, mozjpeg_size_mode, measurement);
|
|
1354
1702
|
|
|
1355
1703
|
jpeg_start_compress(&cinfo, TRUE);
|
|
1356
|
-
|
|
1357
|
-
if (!ctx->strip_metadata)
|
|
1358
|
-
ip_write_preserved_markers(ctx, &cinfo);
|
|
1704
|
+
ip_write_sidecar_metadata(ctx, &cinfo);
|
|
1359
1705
|
|
|
1360
1706
|
while (cinfo.next_scanline < cinfo.image_height) {
|
|
1361
1707
|
if (atomic_load(&ctx->cancelled))
|
|
@@ -1376,16 +1722,12 @@ static int encode_pixels_with_libjpeg(ip_context_t *ctx, int mozjpeg_size_mode,
|
|
|
1376
1722
|
|
|
1377
1723
|
jpeg_finish_compress(&cinfo);
|
|
1378
1724
|
|
|
1379
|
-
if (!measurement && ctx
|
|
1380
|
-
|
|
1725
|
+
if (!measurement && !ip_within_output_limit(ctx, jpeg_size))
|
|
1726
|
+
goto fail;
|
|
1381
1727
|
|
|
1382
1728
|
jpeg_destroy_compress(&cinfo);
|
|
1383
1729
|
ctx->jmp_armed = 0;
|
|
1384
|
-
|
|
1385
|
-
ctx->output_data = ctx->transient_jpeg_buf;
|
|
1386
|
-
ctx->transient_jpeg_buf = NULL;
|
|
1387
|
-
ctx->output_size = (size_t)jpeg_size;
|
|
1388
|
-
ctx->output_owner = IP_OUTPUT_OWNER_MALLOC;
|
|
1730
|
+
ip_claim_jpeg_output(ctx, jpeg_size);
|
|
1389
1731
|
return 1;
|
|
1390
1732
|
|
|
1391
1733
|
fail:
|
|
@@ -1401,9 +1743,7 @@ static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, i
|
|
|
1401
1743
|
int allow_ycbcr_transcode) {
|
|
1402
1744
|
struct jpeg_decompress_struct cinfo;
|
|
1403
1745
|
ip_jpeg_error_mgr jerr;
|
|
1404
|
-
|
|
1405
|
-
memset(&jerr, 0, sizeof(jerr));
|
|
1406
|
-
cinfo.err = ip_use_error(&jerr, ctx, ip_jpeg_invalid_error_exit);
|
|
1746
|
+
ip_init_decompress(&cinfo, &jerr, ctx);
|
|
1407
1747
|
ctx->transient_decode_buf = NULL;
|
|
1408
1748
|
ctx->source_orientation = 1;
|
|
1409
1749
|
ctx->decoded_as_ycbcr = 0;
|
|
@@ -1415,28 +1755,11 @@ static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, i
|
|
|
1415
1755
|
goto fail;
|
|
1416
1756
|
}
|
|
1417
1757
|
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
|
|
1424
|
-
for (int app = 2; app < 16; app++)
|
|
1425
|
-
jpeg_save_markers(&cinfo, JPEG_APP0 + app, 0xFFFF);
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
|
|
1429
|
-
IP_FAIL_GOTO(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1430
|
-
|
|
1431
|
-
ctx->source_orientation = ip_read_exif_orientation_from_decompress(&cinfo);
|
|
1432
|
-
|
|
1433
|
-
if (cinfo.image_width > (JDIMENSION)INT_MAX || cinfo.image_height > (JDIMENSION)INT_MAX)
|
|
1434
|
-
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "JPEG dimensions exceed native int range");
|
|
1435
|
-
|
|
1436
|
-
if (cinfo.num_components == 4 || cinfo.jpeg_color_space == JCS_CMYK ||
|
|
1437
|
-
cinfo.jpeg_color_space == JCS_YCCK)
|
|
1438
|
-
IP_FAIL_GOTO(ctx, IP_ERR_UNSUPPORTED,
|
|
1439
|
-
"CMYK/YCCK JPEG input is not supported in this release");
|
|
1758
|
+
ip_open_mem_src(&cinfo, ctx->input_data, ctx->input_size);
|
|
1759
|
+
if (!ip_open_source_jpeg(ctx, &cinfo))
|
|
1760
|
+
goto fail;
|
|
1761
|
+
if (!ip_header_fits_int(ctx, &cinfo) || !ip_reject_cmyk(ctx, &cinfo))
|
|
1762
|
+
goto fail;
|
|
1440
1763
|
|
|
1441
1764
|
int ch = cinfo.num_components == 1 ? 1 : 3;
|
|
1442
1765
|
|
|
@@ -1453,12 +1776,21 @@ static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, i
|
|
|
1453
1776
|
int use_ycbcr = allow_ycbcr_transcode && ch == 3;
|
|
1454
1777
|
cinfo.out_color_space = ch == 1 ? JCS_GRAYSCALE : (use_ycbcr ? JCS_YCbCr : JCS_RGB);
|
|
1455
1778
|
ctx->decoded_as_ycbcr = use_ycbcr;
|
|
1779
|
+
ip_apply_decode_scale(&cinfo, ctx);
|
|
1456
1780
|
if (fast_decode_mode)
|
|
1457
1781
|
ip_apply_fast_decode(&cinfo);
|
|
1458
1782
|
|
|
1459
1783
|
jpeg_start_decompress(&cinfo);
|
|
1460
1784
|
|
|
1461
|
-
|
|
1785
|
+
ctx->width = (int)cinfo.output_width;
|
|
1786
|
+
ctx->height = (int)cinfo.output_height;
|
|
1787
|
+
if (!ip_checked_image_size(ctx->width, ctx->height, ctx->channels, &ctx->decoded_bytes))
|
|
1788
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "decoded image size overflows native size");
|
|
1789
|
+
validate_limits_for_pixels(ctx);
|
|
1790
|
+
if (ctx->status != IP_OK)
|
|
1791
|
+
goto fail;
|
|
1792
|
+
|
|
1793
|
+
if (!ip_ingest_source_metadata(ctx, &cinfo))
|
|
1462
1794
|
goto fail;
|
|
1463
1795
|
|
|
1464
1796
|
size_t row_stride = 0;
|
|
@@ -1528,11 +1860,166 @@ fail:
|
|
|
1528
1860
|
return 0;
|
|
1529
1861
|
}
|
|
1530
1862
|
|
|
1863
|
+
static void ip_free_raw_planes(JSAMPARRAY planes[3], unsigned char *bufs[3]) {
|
|
1864
|
+
int ci;
|
|
1865
|
+
for (ci = 0; ci < 3; ci++) {
|
|
1866
|
+
free(planes[ci]);
|
|
1867
|
+
planes[ci] = NULL;
|
|
1868
|
+
free(bufs[ci]);
|
|
1869
|
+
bufs[ci] = NULL;
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
static int ip_alloc_raw_planes(ip_context_t *ctx, struct jpeg_decompress_struct *src,
|
|
1874
|
+
JSAMPARRAY planes[3], unsigned char *bufs[3], int *rows_out) {
|
|
1875
|
+
int ci;
|
|
1876
|
+
int rows = (int)src->max_v_samp_factor * DCTSIZE;
|
|
1877
|
+
|
|
1878
|
+
memset(planes, 0, sizeof(JSAMPARRAY) * 3);
|
|
1879
|
+
memset(bufs, 0, sizeof(unsigned char *) * 3);
|
|
1880
|
+
*rows_out = rows;
|
|
1881
|
+
|
|
1882
|
+
for (ci = 0; ci < src->num_components && ci < 3; ci++) {
|
|
1883
|
+
int width = (int)src->comp_info[ci].width_in_blocks * DCTSIZE;
|
|
1884
|
+
size_t plane_size = (size_t)rows * (size_t)width;
|
|
1885
|
+
int r;
|
|
1886
|
+
|
|
1887
|
+
planes[ci] = (JSAMPARRAY)malloc((size_t)rows * sizeof(JSAMPROW));
|
|
1888
|
+
bufs[ci] = (unsigned char *)malloc(plane_size);
|
|
1889
|
+
if ((!planes[ci] || !bufs[ci]) && (rows > 0) && (width > 0)) {
|
|
1890
|
+
ip_context_set_error(ctx, IP_ERR_OOM, "failed to allocate raw JPEG plane buffer");
|
|
1891
|
+
ip_free_raw_planes(planes, bufs);
|
|
1892
|
+
return 0;
|
|
1893
|
+
}
|
|
1894
|
+
for (r = 0; r < rows; r++)
|
|
1895
|
+
planes[ci][r] = bufs[ci] + ((size_t)r * (size_t)width);
|
|
1896
|
+
}
|
|
1897
|
+
return 1;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
/* Returns 1 on success, 0 on fallback (status still IP_OK) or error. */
|
|
1901
|
+
static int encode_jpeg_raw_transcode(ip_context_t *ctx, int mozjpeg_size_mode) {
|
|
1902
|
+
struct jpeg_decompress_struct srcinfo;
|
|
1903
|
+
struct jpeg_compress_struct dstinfo;
|
|
1904
|
+
ip_jpeg_error_mgr srcerr;
|
|
1905
|
+
ip_jpeg_error_mgr dsterr;
|
|
1906
|
+
JSAMPARRAY planes[3];
|
|
1907
|
+
unsigned char *bufs[3];
|
|
1908
|
+
int rows = 0;
|
|
1909
|
+
unsigned long jpeg_size = 0;
|
|
1910
|
+
int want_h = 2;
|
|
1911
|
+
int want_v = 2;
|
|
1912
|
+
|
|
1913
|
+
ip_init_decompress(&srcinfo, &srcerr, ctx);
|
|
1914
|
+
ip_init_compress(&dstinfo, &dsterr, ctx);
|
|
1915
|
+
memset(planes, 0, sizeof(planes));
|
|
1916
|
+
memset(bufs, 0, sizeof(bufs));
|
|
1917
|
+
ctx->transient_jpeg_buf = NULL;
|
|
1918
|
+
|
|
1919
|
+
ctx->jmp_armed = 1;
|
|
1920
|
+
if (setjmp(ctx->jmpbuf)) {
|
|
1921
|
+
if (ctx->status == IP_OK)
|
|
1922
|
+
ip_context_set_error(ctx, IP_ERR_ENCODE, "raw JPEG transcode failed");
|
|
1923
|
+
goto fail;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
ip_open_mem_src(&srcinfo, ctx->input_data, ctx->input_size);
|
|
1927
|
+
if (!ip_open_source_jpeg(ctx, &srcinfo))
|
|
1928
|
+
goto fail;
|
|
1929
|
+
if (ctx->strip_metadata && ctx->source_orientation > 1)
|
|
1930
|
+
goto fail;
|
|
1931
|
+
|
|
1932
|
+
if (srcinfo.jpeg_color_space != JCS_YCbCr || srcinfo.num_components != 3)
|
|
1933
|
+
goto fail;
|
|
1934
|
+
if (ctx->scale_denom > 0 && !(ctx->scale_num == 1 && ctx->scale_denom == 1))
|
|
1935
|
+
goto fail;
|
|
1936
|
+
|
|
1937
|
+
ctx->subsampling_active = ip_resolve_subsampling(ctx);
|
|
1938
|
+
ip_luma_samp(ctx->subsampling_active, &want_h, &want_v);
|
|
1939
|
+
if (srcinfo.comp_info[0].h_samp_factor != want_h ||
|
|
1940
|
+
srcinfo.comp_info[0].v_samp_factor != want_v || srcinfo.comp_info[1].h_samp_factor != 1 ||
|
|
1941
|
+
srcinfo.comp_info[1].v_samp_factor != 1 || srcinfo.comp_info[2].h_samp_factor != 1 ||
|
|
1942
|
+
srcinfo.comp_info[2].v_samp_factor != 1)
|
|
1943
|
+
goto fail;
|
|
1944
|
+
|
|
1945
|
+
ip_apply_decode_scale(&srcinfo, ctx);
|
|
1946
|
+
srcinfo.raw_data_out = TRUE;
|
|
1947
|
+
srcinfo.out_color_space = JCS_YCbCr;
|
|
1948
|
+
jpeg_start_decompress(&srcinfo);
|
|
1949
|
+
|
|
1950
|
+
ctx->width = (int)srcinfo.output_width;
|
|
1951
|
+
ctx->height = (int)srcinfo.output_height;
|
|
1952
|
+
ctx->channels = 3;
|
|
1953
|
+
if (!ip_checked_image_size(ctx->width, ctx->height, ctx->channels, &ctx->decoded_bytes))
|
|
1954
|
+
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "decoded image size overflows native size");
|
|
1955
|
+
validate_limits_for_pixels(ctx);
|
|
1956
|
+
if (ctx->status != IP_OK)
|
|
1957
|
+
goto fail;
|
|
1958
|
+
|
|
1959
|
+
if (!ip_ingest_source_metadata(ctx, &srcinfo))
|
|
1960
|
+
goto fail;
|
|
1961
|
+
|
|
1962
|
+
if (!ip_alloc_raw_planes(ctx, &srcinfo, planes, bufs, &rows))
|
|
1963
|
+
goto fail;
|
|
1964
|
+
|
|
1965
|
+
jpeg_create_compress(&dstinfo);
|
|
1966
|
+
jpeg_mem_dest(&dstinfo, &ctx->transient_jpeg_buf, &jpeg_size);
|
|
1967
|
+
dstinfo.image_width = srcinfo.output_width;
|
|
1968
|
+
dstinfo.image_height = srcinfo.output_height;
|
|
1969
|
+
dstinfo.input_components = 3;
|
|
1970
|
+
dstinfo.in_color_space = JCS_YCbCr;
|
|
1971
|
+
ip_configure_encoder(&dstinfo, ctx, mozjpeg_size_mode, 0);
|
|
1972
|
+
dstinfo.raw_data_in = TRUE;
|
|
1973
|
+
|
|
1974
|
+
jpeg_start_compress(&dstinfo, TRUE);
|
|
1975
|
+
ip_write_sidecar_metadata(ctx, &dstinfo);
|
|
1976
|
+
|
|
1977
|
+
while (srcinfo.output_scanline < srcinfo.output_height) {
|
|
1978
|
+
JDIMENSION got;
|
|
1979
|
+
|
|
1980
|
+
if (atomic_load(&ctx->cancelled))
|
|
1981
|
+
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "JPEG transcode cancelled");
|
|
1982
|
+
|
|
1983
|
+
got = jpeg_read_raw_data(&srcinfo, planes, (JDIMENSION)rows);
|
|
1984
|
+
if (got == 0)
|
|
1985
|
+
IP_FAIL_GOTO(ctx, IP_ERR_INVALID_IMAGE, "failed to read raw JPEG data");
|
|
1986
|
+
if (jpeg_write_raw_data(&dstinfo, planes, (JDIMENSION)rows) == 0)
|
|
1987
|
+
IP_FAIL_GOTO(ctx, IP_ERR_ENCODE, "failed to write raw JPEG data");
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
jpeg_finish_compress(&dstinfo);
|
|
1991
|
+
jpeg_finish_decompress(&srcinfo);
|
|
1992
|
+
|
|
1993
|
+
if (!ip_within_output_limit(ctx, jpeg_size))
|
|
1994
|
+
goto fail;
|
|
1995
|
+
|
|
1996
|
+
ip_free_raw_planes(planes, bufs);
|
|
1997
|
+
ip_close_codec_pair(&dstinfo, &srcinfo, ctx);
|
|
1998
|
+
ip_claim_jpeg_output(ctx, jpeg_size);
|
|
1999
|
+
return 1;
|
|
2000
|
+
|
|
2001
|
+
fail:
|
|
2002
|
+
ip_close_codec_pair(&dstinfo, &srcinfo, ctx);
|
|
2003
|
+
ip_free_raw_planes(planes, bufs);
|
|
2004
|
+
free(ctx->transient_jpeg_buf);
|
|
2005
|
+
ctx->transient_jpeg_buf = NULL;
|
|
2006
|
+
return 0;
|
|
2007
|
+
}
|
|
2008
|
+
|
|
1531
2009
|
static int compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_size_mode) {
|
|
1532
2010
|
if (ctx->pixel_data) {
|
|
2011
|
+
ctx->subsampling_active = ip_resolve_subsampling(ctx);
|
|
1533
2012
|
return encode_pixels_with_libjpeg(ctx, mozjpeg_size_mode, 0);
|
|
1534
2013
|
}
|
|
1535
2014
|
|
|
2015
|
+
if (!ctx->ssim_guard_enabled &&
|
|
2016
|
+
(ctx->scale_denom == 0 || (ctx->scale_num == 1 && ctx->scale_denom == 1))) {
|
|
2017
|
+
if (encode_jpeg_raw_transcode(ctx, mozjpeg_size_mode))
|
|
2018
|
+
return 1;
|
|
2019
|
+
if (ctx->status != IP_OK)
|
|
2020
|
+
return 0;
|
|
2021
|
+
}
|
|
2022
|
+
|
|
1536
2023
|
unsigned char *pixels = NULL;
|
|
1537
2024
|
int width = 0;
|
|
1538
2025
|
int height = 0;
|
|
@@ -1547,6 +2034,7 @@ static int compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_size_mod
|
|
|
1547
2034
|
ctx->height = height;
|
|
1548
2035
|
ctx->channels = channels;
|
|
1549
2036
|
ctx->decoded_bytes = ctx->pixel_size;
|
|
2037
|
+
ctx->subsampling_active = ip_resolve_subsampling(ctx);
|
|
1550
2038
|
|
|
1551
2039
|
return encode_pixels_with_libjpeg(ctx, mozjpeg_size_mode, 0);
|
|
1552
2040
|
}
|
|
@@ -1567,9 +2055,7 @@ static int ip_decode_jpeg_to_luma_buffer(ip_context_t *ctx, const unsigned char
|
|
|
1567
2055
|
unsigned char **luma, int *width, int *height) {
|
|
1568
2056
|
struct jpeg_decompress_struct cinfo;
|
|
1569
2057
|
ip_jpeg_error_mgr jerr;
|
|
1570
|
-
|
|
1571
|
-
memset(&jerr, 0, sizeof(jerr));
|
|
1572
|
-
cinfo.err = ip_use_error(&jerr, ctx, ip_jpeg_invalid_error_exit);
|
|
2058
|
+
ip_init_decompress(&cinfo, &jerr, ctx);
|
|
1573
2059
|
ctx->transient_decode_buf = NULL;
|
|
1574
2060
|
|
|
1575
2061
|
ctx->jmp_armed = 1;
|
|
@@ -1579,19 +2065,12 @@ static int ip_decode_jpeg_to_luma_buffer(ip_context_t *ctx, const unsigned char
|
|
|
1579
2065
|
goto fail;
|
|
1580
2066
|
}
|
|
1581
2067
|
|
|
1582
|
-
|
|
1583
|
-
jpeg_mem_src(&cinfo, data, (unsigned long)size);
|
|
2068
|
+
ip_open_mem_src(&cinfo, data, size);
|
|
1584
2069
|
|
|
1585
2070
|
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
|
|
1586
2071
|
IP_FAIL_GOTO(ctx, IP_ERR_INVALID_IMAGE, "invalid JPEG header");
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
IP_FAIL_GOTO(ctx, IP_ERR_LIMIT, "JPEG dimensions exceed native int range");
|
|
1590
|
-
|
|
1591
|
-
if (cinfo.num_components == 4 || cinfo.jpeg_color_space == JCS_CMYK ||
|
|
1592
|
-
cinfo.jpeg_color_space == JCS_YCCK)
|
|
1593
|
-
IP_FAIL_GOTO(ctx, IP_ERR_UNSUPPORTED,
|
|
1594
|
-
"CMYK/YCCK JPEG input is not supported in this release");
|
|
2072
|
+
if (!ip_header_fits_int(ctx, &cinfo) || !ip_reject_cmyk(ctx, &cinfo))
|
|
2073
|
+
goto fail;
|
|
1595
2074
|
|
|
1596
2075
|
int old_width = ctx->width;
|
|
1597
2076
|
int old_height = ctx->height;
|
|
@@ -1918,6 +2397,9 @@ static int guarded_compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_
|
|
|
1918
2397
|
if (!reference_luma)
|
|
1919
2398
|
return 0;
|
|
1920
2399
|
|
|
2400
|
+
ctx->subsampling_active = ip_resolve_subsampling(ctx);
|
|
2401
|
+
|
|
2402
|
+
int search_start_quality = ctx->quality;
|
|
1921
2403
|
int search_low = ctx->quality;
|
|
1922
2404
|
int search_high = 100;
|
|
1923
2405
|
int best_quality = -1;
|
|
@@ -1931,6 +2413,7 @@ static int guarded_compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_
|
|
|
1931
2413
|
int q_lo = 0, q_hi = 0;
|
|
1932
2414
|
double s_lo = 0.0, s_hi = 0.0;
|
|
1933
2415
|
|
|
2416
|
+
ssim_search:
|
|
1934
2417
|
while (search_low <= search_high) {
|
|
1935
2418
|
if (atomic_load(&ctx->cancelled)) {
|
|
1936
2419
|
free(reference_luma);
|
|
@@ -1990,6 +2473,20 @@ static int guarded_compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_
|
|
|
1990
2473
|
}
|
|
1991
2474
|
|
|
1992
2475
|
if (best_quality < 0) {
|
|
2476
|
+
if (ctx->subsampling == IP_SUBSAMP_AUTO && ctx->subsampling_active == IP_SUBSAMP_420) {
|
|
2477
|
+
ctx->subsampling_active = IP_SUBSAMP_444;
|
|
2478
|
+
ctx->quality = search_start_quality;
|
|
2479
|
+
search_low = search_start_quality;
|
|
2480
|
+
search_high = 100;
|
|
2481
|
+
best_quality = -1;
|
|
2482
|
+
best_ssim = 0.0;
|
|
2483
|
+
free(best_jpeg);
|
|
2484
|
+
best_jpeg = NULL;
|
|
2485
|
+
best_jpeg_size = 0;
|
|
2486
|
+
have_lo = 0;
|
|
2487
|
+
have_hi = 0;
|
|
2488
|
+
goto ssim_search;
|
|
2489
|
+
}
|
|
1993
2490
|
char message[512];
|
|
1994
2491
|
snprintf(message, sizeof(message),
|
|
1995
2492
|
"cannot satisfy min_ssim=%.6f; best observed SSIM %.6f at quality=%d",
|
|
@@ -2072,11 +2569,15 @@ static int guarded_compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_
|
|
|
2072
2569
|
return 1;
|
|
2073
2570
|
}
|
|
2074
2571
|
|
|
2075
|
-
static void ip_setup_marker_saving(struct jpeg_decompress_struct *cinfo,
|
|
2572
|
+
static void ip_setup_marker_saving(struct jpeg_decompress_struct *cinfo, const ip_context_t *ctx) {
|
|
2076
2573
|
jpeg_save_markers(cinfo, JPEG_APP0 + 1, 0xFFFF);
|
|
2077
|
-
if (!
|
|
2574
|
+
if (!ctx->strip_icc)
|
|
2575
|
+
jpeg_save_markers(cinfo, JPEG_APP0 + 2, 0xFFFF);
|
|
2576
|
+
if (!ctx->strip_metadata) {
|
|
2078
2577
|
jpeg_save_markers(cinfo, JPEG_COM, 0xFFFF);
|
|
2079
|
-
for (int app =
|
|
2578
|
+
for (int app = 0; app < 16; app++) {
|
|
2579
|
+
if (app == 1 || app == 2)
|
|
2580
|
+
continue;
|
|
2080
2581
|
jpeg_save_markers(cinfo, JPEG_APP0 + app, 0xFFFF);
|
|
2081
2582
|
}
|
|
2082
2583
|
}
|
|
@@ -2084,10 +2585,8 @@ static void ip_setup_marker_saving(struct jpeg_decompress_struct *cinfo, int str
|
|
|
2084
2585
|
|
|
2085
2586
|
static int ip_validate_lossless_optimize_header(ip_context_t *ctx,
|
|
2086
2587
|
struct jpeg_decompress_struct *srcinfo) {
|
|
2087
|
-
if (
|
|
2088
|
-
ip_context_set_error(ctx, IP_ERR_LIMIT, "JPEG dimensions exceed native int range");
|
|
2588
|
+
if (!ip_header_fits_int(ctx, srcinfo))
|
|
2089
2589
|
return 0;
|
|
2090
|
-
}
|
|
2091
2590
|
|
|
2092
2591
|
ctx->width = (int)srcinfo->image_width;
|
|
2093
2592
|
ctx->height = (int)srcinfo->image_height;
|
|
@@ -2109,15 +2608,10 @@ static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
|
2109
2608
|
jvirt_barray_ptr *coef_arrays = NULL;
|
|
2110
2609
|
unsigned long jpeg_size = 0;
|
|
2111
2610
|
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
memset(&srcerr, 0, sizeof(srcerr));
|
|
2115
|
-
memset(&dsterr, 0, sizeof(dsterr));
|
|
2611
|
+
ip_init_decompress(&srcinfo, &srcerr, ctx);
|
|
2612
|
+
ip_init_compress(&dstinfo, &dsterr, ctx);
|
|
2116
2613
|
ctx->transient_jpeg_buf = NULL;
|
|
2117
2614
|
|
|
2118
|
-
srcinfo.err = ip_use_error(&srcerr, ctx, ip_jpeg_invalid_error_exit);
|
|
2119
|
-
dstinfo.err = ip_use_error(&dsterr, ctx, ip_jpeg_encode_error_exit);
|
|
2120
|
-
|
|
2121
2615
|
ctx->jmp_armed = 1;
|
|
2122
2616
|
if (setjmp(ctx->jmpbuf)) {
|
|
2123
2617
|
if (ctx->status == IP_OK)
|
|
@@ -2125,70 +2619,82 @@ static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
|
2125
2619
|
goto fail;
|
|
2126
2620
|
}
|
|
2127
2621
|
|
|
2622
|
+
jpeg_transform_info xform;
|
|
2623
|
+
jvirt_barray_ptr *dst_coef_arrays = NULL;
|
|
2624
|
+
|
|
2625
|
+
memset(&xform, 0, sizeof(xform));
|
|
2626
|
+
xform.trim = ctx->trim_partial_mcus ? TRUE : FALSE;
|
|
2627
|
+
xform.perfect = ctx->trim_partial_mcus ? FALSE : TRUE;
|
|
2628
|
+
|
|
2128
2629
|
jpeg_create_decompress(&srcinfo);
|
|
2129
2630
|
jpeg_create_compress(&dstinfo);
|
|
2130
2631
|
jpeg_mem_src(&srcinfo, ctx->input_data, (unsigned long)ctx->input_size);
|
|
2131
|
-
|
|
2632
|
+
if (!ip_prepare_source_markers(ctx, &srcinfo))
|
|
2633
|
+
goto fail;
|
|
2132
2634
|
|
|
2133
2635
|
if (atomic_load(&ctx->cancelled))
|
|
2134
2636
|
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "lossless JPEG optimize cancelled");
|
|
2135
2637
|
|
|
2136
|
-
if (
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
ctx->source_orientation = ip_read_exif_orientation_from_decompress(&srcinfo);
|
|
2638
|
+
if (!ip_read_source_header(ctx, &srcinfo))
|
|
2639
|
+
goto fail;
|
|
2140
2640
|
if (ctx->strip_metadata && ctx->source_orientation > 1)
|
|
2141
|
-
|
|
2142
|
-
"lossless optimize cannot strip EXIF Orientation without changing "
|
|
2143
|
-
"visual orientation; use strip_metadata: false or ImagePack.compress");
|
|
2641
|
+
xform.transform = ip_xform_for_orientation(ctx->source_orientation);
|
|
2144
2642
|
|
|
2145
2643
|
if (!ip_validate_lossless_optimize_header(ctx, &srcinfo))
|
|
2146
2644
|
goto fail;
|
|
2147
2645
|
|
|
2148
|
-
if (!
|
|
2646
|
+
if (!ip_ingest_source_metadata(ctx, &srcinfo))
|
|
2149
2647
|
goto fail;
|
|
2150
2648
|
|
|
2649
|
+
if (xform.transform != JXFORM_NONE) {
|
|
2650
|
+
if (!jtransform_request_workspace(&srcinfo, &xform))
|
|
2651
|
+
IP_FAIL_GOTO(ctx, IP_ERR_UNSUPPORTED,
|
|
2652
|
+
"lossless EXIF Orientation transform would crop partial MCU edges; "
|
|
2653
|
+
"pass trim: true to allow cropping, or ImagePack.compress");
|
|
2654
|
+
}
|
|
2655
|
+
|
|
2151
2656
|
coef_arrays = jpeg_read_coefficients(&srcinfo);
|
|
2152
2657
|
jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
|
|
2153
2658
|
dstinfo.optimize_coding = TRUE;
|
|
2154
2659
|
dstinfo.num_scans = 0;
|
|
2155
2660
|
dstinfo.scan_info = NULL;
|
|
2156
2661
|
if (ctx->progressive) {
|
|
2662
|
+
jpeg_c_set_bool_param(&dstinfo, JBOOLEAN_OPTIMIZE_SCANS, TRUE);
|
|
2157
2663
|
jpeg_simple_progression(&dstinfo);
|
|
2158
2664
|
dstinfo.optimize_coding = TRUE;
|
|
2665
|
+
} else {
|
|
2666
|
+
jpeg_c_set_bool_param(&dstinfo, JBOOLEAN_OPTIMIZE_SCANS, FALSE);
|
|
2159
2667
|
}
|
|
2160
2668
|
|
|
2669
|
+
if (xform.transform != JXFORM_NONE)
|
|
2670
|
+
dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, coef_arrays, &xform);
|
|
2671
|
+
else
|
|
2672
|
+
dst_coef_arrays = coef_arrays;
|
|
2673
|
+
|
|
2161
2674
|
jpeg_mem_dest(&dstinfo, &ctx->transient_jpeg_buf, &jpeg_size);
|
|
2162
2675
|
|
|
2163
2676
|
if (atomic_load(&ctx->cancelled))
|
|
2164
2677
|
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "lossless JPEG optimize cancelled");
|
|
2165
2678
|
|
|
2166
|
-
jpeg_write_coefficients(&dstinfo,
|
|
2167
|
-
|
|
2168
|
-
|
|
2679
|
+
jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
|
|
2680
|
+
ip_write_sidecar_metadata(ctx, &dstinfo);
|
|
2681
|
+
if (xform.transform != JXFORM_NONE)
|
|
2682
|
+
jtransform_execute_transform(&srcinfo, &dstinfo, coef_arrays, &xform);
|
|
2169
2683
|
|
|
2170
2684
|
jpeg_finish_compress(&dstinfo);
|
|
2171
2685
|
jpeg_finish_decompress(&srcinfo);
|
|
2172
2686
|
|
|
2173
|
-
if (ctx
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
jpeg_destroy_compress(&dstinfo);
|
|
2177
|
-
jpeg_destroy_decompress(&srcinfo);
|
|
2178
|
-
ctx->jmp_armed = 0;
|
|
2687
|
+
if (!ip_within_output_limit(ctx, jpeg_size))
|
|
2688
|
+
goto fail;
|
|
2179
2689
|
|
|
2180
|
-
|
|
2181
|
-
ctx
|
|
2182
|
-
ctx->output_size = (size_t)jpeg_size;
|
|
2183
|
-
ctx->output_owner = IP_OUTPUT_OWNER_MALLOC;
|
|
2690
|
+
ip_close_codec_pair(&dstinfo, &srcinfo, ctx);
|
|
2691
|
+
ip_claim_jpeg_output(ctx, jpeg_size);
|
|
2184
2692
|
return 1;
|
|
2185
2693
|
|
|
2186
2694
|
fail:
|
|
2187
|
-
|
|
2188
|
-
jpeg_destroy_decompress(&srcinfo);
|
|
2695
|
+
ip_close_codec_pair(&dstinfo, &srcinfo, ctx);
|
|
2189
2696
|
free(ctx->transient_jpeg_buf);
|
|
2190
2697
|
ctx->transient_jpeg_buf = NULL;
|
|
2191
|
-
ctx->jmp_armed = 0;
|
|
2192
2698
|
return 0;
|
|
2193
2699
|
}
|
|
2194
2700
|
|
|
@@ -2264,16 +2770,16 @@ static void *ip_run_encode_nogvl(void *data) {
|
|
|
2264
2770
|
return NULL;
|
|
2265
2771
|
}
|
|
2266
2772
|
|
|
2267
|
-
static int
|
|
2773
|
+
static int ip_invoke(ip_context_t *ctx, void *(*fn)(void *)) {
|
|
2268
2774
|
ip_resolve_execution(ctx);
|
|
2269
2775
|
|
|
2270
2776
|
if (ctx->resolved_execution == IP_EXEC_DIRECT) {
|
|
2271
|
-
|
|
2777
|
+
fn(ctx);
|
|
2272
2778
|
} else if (ctx->resolved_execution == IP_EXEC_NOGVL) {
|
|
2273
|
-
rb_nogvl(
|
|
2779
|
+
rb_nogvl(fn, ctx, ip_unblock_function, ctx, 0);
|
|
2274
2780
|
} else if (ctx->resolved_execution == IP_EXEC_OFFLOAD) {
|
|
2275
2781
|
#if IMAGE_PACK_HAS_OFFLOAD_SAFE
|
|
2276
|
-
rb_nogvl(
|
|
2782
|
+
rb_nogvl(fn, ctx, ip_unblock_function, ctx, RB_NOGVL_OFFLOAD_SAFE);
|
|
2277
2783
|
#else
|
|
2278
2784
|
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
2279
2785
|
"offload execution is unavailable in this runtime; it requires Ruby "
|
|
@@ -2289,6 +2795,10 @@ static int ip_run_context(ip_context_t *ctx) {
|
|
|
2289
2795
|
return ctx->status == IP_OK;
|
|
2290
2796
|
}
|
|
2291
2797
|
|
|
2798
|
+
static int ip_run_context(ip_context_t *ctx) {
|
|
2799
|
+
return ip_invoke(ctx, ip_run_encode_nogvl);
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2292
2802
|
static void *ip_run_optimize_nogvl(void *data) {
|
|
2293
2803
|
ip_context_t *ctx = (ip_context_t *)data;
|
|
2294
2804
|
if (ctx->status == IP_OK)
|
|
@@ -2297,28 +2807,7 @@ static void *ip_run_optimize_nogvl(void *data) {
|
|
|
2297
2807
|
}
|
|
2298
2808
|
|
|
2299
2809
|
static int ip_run_optimize_context(ip_context_t *ctx) {
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
if (ctx->resolved_execution == IP_EXEC_DIRECT) {
|
|
2303
|
-
ip_run_optimize_nogvl(ctx);
|
|
2304
|
-
} else if (ctx->resolved_execution == IP_EXEC_NOGVL) {
|
|
2305
|
-
rb_nogvl(ip_run_optimize_nogvl, ctx, ip_unblock_function, ctx, 0);
|
|
2306
|
-
} else if (ctx->resolved_execution == IP_EXEC_OFFLOAD) {
|
|
2307
|
-
#if IMAGE_PACK_HAS_OFFLOAD_SAFE
|
|
2308
|
-
rb_nogvl(ip_run_optimize_nogvl, ctx, ip_unblock_function, ctx, RB_NOGVL_OFFLOAD_SAFE);
|
|
2309
|
-
#else
|
|
2310
|
-
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
2311
|
-
"offload execution is unavailable in this runtime; it requires Ruby "
|
|
2312
|
-
">= 3.4 and IMAGE_PACK_DISABLE_OFFLOAD must not be set");
|
|
2313
|
-
#endif
|
|
2314
|
-
} else {
|
|
2315
|
-
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "invalid resolved execution mode");
|
|
2316
|
-
}
|
|
2317
|
-
|
|
2318
|
-
if (ctx->resolved_execution != IP_EXEC_DIRECT)
|
|
2319
|
-
rb_thread_check_ints();
|
|
2320
|
-
|
|
2321
|
-
return ctx->status == IP_OK;
|
|
2810
|
+
return ip_invoke(ctx, ip_run_optimize_nogvl);
|
|
2322
2811
|
}
|
|
2323
2812
|
|
|
2324
2813
|
static size_t config_size_value(VALUE config, ID id, size_t fallback, const char *name) {
|
|
@@ -2378,161 +2867,151 @@ static void validate_limits_for_pixels(ip_context_t *ctx) {
|
|
|
2378
2867
|
ctx->decoded_bytes = decoded_bytes;
|
|
2379
2868
|
}
|
|
2380
2869
|
|
|
2381
|
-
static
|
|
2382
|
-
ip_compress_jpeg_call_t *call = (ip_compress_jpeg_call_t *)ptr;
|
|
2870
|
+
static ip_context_t *ip_entry_alloc(ip_context_t **slot) {
|
|
2383
2871
|
ip_context_t *ctx = ip_context_new();
|
|
2384
2872
|
if (!ctx)
|
|
2385
2873
|
rb_raise(rb_eImagePackOutOfMemoryError, "failed to allocate native context");
|
|
2386
|
-
|
|
2874
|
+
*slot = ctx;
|
|
2875
|
+
return ctx;
|
|
2876
|
+
}
|
|
2387
2877
|
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
ctx->
|
|
2878
|
+
static void ip_bind_runtime(ip_context_t *ctx, VALUE execution, VALUE cancellable,
|
|
2879
|
+
VALUE has_scheduler, VALUE strict) {
|
|
2880
|
+
ctx->requested_execution = ip_parse_execution(execution);
|
|
2881
|
+
ctx->cancellable_requested = ip_bool_value(cancellable);
|
|
2882
|
+
ctx->has_scheduler = ip_bool_value(has_scheduler);
|
|
2883
|
+
ctx->strict = ip_bool_value(strict);
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
static void ip_bind_encode_profile(ip_context_t *ctx, VALUE algo, VALUE quality, VALUE min_ssim,
|
|
2887
|
+
VALUE trellis, VALUE scan_opt, VALUE progressive) {
|
|
2888
|
+
ctx->algo = ip_parse_algo(algo);
|
|
2889
|
+
ctx->quality = NUM2INT(quality);
|
|
2391
2890
|
ctx->selected_quality = ctx->quality;
|
|
2392
2891
|
ip_validate_quality_or_raise(ctx);
|
|
2393
|
-
ctx->min_ssim = NUM2DBL(
|
|
2892
|
+
ctx->min_ssim = NUM2DBL(min_ssim);
|
|
2394
2893
|
ctx->ssim_guard_enabled = ctx->min_ssim > 0.0;
|
|
2395
2894
|
ip_validate_min_ssim_or_raise(ctx);
|
|
2396
|
-
ctx->mozjpeg_trellis_enabled = ip_bool_value(
|
|
2397
|
-
ctx->mozjpeg_scan_opt_enabled = ip_bool_value(
|
|
2398
|
-
ctx->progressive = ip_bool_value(
|
|
2399
|
-
|
|
2400
|
-
ctx->requested_execution = ip_parse_execution(call->execution);
|
|
2401
|
-
ctx->cancellable_requested = ip_bool_value(call->cancellable);
|
|
2402
|
-
ctx->has_scheduler = ip_bool_value(call->has_scheduler);
|
|
2403
|
-
ctx->strict = ip_bool_value(call->strict);
|
|
2404
|
-
apply_configuration(call->self, ctx);
|
|
2895
|
+
ctx->mozjpeg_trellis_enabled = ip_bool_value(trellis);
|
|
2896
|
+
ctx->mozjpeg_scan_opt_enabled = ip_bool_value(scan_opt);
|
|
2897
|
+
ctx->progressive = ip_bool_value(progressive);
|
|
2898
|
+
}
|
|
2405
2899
|
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
}
|
|
2900
|
+
static void ip_bind_encode_knobs(ip_context_t *ctx, VALUE subsampling, VALUE tune, VALUE effort) {
|
|
2901
|
+
ctx->subsampling = NUM2INT(subsampling);
|
|
2902
|
+
ctx->tune = NUM2INT(tune);
|
|
2903
|
+
ctx->effort = NUM2INT(effort);
|
|
2904
|
+
}
|
|
2412
2905
|
|
|
2906
|
+
static int ip_bind_jpeg_io(ip_context_t *ctx, VALUE input, VALUE input_kind, VALUE output,
|
|
2907
|
+
ip_output_kind_t out_kind, int inspect_allow_cmyk) {
|
|
2908
|
+
ip_input_kind_t in_kind = ip_parse_input_kind(input_kind);
|
|
2909
|
+
if (!ip_prepare_output_path(ctx, output, out_kind) ||
|
|
2910
|
+
!ip_prepare_input_bytes(ctx, input, in_kind))
|
|
2911
|
+
return 0;
|
|
2413
2912
|
if (ctx->requested_execution == IP_EXEC_AUTO && ctx->input_size < ctx->direct_input_threshold &&
|
|
2414
|
-
!ip_inspect_jpeg_header(ctx,
|
|
2415
|
-
|
|
2416
|
-
rb_raise(rb_eImagePackInvalidImageError, "invalid JPEG input");
|
|
2417
|
-
}
|
|
2418
|
-
|
|
2913
|
+
!ip_inspect_jpeg_header(ctx, inspect_allow_cmyk))
|
|
2914
|
+
return 0;
|
|
2419
2915
|
ip_resolve_execution(ctx);
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2916
|
+
return ip_ensure_owned_input_for_async(ctx, input, in_kind);
|
|
2917
|
+
}
|
|
2918
|
+
|
|
2919
|
+
static void ip_raise_prepare(ip_context_t *ctx, const char *message) {
|
|
2920
|
+
ip_raise_for_status(ctx);
|
|
2921
|
+
rb_raise(rb_eImagePackInvalidArgumentError, "%s", message);
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
static VALUE ip_complete_encode(ip_context_t *ctx, ip_output_kind_t kind, VALUE report) {
|
|
2925
|
+
VALUE out = ip_finish_output(ctx, kind);
|
|
2926
|
+
return ip_bool_value(report) ? ip_build_report(ctx, out) : out;
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
static VALUE ip_compress_jpeg_entry_body(VALUE ptr) {
|
|
2930
|
+
ip_compress_jpeg_call_t *call = (ip_compress_jpeg_call_t *)ptr;
|
|
2931
|
+
ip_context_t *ctx = ip_entry_alloc(&call->ctx);
|
|
2932
|
+
ip_output_kind_t out_kind = ip_parse_output_kind(call->output_kind);
|
|
2933
|
+
|
|
2934
|
+
ip_bind_encode_profile(ctx, call->algo, call->quality, call->min_ssim, call->mozjpeg_trellis,
|
|
2935
|
+
call->mozjpeg_scan_opt, call->progressive);
|
|
2936
|
+
ctx->strip_metadata = ip_bool_value(call->strip_metadata);
|
|
2937
|
+
ctx->strip_icc = ip_bool_value(call->strip_icc);
|
|
2938
|
+
ip_bind_encode_knobs(ctx, call->subsampling, call->tune, call->effort);
|
|
2939
|
+
ctx->scale_num = (unsigned int)NUM2INT(call->scale_num);
|
|
2940
|
+
ctx->scale_denom = (unsigned int)NUM2INT(call->scale_denom);
|
|
2941
|
+
ip_bind_runtime(ctx, call->execution, call->cancellable, call->has_scheduler, call->strict);
|
|
2942
|
+
apply_configuration(call->self, ctx);
|
|
2943
|
+
|
|
2944
|
+
if (!ip_bind_jpeg_io(ctx, call->input, call->input_kind, call->output, out_kind, 0))
|
|
2945
|
+
ip_raise_prepare(ctx, "invalid JPEG input");
|
|
2424
2946
|
|
|
2425
2947
|
ip_run_context(ctx);
|
|
2426
|
-
|
|
2427
|
-
VALUE output_value = ip_finish_output(ctx, out_kind);
|
|
2428
|
-
return ip_build_report(ctx, output_value);
|
|
2429
|
-
}
|
|
2430
|
-
return ip_finish_output(ctx, out_kind);
|
|
2948
|
+
return ip_complete_encode(ctx, out_kind, call->report);
|
|
2431
2949
|
}
|
|
2432
2950
|
|
|
2433
2951
|
static VALUE ip_compress_jpeg_entry(int argc, VALUE *argv, VALUE self) {
|
|
2434
|
-
rb_check_arity(argc,
|
|
2952
|
+
rb_check_arity(argc, 22, 22);
|
|
2435
2953
|
ip_compress_jpeg_call_t call = {self, argv[0], argv[1], argv[2], argv[3], argv[4],
|
|
2436
2954
|
argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
|
|
2437
|
-
argv[11], argv[12], argv[13], argv[14], argv[15],
|
|
2955
|
+
argv[11], argv[12], argv[13], argv[14], argv[15], argv[16],
|
|
2956
|
+
argv[17], argv[18], argv[19], argv[20], argv[21], NULL};
|
|
2438
2957
|
return rb_ensure(ip_compress_jpeg_entry_body, (VALUE)&call, ip_call_cleanup, (VALUE)&call.ctx);
|
|
2439
2958
|
}
|
|
2440
2959
|
|
|
2441
2960
|
static VALUE ip_compress_pixels_entry_body(VALUE ptr) {
|
|
2442
2961
|
ip_compress_pixels_call_t *call = (ip_compress_pixels_call_t *)ptr;
|
|
2443
|
-
ip_context_t *ctx =
|
|
2444
|
-
if (!ctx)
|
|
2445
|
-
rb_raise(rb_eImagePackOutOfMemoryError, "failed to allocate native context");
|
|
2446
|
-
call->ctx = ctx;
|
|
2447
|
-
|
|
2962
|
+
ip_context_t *ctx = ip_entry_alloc(&call->ctx);
|
|
2448
2963
|
ip_output_kind_t out_kind = ip_parse_output_kind(call->output_kind);
|
|
2449
|
-
|
|
2450
|
-
ctx->quality
|
|
2451
|
-
|
|
2452
|
-
ip_validate_quality_or_raise(ctx);
|
|
2453
|
-
ctx->min_ssim = NUM2DBL(call->min_ssim);
|
|
2454
|
-
ctx->ssim_guard_enabled = ctx->min_ssim > 0.0;
|
|
2455
|
-
ip_validate_min_ssim_or_raise(ctx);
|
|
2456
|
-
ctx->mozjpeg_trellis_enabled = ip_bool_value(call->mozjpeg_trellis);
|
|
2457
|
-
ctx->mozjpeg_scan_opt_enabled = ip_bool_value(call->mozjpeg_scan_opt);
|
|
2458
|
-
ctx->progressive = ip_bool_value(call->progressive);
|
|
2964
|
+
|
|
2965
|
+
ip_bind_encode_profile(ctx, call->algo, call->quality, call->min_ssim, call->mozjpeg_trellis,
|
|
2966
|
+
call->mozjpeg_scan_opt, call->progressive);
|
|
2459
2967
|
ctx->strip_metadata = 1;
|
|
2460
|
-
ctx->
|
|
2461
|
-
ctx->
|
|
2462
|
-
ctx->
|
|
2463
|
-
ctx->strict = ip_bool_value(call->strict);
|
|
2968
|
+
ctx->strip_icc = 1;
|
|
2969
|
+
ip_bind_encode_knobs(ctx, call->subsampling, call->tune, call->effort);
|
|
2970
|
+
ip_bind_runtime(ctx, call->execution, call->cancellable, call->has_scheduler, call->strict);
|
|
2464
2971
|
apply_configuration(call->self, ctx);
|
|
2465
2972
|
|
|
2466
2973
|
if (!ip_prepare_output_path(ctx, call->output, out_kind) ||
|
|
2467
2974
|
!ip_prepare_pixels(ctx, call->buffer, NUM2INT(call->width), NUM2INT(call->height),
|
|
2468
|
-
NUM2INT(call->channels), ip_bool_value(call->exact_size)))
|
|
2469
|
-
|
|
2470
|
-
rb_raise(rb_eImagePackInvalidArgumentError, "invalid pixel input");
|
|
2471
|
-
}
|
|
2975
|
+
NUM2INT(call->channels), ip_bool_value(call->exact_size)))
|
|
2976
|
+
ip_raise_prepare(ctx, "invalid pixel input");
|
|
2472
2977
|
|
|
2473
2978
|
validate_limits_for_pixels(ctx);
|
|
2474
2979
|
if (ctx->status != IP_OK)
|
|
2475
2980
|
ip_raise_for_status(ctx);
|
|
2476
2981
|
|
|
2477
2982
|
ip_resolve_execution(ctx);
|
|
2478
|
-
if (!ip_ensure_owned_pixels_for_async(ctx, call->buffer))
|
|
2479
|
-
|
|
2480
|
-
rb_raise(rb_eImagePackInvalidArgumentError, "invalid pixel input");
|
|
2481
|
-
}
|
|
2983
|
+
if (!ip_ensure_owned_pixels_for_async(ctx, call->buffer))
|
|
2984
|
+
ip_raise_prepare(ctx, "invalid pixel input");
|
|
2482
2985
|
|
|
2483
2986
|
ip_run_context(ctx);
|
|
2484
|
-
|
|
2485
|
-
VALUE output_value = ip_finish_output(ctx, out_kind);
|
|
2486
|
-
return ip_build_report(ctx, output_value);
|
|
2487
|
-
}
|
|
2488
|
-
return ip_finish_output(ctx, out_kind);
|
|
2987
|
+
return ip_complete_encode(ctx, out_kind, call->report);
|
|
2489
2988
|
}
|
|
2490
2989
|
|
|
2491
2990
|
static VALUE ip_compress_pixels_entry(int argc, VALUE *argv, VALUE self) {
|
|
2492
|
-
rb_check_arity(argc,
|
|
2493
|
-
ip_compress_pixels_call_t call = {self, argv[0], argv[1], argv[2], argv[3],
|
|
2494
|
-
argv[
|
|
2495
|
-
argv[
|
|
2496
|
-
argv[
|
|
2991
|
+
rb_check_arity(argc, 21, 21);
|
|
2992
|
+
ip_compress_pixels_call_t call = {self, argv[0], argv[1], argv[2], argv[3], argv[4],
|
|
2993
|
+
argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
|
|
2994
|
+
argv[11], argv[12], argv[13], argv[14], argv[15], argv[16],
|
|
2995
|
+
argv[17], argv[18], argv[19], argv[20], NULL};
|
|
2497
2996
|
return rb_ensure(ip_compress_pixels_entry_body, (VALUE)&call, ip_call_cleanup,
|
|
2498
2997
|
(VALUE)&call.ctx);
|
|
2499
2998
|
}
|
|
2500
2999
|
|
|
2501
3000
|
static VALUE ip_optimize_jpeg_entry_body(VALUE ptr) {
|
|
2502
3001
|
ip_optimize_jpeg_call_t *call = (ip_optimize_jpeg_call_t *)ptr;
|
|
2503
|
-
ip_context_t *ctx =
|
|
2504
|
-
if (!ctx)
|
|
2505
|
-
rb_raise(rb_eImagePackOutOfMemoryError, "failed to allocate native context");
|
|
2506
|
-
call->ctx = ctx;
|
|
2507
|
-
|
|
3002
|
+
ip_context_t *ctx = ip_entry_alloc(&call->ctx);
|
|
2508
3003
|
ip_output_kind_t out_kind = ip_parse_output_kind(call->output_kind);
|
|
3004
|
+
|
|
2509
3005
|
ctx->progressive = ip_bool_value(call->progressive);
|
|
2510
3006
|
ctx->strip_metadata = ip_bool_value(call->strip_metadata);
|
|
2511
|
-
ctx->
|
|
2512
|
-
ctx->
|
|
2513
|
-
ctx->has_scheduler = ip_bool_value(call->has_scheduler);
|
|
2514
|
-
ctx->strict = ip_bool_value(call->strict);
|
|
3007
|
+
ctx->strip_icc = ip_bool_value(call->strip_icc);
|
|
3008
|
+
ctx->trim_partial_mcus = ip_bool_value(call->trim);
|
|
2515
3009
|
ctx->ssim_guard_enabled = 0;
|
|
3010
|
+
ip_bind_runtime(ctx, call->execution, call->cancellable, call->has_scheduler, call->strict);
|
|
2516
3011
|
apply_configuration(call->self, ctx);
|
|
2517
3012
|
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
!ip_prepare_input_bytes(ctx, call->input, in_kind)) {
|
|
2521
|
-
ip_raise_for_status(ctx);
|
|
2522
|
-
rb_raise(rb_eImagePackInvalidArgumentError, "invalid JPEG input");
|
|
2523
|
-
}
|
|
2524
|
-
|
|
2525
|
-
if (ctx->requested_execution == IP_EXEC_AUTO && ctx->input_size < ctx->direct_input_threshold &&
|
|
2526
|
-
!ip_inspect_jpeg_header(ctx, 1)) {
|
|
2527
|
-
ip_raise_for_status(ctx);
|
|
2528
|
-
rb_raise(rb_eImagePackInvalidImageError, "invalid JPEG input");
|
|
2529
|
-
}
|
|
2530
|
-
|
|
2531
|
-
ip_resolve_execution(ctx);
|
|
2532
|
-
if (!ip_ensure_owned_input_for_async(ctx, call->input, in_kind)) {
|
|
2533
|
-
ip_raise_for_status(ctx);
|
|
2534
|
-
rb_raise(rb_eImagePackInvalidArgumentError, "invalid JPEG input");
|
|
2535
|
-
}
|
|
3013
|
+
if (!ip_bind_jpeg_io(ctx, call->input, call->input_kind, call->output, out_kind, 1))
|
|
3014
|
+
ip_raise_prepare(ctx, "invalid JPEG input");
|
|
2536
3015
|
|
|
2537
3016
|
ip_run_optimize_context(ctx);
|
|
2538
3017
|
return ip_finish_output(ctx, out_kind);
|
|
@@ -2541,10 +3020,10 @@ static VALUE ip_optimize_jpeg_entry_body(VALUE ptr) {
|
|
|
2541
3020
|
static VALUE ip_optimize_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
|
|
2542
3021
|
VALUE output_kind, VALUE progressive, VALUE strip_metadata,
|
|
2543
3022
|
VALUE execution, VALUE cancellable, VALUE has_scheduler,
|
|
2544
|
-
VALUE strict) {
|
|
2545
|
-
ip_optimize_jpeg_call_t call = {
|
|
2546
|
-
|
|
2547
|
-
|
|
3023
|
+
VALUE strict, VALUE strip_icc, VALUE trim) {
|
|
3024
|
+
ip_optimize_jpeg_call_t call = {
|
|
3025
|
+
self, input, input_kind, output, output_kind, progressive, strip_metadata,
|
|
3026
|
+
execution, cancellable, has_scheduler, strict, strip_icc, trim, NULL};
|
|
2548
3027
|
return rb_ensure(ip_optimize_jpeg_entry_body, (VALUE)&call, ip_call_cleanup, (VALUE)&call.ctx);
|
|
2549
3028
|
}
|
|
2550
3029
|
|
|
@@ -2611,7 +3090,7 @@ IMAGE_PACK_INIT_EXPORT void Init_image_pack(void) {
|
|
|
2611
3090
|
int arity;
|
|
2612
3091
|
} methods[] = {{"__compress_jpeg", (VALUE (*)(ANYARGS))ip_compress_jpeg_entry, -1},
|
|
2613
3092
|
{"__compress_pixels", (VALUE (*)(ANYARGS))ip_compress_pixels_entry, -1},
|
|
2614
|
-
{"__optimize_jpeg", (VALUE (*)(ANYARGS))ip_optimize_jpeg_entry,
|
|
3093
|
+
{"__optimize_jpeg", (VALUE (*)(ANYARGS))ip_optimize_jpeg_entry, 12},
|
|
2615
3094
|
{"__inspect_image", (VALUE (*)(ANYARGS))ip_inspect_image_entry, 2}};
|
|
2616
3095
|
for (size_t i = 0; i < IP_ARRAY_LEN(methods); i++) {
|
|
2617
3096
|
rb_define_singleton_method(rb_mImagePack, methods[i].name, methods[i].fn, methods[i].arity);
|