image_pack 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +106 -136
- data/ext/image_pack/extconf.rb +1 -1
- data/ext/image_pack/image_pack.c +118 -39
- data/lib/image_pack/configuration.rb +1 -1
- data/lib/image_pack/version.rb +1 -1
- data/lib/image_pack.rb +23 -8
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 124f64ccca4910cee483d3c63a189f6de7d7ea72e8f4a8b88d538fe503cf0673
|
|
4
|
+
data.tar.gz: d51e698fdad475590f685ddd6097f9bf8486dcb154ce37716e60fac256949c6c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ed979776d78d8e1d64e01ec46499d2453d13483495688cc79b71e0fc84b542d68e2a076015d480aaac812c90ef1dea49952f40b156d11d2b4dd95ace73a7688
|
|
7
|
+
data.tar.gz: 233229ccf2b0daa138dc6bd664ce14c8ddf770aaa232d42efc33d43419a3c7f0f5b673daf985e6745e49fcf8ceeac9503d84910319cb8a7d269178e08cd657eb
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.4
|
|
4
|
+
|
|
5
|
+
- libjpeg/MozJPEG diagnostics are no longer written to `stderr`; instead decode warnings (e.g. "Premature end of JPEG file") are counted and the first message is captured, so a damaged or truncated input is observable rather than silently degraded.
|
|
6
|
+
- Added `strict:` to `compress`, `compress_pixels`, and `optimize_jpeg`. With `strict: true` the first libjpeg warning is promoted to `ImagePack::InvalidImageError` instead of producing a degraded JPEG; default is `false`.
|
|
7
|
+
- Added `report:` to `compress` and `compress_pixels`. With `report: true` the call returns a Hash `{ output:, quality:, ssim:, algo:, bytesize:, input_bytesize:, warning_count:, warning: }`, exposing the SSIM-selected quality, achieved luma SSIM, and decode warning information; `report: false` (default) keeps the previous return value (binary `String` or `true`).
|
|
8
|
+
- Made the no-GVL and offload execution paths interruptible regardless of `cancellable:`. Decode/encode/SSIM checkpoints now always observe the unblock signal, and a pending Ruby interrupt (signal, `Thread#raise`, `Thread#kill`, scheduler) is delivered via `rb_thread_check_ints` after the native section, so the raised exception propagates instead of being masked. Cancellation is expressed by raising on the worker thread; `ImagePack::CancelledError` is the idiomatic choice.
|
|
9
|
+
- Added the `IMAGE_PACK_DISABLE_OFFLOAD=1` runtime opt-out. The vendored MozJPEG SIMD thread-local storage is only an idempotent CPU-feature cache, so offload remains safe with SIMD enabled, but the environment variable forces `offload_safe?` to `false` and prevents `:auto`/`:offload` from offloading.
|
|
10
|
+
- `NATIVE_MOZJPEG_VERSION` is now derived from a dedicated `IMAGE_PACK_MOZJPEG_VERSION` compile define instead of relying on the libjpeg `VERSION` macro being in scope.
|
|
11
|
+
- Removed the unused `IMAGE_PACK_RUBY34_ONLY` compile define.
|
|
12
|
+
- Removed a stale `v0.2.2` reference from the `output:` argument error message.
|
|
13
|
+
- Added tests covering `report:`, `strict:`, warning collection, the cancellation contract, offload availability, and version hygiene.
|
|
14
|
+
- Documentation and message version references updated to 0.2.4.
|
|
15
|
+
|
|
16
|
+
## 0.2.3
|
|
17
|
+
|
|
3
18
|
- Lowered the runtime floor to Ruby >= 3.1.0 while keeping `execution: :offload` Ruby >= 3.4-only and explicitly unsupported on older Rubies.
|
|
19
|
+
|
|
4
20
|
## 0.2.2
|
|
5
21
|
|
|
6
22
|
- Fixed native cleanup safety: native contexts are now released through `rb_ensure`, including Ruby exception paths from argument coercion, config reads, native status errors, output `String` allocation, and file-output failures.
|
data/README.md
CHANGED
|
@@ -1,199 +1,169 @@
|
|
|
1
1
|
# image_pack
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Ruby-native JPEG compression and optimization backed by vendored pure-C MozJPEG/libjpeg.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
This pure-C variant intentionally removes Jpegli and any C++ toolchain requirement.
|
|
8
|
-
The native layer is written in C and links against vendored MozJPEG, which is libjpeg-compatible and based on libjpeg-turbo.
|
|
9
|
-
|
|
10
|
-
## Backends / modes
|
|
5
|
+
No system `libjpeg`, `mozjpeg`, `git`, or `CMake` is required for gem users.
|
|
11
6
|
|
|
12
7
|
```ruby
|
|
13
|
-
|
|
14
|
-
ImagePack.compress(input, algo: :mozjpeg)
|
|
8
|
+
gem "image_pack"
|
|
15
9
|
```
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Both modes produce ordinary `.jpg` files.
|
|
21
|
-
|
|
22
|
-
Important implementation note: this prototype uses one pure-C vendored codec family to avoid static-link symbol conflicts between separate libjpeg-compatible libraries. If later we need exact latest `libjpeg-turbo` and exact MozJPEG in one gem, the correct next step is linker isolation / separate native extensions with hidden symbols.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
## Native extension layout
|
|
26
|
-
|
|
27
|
-
For the first prototype the native layer intentionally lives in one file:
|
|
28
|
-
|
|
29
|
-
```text
|
|
30
|
-
ext/image_pack/
|
|
31
|
-
├── extconf.rb
|
|
32
|
-
└── image_pack.c
|
|
11
|
+
```ruby
|
|
12
|
+
require "image_pack"
|
|
33
13
|
```
|
|
34
14
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
## API
|
|
15
|
+
## Quick use
|
|
38
16
|
|
|
39
17
|
```ruby
|
|
40
|
-
require "image_pack"
|
|
41
|
-
|
|
42
18
|
jpeg = File.binread("photo.jpg")
|
|
43
19
|
|
|
44
|
-
ImagePack.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# SSIM-guarded compression: choose the smallest acceptable JPEG quality
|
|
48
|
-
# while keeping decoded visual similarity >= 0.985.
|
|
49
|
-
ImagePack.compress(jpeg, algo: :mozjpeg, min_ssim: 0.985)
|
|
20
|
+
small = ImagePack.compress_bytes(jpeg, quality: 82)
|
|
21
|
+
File.binwrite("photo.small.jpg", small)
|
|
50
22
|
|
|
51
|
-
|
|
52
|
-
ImagePack.
|
|
23
|
+
ImagePack.compress_file("photo.jpg", output: "photo.small.jpg")
|
|
24
|
+
ImagePack.optimize_file("photo.jpg", output: "photo.optimized.jpg")
|
|
25
|
+
```
|
|
53
26
|
|
|
54
|
-
|
|
55
|
-
ImagePack.compress_file("photo.jpg", output: "photo.optimized.jpg", algo: :mozjpeg)
|
|
56
|
-
ImagePack.compress_bytes(jpeg, algo: :fast, quality: 82)
|
|
27
|
+
Prefer explicit helpers:
|
|
57
28
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
ImagePack.
|
|
61
|
-
ImagePack.optimize_file("photo.jpg", output: "photo.lossless.jpg")
|
|
29
|
+
```ruby
|
|
30
|
+
ImagePack.compress_bytes(jpeg)
|
|
31
|
+
ImagePack.compress_file("photo.jpg", output: "out.jpg")
|
|
62
32
|
ImagePack.optimize_bytes(jpeg)
|
|
63
|
-
|
|
64
|
-
ImagePack.compress_pixels(rgb_buffer,
|
|
65
|
-
width: 1920,
|
|
66
|
-
height: 1080,
|
|
67
|
-
channels: 3,
|
|
68
|
-
algo: :mozjpeg
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
ImagePack.inspect_image(jpeg)
|
|
72
|
-
# => { format: :jpeg, width: 1920, height: 1080, channels: 3, bit_depth: 8, decoded_bytes: 6220800 }
|
|
33
|
+
ImagePack.optimize_file("photo.jpg", output: "out.jpg")
|
|
73
34
|
```
|
|
74
35
|
|
|
75
|
-
##
|
|
36
|
+
## Compression
|
|
76
37
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
For non-ambiguous call sites prefer `compress_bytes(bytes, ...)` or `compress_file(path, ...)`.
|
|
38
|
+
```ruby
|
|
39
|
+
ImagePack.compress_bytes(jpeg,
|
|
40
|
+
algo: :size,
|
|
41
|
+
quality: 82,
|
|
42
|
+
strip_metadata: true
|
|
43
|
+
)
|
|
44
|
+
```
|
|
85
45
|
|
|
86
|
-
|
|
87
|
-
`channels` must be `1`, `3`, or `4`. Alpha in RGBA input is dropped in v0.2.2; pass `drop_alpha: true` to make that explicit, or `drop_alpha: false` to reject it.
|
|
46
|
+
Algorithms:
|
|
88
47
|
|
|
89
|
-
|
|
90
|
-
|
|
48
|
+
- `:size` / `:mozjpeg` — smaller files, default
|
|
49
|
+
- `:fast` / `:jpeg_turbo` — faster mode
|
|
91
50
|
|
|
92
|
-
|
|
51
|
+
Common options:
|
|
93
52
|
|
|
94
|
-
|
|
53
|
+
```ruby
|
|
54
|
+
ImagePack.compress_bytes(jpeg, min_ssim: 0.985)
|
|
55
|
+
ImagePack.compress_bytes(jpeg, progressive: true)
|
|
56
|
+
ImagePack.compress_bytes(jpeg, strict: true)
|
|
57
|
+
ImagePack.compress_bytes(jpeg, report: true)
|
|
58
|
+
```
|
|
95
59
|
|
|
96
|
-
|
|
60
|
+
`min_ssim:` searches for the lowest acceptable quality using a fast native luma SSIM guard.
|
|
97
61
|
|
|
98
|
-
|
|
99
|
-
- `strip_metadata: false` preserves APP/COM metadata by default because this path is meant to be visually/losslessly safe.
|
|
62
|
+
`strict: true` raises `ImagePack::InvalidImageError` on damaged/truncated JPEG warnings.
|
|
100
63
|
|
|
101
|
-
|
|
64
|
+
`report: true` returns a Hash:
|
|
102
65
|
|
|
103
|
-
|
|
66
|
+
```ruby
|
|
67
|
+
{
|
|
68
|
+
output: "\xFF\xD8...",
|
|
69
|
+
quality: 84,
|
|
70
|
+
ssim: 0.9861,
|
|
71
|
+
algo: :mozjpeg,
|
|
72
|
+
bytesize: 50122,
|
|
73
|
+
input_bytesize: 81344,
|
|
74
|
+
warning_count: 0,
|
|
75
|
+
warning: nil
|
|
76
|
+
}
|
|
77
|
+
```
|
|
104
78
|
|
|
105
|
-
`
|
|
79
|
+
With `output: "file.jpg"`, `output` is `true`.
|
|
106
80
|
|
|
107
|
-
|
|
108
|
-
2. encode trial JPEG candidates with the existing MozJPEG/libjpeg backend;
|
|
109
|
-
3. decode each candidate;
|
|
110
|
-
4. compute a native luma SSIM score;
|
|
111
|
-
5. return the smallest quality that satisfies `SSIM >= min_ssim`.
|
|
81
|
+
## Lossless optimize
|
|
112
82
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
83
|
+
```ruby
|
|
84
|
+
ImagePack.optimize_bytes(jpeg)
|
|
85
|
+
ImagePack.optimize_file("photo.jpg", output: "photo.optimized.jpg")
|
|
86
|
+
```
|
|
116
87
|
|
|
117
|
-
|
|
118
|
-
is raised. With `execution: :auto`, guarded compression uses the no-GVL/offload
|
|
119
|
-
path instead of the small-image direct path because it may encode/decode several
|
|
120
|
-
candidates. For `compress_pixels`, the reference is the raw pixel buffer itself, not a seed JPEG. RGBA + `min_ssim` is rejected because JPEG cannot represent alpha.
|
|
88
|
+
This rewrites JPEG coefficients without decoding and re-encoding pixels. It is the right path for existing JPEGs when you only want optimized Huffman tables and optional progressive scans.
|
|
121
89
|
|
|
122
|
-
|
|
90
|
+
Defaults: `progressive: true`, `strip_metadata: false`.
|
|
123
91
|
|
|
124
|
-
|
|
92
|
+
If `strip_metadata: true` would remove EXIF Orientation, `optimize_jpeg` raises `UnsupportedError` instead of silently changing visual orientation.
|
|
125
93
|
|
|
126
|
-
|
|
94
|
+
## Raw pixels
|
|
127
95
|
|
|
128
|
-
|
|
96
|
+
```ruby
|
|
97
|
+
ImagePack.compress_pixels(rgb,
|
|
98
|
+
width: 1920,
|
|
99
|
+
height: 1080,
|
|
100
|
+
channels: 3,
|
|
101
|
+
output: "frame.jpg"
|
|
102
|
+
)
|
|
103
|
+
```
|
|
129
104
|
|
|
130
|
-
|
|
105
|
+
`channels` must be `1`, `3`, or `4`. JPEG cannot store alpha, so RGBA input needs explicit opt-in:
|
|
131
106
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
- explicit `execution: :offload` raises `ImagePack::UnsupportedError`.
|
|
107
|
+
```ruby
|
|
108
|
+
ImagePack.compress_pixels(rgba, width: 100, height: 100, channels: 4, drop_alpha: true)
|
|
109
|
+
```
|
|
136
110
|
|
|
137
|
-
|
|
111
|
+
## Inspect
|
|
138
112
|
|
|
139
|
-
|
|
140
|
-
|
|
113
|
+
```ruby
|
|
114
|
+
ImagePack.inspect_image(jpeg)
|
|
115
|
+
# => { format: :jpeg, width: 1920, height: 1080, channels: 3, bit_depth: 8, decoded_bytes: 6220800 }
|
|
116
|
+
```
|
|
141
117
|
|
|
142
|
-
|
|
118
|
+
## Execution
|
|
143
119
|
|
|
120
|
+
Default mode is `:auto`.
|
|
144
121
|
|
|
145
122
|
```ruby
|
|
146
|
-
ImagePack.
|
|
147
|
-
ImagePack.
|
|
148
|
-
ImagePack.
|
|
149
|
-
ImagePack.
|
|
123
|
+
ImagePack.compress_bytes(jpeg, execution: :auto)
|
|
124
|
+
ImagePack.compress_bytes(jpeg, execution: :direct)
|
|
125
|
+
ImagePack.compress_bytes(jpeg, execution: :nogvl)
|
|
126
|
+
ImagePack.compress_bytes(jpeg, execution: :offload) # Ruby >= 3.4 only
|
|
150
127
|
```
|
|
151
128
|
|
|
152
|
-
|
|
153
|
-
- `:nogvl` — uses `rb_nogvl(..., flags: 0)`.
|
|
154
|
-
- `:offload` — uses `rb_nogvl(..., RB_NOGVL_OFFLOAD_SAFE)` on Ruby 3.4+; raises `ImagePack::UnsupportedError` on Ruby 3.1–3.3.
|
|
155
|
-
- `:auto` — header-first policy chooses direct/nogvl/offload; on Ruby 3.1–3.3 it never selects offload.
|
|
129
|
+
Use `ImagePack.offload_safe?` or `ImagePack.build_info` to inspect runtime support.
|
|
156
130
|
|
|
157
|
-
|
|
131
|
+
Set `IMAGE_PACK_DISABLE_OFFLOAD=1` before loading the gem to disable offload.
|
|
158
132
|
|
|
159
|
-
|
|
160
|
-
Cancellation is cooperative at decode, encode, and SSIM-search checkpoints, not instant.
|
|
133
|
+
Long no-GVL/offload calls can be interrupted by raising into the worker thread:
|
|
161
134
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
135
|
+
```ruby
|
|
136
|
+
worker = Thread.new { ImagePack.compress_bytes(jpeg, execution: :nogvl, cancellable: true) }
|
|
137
|
+
worker.raise(ImagePack::CancelledError, "cancelled")
|
|
138
|
+
worker.join
|
|
139
|
+
```
|
|
165
140
|
|
|
166
|
-
##
|
|
141
|
+
## Configuration
|
|
167
142
|
|
|
168
143
|
```ruby
|
|
169
|
-
ImagePack.
|
|
170
|
-
|
|
144
|
+
ImagePack.configure do |config|
|
|
145
|
+
config.execution = :auto
|
|
146
|
+
config.max_input_size = 256 * 1024 * 1024
|
|
147
|
+
config.max_output_size = 256 * 1024 * 1024
|
|
148
|
+
config.max_pixels = 100_000_000
|
|
149
|
+
end
|
|
171
150
|
```
|
|
172
151
|
|
|
173
|
-
##
|
|
174
|
-
|
|
175
|
-
The gem is expected to ship vendored native sources/libs, so end users should not install system libjpeg/mozjpeg.
|
|
176
|
-
For repository preparation:
|
|
152
|
+
## Development
|
|
177
153
|
|
|
178
154
|
```bash
|
|
179
155
|
bundle exec rake vendor
|
|
180
156
|
bundle exec rake compile
|
|
157
|
+
bundle exec rake test
|
|
181
158
|
```
|
|
182
159
|
|
|
183
160
|
`rake vendor` pins MozJPEG `v4.1.5`.
|
|
184
161
|
|
|
185
|
-
##
|
|
186
|
-
|
|
187
|
-
- Pixel-level `compress` rejects CMYK/YCCK JPEG input because it decodes to RGB/gray before re-encoding. Use `optimize_jpeg` for coefficient-level lossless optimization of existing CMYK/YCCK JPEGs.
|
|
188
|
-
- Arithmetic-coded JPEG support is disabled in the vendored `jconfig.h` for v0.2.2.
|
|
189
|
-
- The SSIM guard is a fast 8x8 luma block metric and still assumes quality/SSIM monotonicity during binary search.
|
|
190
|
-
- `compress(input, ...)` still has a legacy path-vs-bytes heuristic for non-binary `String`; prefer `compress_bytes` / `compress_file` and `optimize_bytes` / `optimize_file` in new code.
|
|
191
|
-
|
|
192
|
-
## What is intentionally not included
|
|
162
|
+
## Limits
|
|
193
163
|
|
|
194
|
-
-
|
|
195
|
-
-
|
|
196
|
-
-
|
|
197
|
-
-
|
|
198
|
-
-
|
|
199
|
-
-
|
|
164
|
+
- JPEG only.
|
|
165
|
+
- Ruby `>= 3.1`; `execution: :offload` requires Ruby `>= 3.4`.
|
|
166
|
+
- Pixel-level `compress` rejects CMYK/YCCK JPEG input; use `optimize_jpeg` for existing CMYK/YCCK JPEGs.
|
|
167
|
+
- Arithmetic-coded JPEG support is disabled in `0.2.4`.
|
|
168
|
+
- Streaming output is not supported; file output uses atomic write-through-temp-file and rename.
|
|
169
|
+
- `ImagePack.compress(input, ...)` keeps a legacy path-vs-bytes heuristic; prefer explicit `*_bytes` / `*_file` helpers.
|
data/ext/image_pack/extconf.rb
CHANGED
|
@@ -416,8 +416,8 @@ else
|
|
|
416
416
|
$CFLAGS += " -O2"
|
|
417
417
|
end
|
|
418
418
|
|
|
419
|
-
$CFLAGS += " -DIMAGE_PACK_RUBY34_ONLY=1"
|
|
420
419
|
$CFLAGS += build_info[:with_simd] ? " -DIMAGE_PACK_HAS_SIMD=1" : " -DIMAGE_PACK_PURE_C=1"
|
|
420
|
+
$CPPFLAGS += " -DIMAGE_PACK_MOZJPEG_VERSION=\\\"#{MOZJPEG_VERSION}\\\""
|
|
421
421
|
$LIBS += " -lm" unless msvc?
|
|
422
422
|
|
|
423
423
|
have_header("ruby/thread.h") or abort "ruby/thread.h not found"
|
data/ext/image_pack/image_pack.c
CHANGED
|
@@ -40,6 +40,10 @@
|
|
|
40
40
|
#define TRUE 1
|
|
41
41
|
#endif
|
|
42
42
|
|
|
43
|
+
#ifndef IMAGE_PACK_MOZJPEG_VERSION
|
|
44
|
+
#define IMAGE_PACK_MOZJPEG_VERSION VERSION
|
|
45
|
+
#endif
|
|
46
|
+
|
|
43
47
|
#ifndef FALSE
|
|
44
48
|
#define FALSE 0
|
|
45
49
|
#endif
|
|
@@ -151,6 +155,9 @@ typedef struct {
|
|
|
151
155
|
|
|
152
156
|
atomic_int cancelled;
|
|
153
157
|
int cancellable_requested;
|
|
158
|
+
int strict;
|
|
159
|
+
int warning_count;
|
|
160
|
+
char first_warning[200];
|
|
154
161
|
|
|
155
162
|
jmp_buf jmpbuf;
|
|
156
163
|
int jmp_armed;
|
|
@@ -174,6 +181,7 @@ typedef struct {
|
|
|
174
181
|
} ip_jpeg_error_mgr;
|
|
175
182
|
|
|
176
183
|
static VALUE rb_mImagePack;
|
|
184
|
+
static int ip_offload_runtime_enabled = 1;
|
|
177
185
|
static VALUE rb_eImagePackError;
|
|
178
186
|
static VALUE rb_eImagePackInvalidArgumentError;
|
|
179
187
|
static VALUE rb_eImagePackInvalidImageError;
|
|
@@ -255,18 +263,20 @@ static int ip_run_optimize_context(ip_context_t *ctx);
|
|
|
255
263
|
typedef struct {
|
|
256
264
|
VALUE self, input, input_kind, output, output_kind, algo, quality, min_ssim;
|
|
257
265
|
VALUE mozjpeg_trellis, progressive, strip_metadata, execution, cancellable, has_scheduler;
|
|
266
|
+
VALUE report, strict;
|
|
258
267
|
ip_context_t *ctx;
|
|
259
268
|
} ip_compress_jpeg_call_t;
|
|
260
269
|
|
|
261
270
|
typedef struct {
|
|
262
271
|
VALUE self, buffer, width, height, channels, output, output_kind, algo, quality, min_ssim;
|
|
263
272
|
VALUE mozjpeg_trellis, progressive, exact_size, execution, cancellable, has_scheduler;
|
|
273
|
+
VALUE report, strict;
|
|
264
274
|
ip_context_t *ctx;
|
|
265
275
|
} ip_compress_pixels_call_t;
|
|
266
276
|
|
|
267
277
|
typedef struct {
|
|
268
278
|
VALUE self, input, input_kind, output, output_kind, progressive, strip_metadata;
|
|
269
|
-
VALUE execution, cancellable, has_scheduler;
|
|
279
|
+
VALUE execution, cancellable, has_scheduler, strict;
|
|
270
280
|
ip_context_t *ctx;
|
|
271
281
|
} ip_optimize_jpeg_call_t;
|
|
272
282
|
|
|
@@ -287,15 +297,13 @@ static VALUE ip_call_cleanup(VALUE ptr) {
|
|
|
287
297
|
static VALUE ip_compress_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
|
|
288
298
|
VALUE output_kind, VALUE algo, VALUE quality, VALUE min_ssim,
|
|
289
299
|
VALUE mozjpeg_trellis, VALUE progressive, VALUE strip_metadata,
|
|
290
|
-
VALUE execution, VALUE cancellable, VALUE has_scheduler
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
VALUE quality, VALUE min_ssim, VALUE mozjpeg_trellis,
|
|
294
|
-
VALUE progressive, VALUE exact_size, VALUE execution,
|
|
295
|
-
VALUE cancellable, VALUE has_scheduler);
|
|
300
|
+
VALUE execution, VALUE cancellable, VALUE has_scheduler,
|
|
301
|
+
VALUE report, VALUE strict);
|
|
302
|
+
static VALUE ip_compress_pixels_entry(int argc, VALUE *argv, VALUE self);
|
|
296
303
|
static VALUE ip_optimize_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
|
|
297
304
|
VALUE output_kind, VALUE progressive, VALUE strip_metadata,
|
|
298
|
-
VALUE execution, VALUE cancellable, VALUE has_scheduler
|
|
305
|
+
VALUE execution, VALUE cancellable, VALUE has_scheduler,
|
|
306
|
+
VALUE strict);
|
|
299
307
|
|
|
300
308
|
static VALUE ip_status_to_exception(ip_status_t status) {
|
|
301
309
|
switch (status) {
|
|
@@ -543,10 +551,43 @@ static VALUE ip_sym(const char *name) {
|
|
|
543
551
|
return ID2SYM(rb_intern(name));
|
|
544
552
|
}
|
|
545
553
|
|
|
554
|
+
static void ip_jpeg_emit_message_collect(j_common_ptr cinfo, int msg_level) {
|
|
555
|
+
if (msg_level >= 0)
|
|
556
|
+
return;
|
|
557
|
+
|
|
558
|
+
ip_jpeg_error_mgr *err = (ip_jpeg_error_mgr *)cinfo->err;
|
|
559
|
+
ip_context_t *ctx = err->ctx;
|
|
560
|
+
if (!ctx)
|
|
561
|
+
return;
|
|
562
|
+
|
|
563
|
+
ctx->warning_count++;
|
|
564
|
+
if (ctx->first_warning[0] == '\0') {
|
|
565
|
+
char buffer[JMSG_LENGTH_MAX];
|
|
566
|
+
(*cinfo->err->format_message)(cinfo, buffer);
|
|
567
|
+
snprintf(ctx->first_warning, sizeof(ctx->first_warning), "%s", buffer);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (ctx->strict) {
|
|
571
|
+
if (ctx->status == IP_OK) {
|
|
572
|
+
char buffer[JMSG_LENGTH_MAX];
|
|
573
|
+
(*cinfo->err->format_message)(cinfo, buffer);
|
|
574
|
+
ip_context_set_error(ctx, IP_ERR_INVALID_IMAGE, buffer);
|
|
575
|
+
}
|
|
576
|
+
if (ctx->jmp_armed)
|
|
577
|
+
longjmp(ctx->jmpbuf, 1);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
static void ip_jpeg_output_message_silent(j_common_ptr cinfo) {
|
|
582
|
+
(void)cinfo;
|
|
583
|
+
}
|
|
584
|
+
|
|
546
585
|
static struct jpeg_error_mgr *ip_use_error(ip_jpeg_error_mgr *jerr, ip_context_t *ctx,
|
|
547
586
|
void (*handler)(j_common_ptr)) {
|
|
548
587
|
struct jpeg_error_mgr *base = jpeg_std_error(&jerr->pub);
|
|
549
588
|
jerr->pub.error_exit = handler;
|
|
589
|
+
jerr->pub.emit_message = ip_jpeg_emit_message_collect;
|
|
590
|
+
jerr->pub.output_message = ip_jpeg_output_message_silent;
|
|
550
591
|
jerr->ctx = ctx;
|
|
551
592
|
return base;
|
|
552
593
|
}
|
|
@@ -876,6 +917,23 @@ static VALUE ip_finish_output(ip_context_t *ctx, ip_output_kind_t kind) {
|
|
|
876
917
|
return Qtrue;
|
|
877
918
|
}
|
|
878
919
|
|
|
920
|
+
static VALUE ip_build_report(ip_context_t *ctx, VALUE output_value) {
|
|
921
|
+
VALUE hash = rb_hash_new();
|
|
922
|
+
rb_hash_aset(hash, ip_sym("output"), output_value);
|
|
923
|
+
rb_hash_aset(hash, ip_sym("quality"), INT2NUM(ctx->selected_quality));
|
|
924
|
+
rb_hash_aset(hash, ip_sym("ssim"),
|
|
925
|
+
ctx->ssim_guard_enabled ? DBL2NUM(ctx->measured_ssim) : Qnil);
|
|
926
|
+
rb_hash_aset(hash, ip_sym("algo"),
|
|
927
|
+
ctx->algo == IP_ALGO_MOZJPEG ? ip_sym("mozjpeg") : ip_sym("jpeg_turbo"));
|
|
928
|
+
rb_hash_aset(hash, ip_sym("bytesize"), SIZET2NUM(ctx->output_size));
|
|
929
|
+
rb_hash_aset(hash, ip_sym("input_bytesize"),
|
|
930
|
+
SIZET2NUM(ctx->input_size > 0 ? ctx->input_size : ctx->pixel_size));
|
|
931
|
+
rb_hash_aset(hash, ip_sym("warning_count"), INT2NUM(ctx->warning_count));
|
|
932
|
+
rb_hash_aset(hash, ip_sym("warning"),
|
|
933
|
+
ctx->first_warning[0] ? rb_str_new_cstr(ctx->first_warning) : Qnil);
|
|
934
|
+
return hash;
|
|
935
|
+
}
|
|
936
|
+
|
|
879
937
|
static int ip_save_marker(ip_context_t *ctx, int marker, const unsigned char *data,
|
|
880
938
|
unsigned int len) {
|
|
881
939
|
if (ctx->preserved_marker_count == ctx->preserved_marker_capacity) {
|
|
@@ -1287,7 +1345,7 @@ static int encode_pixels_with_libjpeg(ip_context_t *ctx, int mozjpeg_size_mode)
|
|
|
1287
1345
|
ip_write_preserved_markers(ctx, &cinfo);
|
|
1288
1346
|
|
|
1289
1347
|
while (cinfo.next_scanline < cinfo.image_height) {
|
|
1290
|
-
if (
|
|
1348
|
+
if (atomic_load(&ctx->cancelled))
|
|
1291
1349
|
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "JPEG encode cancelled");
|
|
1292
1350
|
|
|
1293
1351
|
JSAMPROW rows[16];
|
|
@@ -1398,7 +1456,7 @@ static int ip_jpeg_decode_to_pixels(ip_context_t *ctx, unsigned char **pixels, i
|
|
|
1398
1456
|
IP_FAIL_GOTO(ctx, IP_ERR_OOM, "failed to allocate decoded pixel buffer");
|
|
1399
1457
|
|
|
1400
1458
|
while (cinfo.output_scanline < cinfo.output_height) {
|
|
1401
|
-
if (
|
|
1459
|
+
if (atomic_load(&ctx->cancelled))
|
|
1402
1460
|
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "JPEG decode cancelled");
|
|
1403
1461
|
|
|
1404
1462
|
JSAMPROW rows[16];
|
|
@@ -1558,7 +1616,7 @@ static int ip_decode_jpeg_to_luma_buffer(ip_context_t *ctx, const unsigned char
|
|
|
1558
1616
|
IP_FAIL_GOTO(ctx, IP_ERR_OOM, "failed to allocate luma buffer");
|
|
1559
1617
|
|
|
1560
1618
|
while (cinfo.output_scanline < cinfo.output_height) {
|
|
1561
|
-
if (
|
|
1619
|
+
if (atomic_load(&ctx->cancelled))
|
|
1562
1620
|
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "JPEG luma decode cancelled");
|
|
1563
1621
|
|
|
1564
1622
|
JSAMPROW rows[16];
|
|
@@ -1791,8 +1849,7 @@ static int guarded_compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_
|
|
|
1791
1849
|
}
|
|
1792
1850
|
|
|
1793
1851
|
if (reference_channels == 4) {
|
|
1794
|
-
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
1795
|
-
"min_ssim is not supported for RGBA input in v0.2.2");
|
|
1852
|
+
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED, "min_ssim is not supported for RGBA input");
|
|
1796
1853
|
return 0;
|
|
1797
1854
|
}
|
|
1798
1855
|
|
|
@@ -1811,7 +1868,7 @@ static int guarded_compress_jpeg_input_with_mode(ip_context_t *ctx, int mozjpeg_
|
|
|
1811
1868
|
int best_seen_quality = 0;
|
|
1812
1869
|
|
|
1813
1870
|
while (search_low <= search_high) {
|
|
1814
|
-
if (
|
|
1871
|
+
if (atomic_load(&ctx->cancelled)) {
|
|
1815
1872
|
free(reference_luma);
|
|
1816
1873
|
free(best_jpeg);
|
|
1817
1874
|
ip_context_set_error(ctx, IP_ERR_CANCELLED, "SSIM-guarded JPEG encode cancelled");
|
|
@@ -1959,7 +2016,7 @@ static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
|
1959
2016
|
jpeg_mem_src(&srcinfo, ctx->input_data, (unsigned long)ctx->input_size);
|
|
1960
2017
|
ip_setup_marker_saving(&srcinfo, ctx->strip_metadata);
|
|
1961
2018
|
|
|
1962
|
-
if (
|
|
2019
|
+
if (atomic_load(&ctx->cancelled))
|
|
1963
2020
|
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "lossless JPEG optimize cancelled");
|
|
1964
2021
|
|
|
1965
2022
|
if (jpeg_read_header(&srcinfo, TRUE) != JPEG_HEADER_OK)
|
|
@@ -1989,7 +2046,7 @@ static int ip_lossless_optimize_jpeg(ip_context_t *ctx) {
|
|
|
1989
2046
|
|
|
1990
2047
|
jpeg_mem_dest(&dstinfo, &ctx->transient_jpeg_buf, &jpeg_size);
|
|
1991
2048
|
|
|
1992
|
-
if (
|
|
2049
|
+
if (atomic_load(&ctx->cancelled))
|
|
1993
2050
|
IP_FAIL_GOTO(ctx, IP_ERR_CANCELLED, "lossless JPEG optimize cancelled");
|
|
1994
2051
|
|
|
1995
2052
|
jpeg_write_coefficients(&dstinfo, coef_arrays);
|
|
@@ -2035,7 +2092,7 @@ static int ip_mozjpeg_compress(ip_context_t *ctx) {
|
|
|
2035
2092
|
|
|
2036
2093
|
static ip_execution_t ip_async_execution(const ip_context_t *ctx) {
|
|
2037
2094
|
#if IMAGE_PACK_HAS_OFFLOAD_SAFE
|
|
2038
|
-
return ctx->has_scheduler ? IP_EXEC_OFFLOAD : IP_EXEC_NOGVL;
|
|
2095
|
+
return (ip_offload_runtime_enabled && ctx->has_scheduler) ? IP_EXEC_OFFLOAD : IP_EXEC_NOGVL;
|
|
2039
2096
|
#else
|
|
2040
2097
|
(void)ctx;
|
|
2041
2098
|
return IP_EXEC_NOGVL;
|
|
@@ -2105,12 +2162,16 @@ static int ip_run_context(ip_context_t *ctx) {
|
|
|
2105
2162
|
rb_nogvl(ip_run_encode_nogvl, ctx, ip_unblock_function, ctx, RB_NOGVL_OFFLOAD_SAFE);
|
|
2106
2163
|
#else
|
|
2107
2164
|
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
2108
|
-
"offload execution
|
|
2165
|
+
"offload execution is unavailable in this runtime; it requires Ruby "
|
|
2166
|
+
">= 3.4 and IMAGE_PACK_DISABLE_OFFLOAD must not be set");
|
|
2109
2167
|
#endif
|
|
2110
2168
|
} else {
|
|
2111
2169
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "invalid resolved execution mode");
|
|
2112
2170
|
}
|
|
2113
2171
|
|
|
2172
|
+
if (ctx->resolved_execution != IP_EXEC_DIRECT)
|
|
2173
|
+
rb_thread_check_ints();
|
|
2174
|
+
|
|
2114
2175
|
return ctx->status == IP_OK;
|
|
2115
2176
|
}
|
|
2116
2177
|
|
|
@@ -2133,12 +2194,16 @@ static int ip_run_optimize_context(ip_context_t *ctx) {
|
|
|
2133
2194
|
rb_nogvl(ip_run_optimize_nogvl, ctx, ip_unblock_function, ctx, RB_NOGVL_OFFLOAD_SAFE);
|
|
2134
2195
|
#else
|
|
2135
2196
|
ip_context_set_error(ctx, IP_ERR_UNSUPPORTED,
|
|
2136
|
-
"offload execution
|
|
2197
|
+
"offload execution is unavailable in this runtime; it requires Ruby "
|
|
2198
|
+
">= 3.4 and IMAGE_PACK_DISABLE_OFFLOAD must not be set");
|
|
2137
2199
|
#endif
|
|
2138
2200
|
} else {
|
|
2139
2201
|
ip_context_set_error(ctx, IP_ERR_INVALID_ARGUMENT, "invalid resolved execution mode");
|
|
2140
2202
|
}
|
|
2141
2203
|
|
|
2204
|
+
if (ctx->resolved_execution != IP_EXEC_DIRECT)
|
|
2205
|
+
rb_thread_check_ints();
|
|
2206
|
+
|
|
2142
2207
|
return ctx->status == IP_OK;
|
|
2143
2208
|
}
|
|
2144
2209
|
|
|
@@ -2220,6 +2285,7 @@ static VALUE ip_compress_jpeg_entry_body(VALUE ptr) {
|
|
|
2220
2285
|
ctx->requested_execution = ip_parse_execution(call->execution);
|
|
2221
2286
|
ctx->cancellable_requested = ip_bool_value(call->cancellable);
|
|
2222
2287
|
ctx->has_scheduler = ip_bool_value(call->has_scheduler);
|
|
2288
|
+
ctx->strict = ip_bool_value(call->strict);
|
|
2223
2289
|
apply_configuration(call->self, ctx);
|
|
2224
2290
|
|
|
2225
2291
|
ip_input_kind_t in_kind = ip_parse_input_kind(call->input_kind);
|
|
@@ -2242,17 +2308,23 @@ static VALUE ip_compress_jpeg_entry_body(VALUE ptr) {
|
|
|
2242
2308
|
}
|
|
2243
2309
|
|
|
2244
2310
|
ip_run_context(ctx);
|
|
2311
|
+
if (ip_bool_value(call->report)) {
|
|
2312
|
+
VALUE output_value = ip_finish_output(ctx, out_kind);
|
|
2313
|
+
return ip_build_report(ctx, output_value);
|
|
2314
|
+
}
|
|
2245
2315
|
return ip_finish_output(ctx, out_kind);
|
|
2246
2316
|
}
|
|
2247
2317
|
|
|
2248
2318
|
static VALUE ip_compress_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
|
|
2249
2319
|
VALUE output_kind, VALUE algo, VALUE quality, VALUE min_ssim,
|
|
2250
2320
|
VALUE mozjpeg_trellis, VALUE progressive, VALUE strip_metadata,
|
|
2251
|
-
VALUE execution, VALUE cancellable, VALUE has_scheduler
|
|
2321
|
+
VALUE execution, VALUE cancellable, VALUE has_scheduler,
|
|
2322
|
+
VALUE report, VALUE strict) {
|
|
2252
2323
|
ip_compress_jpeg_call_t call = {
|
|
2253
2324
|
self, input, input_kind, output, output_kind,
|
|
2254
2325
|
algo, quality, min_ssim, mozjpeg_trellis, progressive,
|
|
2255
|
-
strip_metadata, execution, cancellable, has_scheduler,
|
|
2326
|
+
strip_metadata, execution, cancellable, has_scheduler, report,
|
|
2327
|
+
strict, NULL};
|
|
2256
2328
|
return rb_ensure(ip_compress_jpeg_entry_body, (VALUE)&call, ip_call_cleanup, (VALUE)&call.ctx);
|
|
2257
2329
|
}
|
|
2258
2330
|
|
|
@@ -2277,6 +2349,7 @@ static VALUE ip_compress_pixels_entry_body(VALUE ptr) {
|
|
|
2277
2349
|
ctx->requested_execution = ip_parse_execution(call->execution);
|
|
2278
2350
|
ctx->cancellable_requested = ip_bool_value(call->cancellable);
|
|
2279
2351
|
ctx->has_scheduler = ip_bool_value(call->has_scheduler);
|
|
2352
|
+
ctx->strict = ip_bool_value(call->strict);
|
|
2280
2353
|
apply_configuration(call->self, ctx);
|
|
2281
2354
|
|
|
2282
2355
|
if (!ip_prepare_output_path(ctx, call->output, out_kind) ||
|
|
@@ -2297,18 +2370,19 @@ static VALUE ip_compress_pixels_entry_body(VALUE ptr) {
|
|
|
2297
2370
|
}
|
|
2298
2371
|
|
|
2299
2372
|
ip_run_context(ctx);
|
|
2373
|
+
if (ip_bool_value(call->report)) {
|
|
2374
|
+
VALUE output_value = ip_finish_output(ctx, out_kind);
|
|
2375
|
+
return ip_build_report(ctx, output_value);
|
|
2376
|
+
}
|
|
2300
2377
|
return ip_finish_output(ctx, out_kind);
|
|
2301
2378
|
}
|
|
2302
2379
|
|
|
2303
|
-
static VALUE ip_compress_pixels_entry(
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
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};
|
|
2380
|
+
static VALUE ip_compress_pixels_entry(int argc, VALUE *argv, VALUE self) {
|
|
2381
|
+
rb_check_arity(argc, 17, 17);
|
|
2382
|
+
ip_compress_pixels_call_t call = {self, argv[0], argv[1], argv[2], argv[3],
|
|
2383
|
+
argv[4], argv[5], argv[6], argv[7], argv[8],
|
|
2384
|
+
argv[9], argv[10], argv[11], argv[12], argv[13],
|
|
2385
|
+
argv[14], argv[15], argv[16], NULL};
|
|
2312
2386
|
return rb_ensure(ip_compress_pixels_entry_body, (VALUE)&call, ip_call_cleanup,
|
|
2313
2387
|
(VALUE)&call.ctx);
|
|
2314
2388
|
}
|
|
@@ -2326,6 +2400,7 @@ static VALUE ip_optimize_jpeg_entry_body(VALUE ptr) {
|
|
|
2326
2400
|
ctx->requested_execution = ip_parse_execution(call->execution);
|
|
2327
2401
|
ctx->cancellable_requested = ip_bool_value(call->cancellable);
|
|
2328
2402
|
ctx->has_scheduler = ip_bool_value(call->has_scheduler);
|
|
2403
|
+
ctx->strict = ip_bool_value(call->strict);
|
|
2329
2404
|
ctx->ssim_guard_enabled = 0;
|
|
2330
2405
|
apply_configuration(call->self, ctx);
|
|
2331
2406
|
|
|
@@ -2354,10 +2429,11 @@ static VALUE ip_optimize_jpeg_entry_body(VALUE ptr) {
|
|
|
2354
2429
|
|
|
2355
2430
|
static VALUE ip_optimize_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
|
|
2356
2431
|
VALUE output_kind, VALUE progressive, VALUE strip_metadata,
|
|
2357
|
-
VALUE execution, VALUE cancellable, VALUE has_scheduler
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2432
|
+
VALUE execution, VALUE cancellable, VALUE has_scheduler,
|
|
2433
|
+
VALUE strict) {
|
|
2434
|
+
ip_optimize_jpeg_call_t call = {self, input, input_kind, output,
|
|
2435
|
+
output_kind, progressive, strip_metadata, execution,
|
|
2436
|
+
cancellable, has_scheduler, strict, NULL};
|
|
2361
2437
|
return rb_ensure(ip_optimize_jpeg_entry_body, (VALUE)&call, ip_call_cleanup, (VALUE)&call.ctx);
|
|
2362
2438
|
}
|
|
2363
2439
|
|
|
@@ -2403,14 +2479,17 @@ IMAGE_PACK_INIT_EXPORT void Init_image_pack(void) {
|
|
|
2403
2479
|
for (size_t i = 0; i < IP_ARRAY_LEN(exceptions); i++)
|
|
2404
2480
|
*exceptions[i].slot = rb_const_get(rb_mImagePack, rb_intern(exceptions[i].name));
|
|
2405
2481
|
|
|
2406
|
-
rb_define_const(rb_mImagePack, "NATIVE_MOZJPEG_VERSION",
|
|
2482
|
+
rb_define_const(rb_mImagePack, "NATIVE_MOZJPEG_VERSION",
|
|
2483
|
+
rb_str_new_cstr(IMAGE_PACK_MOZJPEG_VERSION));
|
|
2407
2484
|
#if defined(IMAGE_PACK_HAS_SIMD)
|
|
2408
2485
|
rb_define_const(rb_mImagePack, "NATIVE_SIMD", Qtrue);
|
|
2409
2486
|
#else
|
|
2410
2487
|
rb_define_const(rb_mImagePack, "NATIVE_SIMD", Qfalse);
|
|
2411
2488
|
#endif
|
|
2489
|
+
ip_offload_runtime_enabled = (getenv("IMAGE_PACK_DISABLE_OFFLOAD") == NULL) ? 1 : 0;
|
|
2412
2490
|
#if IMAGE_PACK_HAS_OFFLOAD_SAFE
|
|
2413
|
-
rb_define_const(rb_mImagePack, "NATIVE_OFFLOAD_SAFE",
|
|
2491
|
+
rb_define_const(rb_mImagePack, "NATIVE_OFFLOAD_SAFE",
|
|
2492
|
+
ip_offload_runtime_enabled ? Qtrue : Qfalse);
|
|
2414
2493
|
#else
|
|
2415
2494
|
rb_define_const(rb_mImagePack, "NATIVE_OFFLOAD_SAFE", Qfalse);
|
|
2416
2495
|
#endif
|
|
@@ -2419,9 +2498,9 @@ IMAGE_PACK_INIT_EXPORT void Init_image_pack(void) {
|
|
|
2419
2498
|
const char *name;
|
|
2420
2499
|
VALUE (*fn)(ANYARGS);
|
|
2421
2500
|
int arity;
|
|
2422
|
-
} methods[] = {{"__compress_jpeg", (VALUE (*)(ANYARGS))ip_compress_jpeg_entry,
|
|
2423
|
-
{"__compress_pixels", (VALUE (*)(ANYARGS))ip_compress_pixels_entry,
|
|
2424
|
-
{"__optimize_jpeg", (VALUE (*)(ANYARGS))ip_optimize_jpeg_entry,
|
|
2501
|
+
} methods[] = {{"__compress_jpeg", (VALUE (*)(ANYARGS))ip_compress_jpeg_entry, 15},
|
|
2502
|
+
{"__compress_pixels", (VALUE (*)(ANYARGS))ip_compress_pixels_entry, -1},
|
|
2503
|
+
{"__optimize_jpeg", (VALUE (*)(ANYARGS))ip_optimize_jpeg_entry, 10},
|
|
2425
2504
|
{"__inspect_image", (VALUE (*)(ANYARGS))ip_inspect_image_entry, 2}};
|
|
2426
2505
|
for (size_t i = 0; i < IP_ARRAY_LEN(methods); i++) {
|
|
2427
2506
|
rb_define_singleton_method(rb_mImagePack, methods[i].name, methods[i].fn, methods[i].arity);
|
|
@@ -42,7 +42,7 @@ module ImagePack
|
|
|
42
42
|
|
|
43
43
|
if value == :offload && ImagePack.respond_to?(:offload_safe?) && !ImagePack.offload_safe?
|
|
44
44
|
raise UnsupportedError,
|
|
45
|
-
"execution: :offload requires Ruby >= 3.4.0
|
|
45
|
+
"execution: :offload is unavailable in this runtime; it requires Ruby >= 3.4.0 and IMAGE_PACK_DISABLE_OFFLOAD must not be set"
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
@execution = value
|
data/lib/image_pack/version.rb
CHANGED
data/lib/image_pack.rb
CHANGED
|
@@ -90,10 +90,12 @@ module ImagePack
|
|
|
90
90
|
progressive: true,
|
|
91
91
|
strip_metadata: false,
|
|
92
92
|
execution: nil,
|
|
93
|
-
cancellable: false
|
|
93
|
+
cancellable: false,
|
|
94
|
+
strict: false)
|
|
94
95
|
validate_boolean!(:progressive, progressive)
|
|
95
96
|
validate_boolean!(:strip_metadata, strip_metadata)
|
|
96
97
|
validate_boolean!(:cancellable, cancellable)
|
|
98
|
+
validate_boolean!(:strict, strict)
|
|
97
99
|
execution ||= configuration.execution
|
|
98
100
|
validate_execution!(execution)
|
|
99
101
|
validate_execution_supported!(execution)
|
|
@@ -109,7 +111,8 @@ module ImagePack
|
|
|
109
111
|
strip_metadata ? 1 : 0,
|
|
110
112
|
execution,
|
|
111
113
|
cancellable ? 1 : 0,
|
|
112
|
-
has_scheduler ? 1 : 0
|
|
114
|
+
has_scheduler ? 1 : 0,
|
|
115
|
+
strict ? 1 : 0)
|
|
113
116
|
end
|
|
114
117
|
|
|
115
118
|
def compress(input,
|
|
@@ -121,13 +124,17 @@ module ImagePack
|
|
|
121
124
|
progressive: false,
|
|
122
125
|
strip_metadata: true,
|
|
123
126
|
execution: nil,
|
|
124
|
-
cancellable: false
|
|
127
|
+
cancellable: false,
|
|
128
|
+
report: false,
|
|
129
|
+
strict: false)
|
|
125
130
|
validate_algo!(algo)
|
|
126
131
|
validate_min_ssim!(min_ssim)
|
|
127
132
|
validate_boolean!(:mozjpeg_trellis, mozjpeg_trellis)
|
|
128
133
|
validate_boolean!(:progressive, progressive)
|
|
129
134
|
validate_boolean!(:strip_metadata, strip_metadata)
|
|
130
135
|
validate_boolean!(:cancellable, cancellable)
|
|
136
|
+
validate_boolean!(:report, report)
|
|
137
|
+
validate_boolean!(:strict, strict)
|
|
131
138
|
quality_was_given = !quality.nil?
|
|
132
139
|
effective_quality = quality_was_given ? quality : DEFAULT_QUALITY
|
|
133
140
|
effective_quality = 1 if min_ssim && !quality_was_given
|
|
@@ -150,7 +157,9 @@ module ImagePack
|
|
|
150
157
|
strip_metadata ? 1 : 0,
|
|
151
158
|
execution,
|
|
152
159
|
cancellable ? 1 : 0,
|
|
153
|
-
has_scheduler ? 1 : 0
|
|
160
|
+
has_scheduler ? 1 : 0,
|
|
161
|
+
report ? 1 : 0,
|
|
162
|
+
strict ? 1 : 0)
|
|
154
163
|
end
|
|
155
164
|
|
|
156
165
|
def compress_pixels(buffer,
|
|
@@ -166,7 +175,9 @@ module ImagePack
|
|
|
166
175
|
drop_alpha: nil,
|
|
167
176
|
exact_size: false,
|
|
168
177
|
execution: nil,
|
|
169
|
-
cancellable: false
|
|
178
|
+
cancellable: false,
|
|
179
|
+
report: false,
|
|
180
|
+
strict: false)
|
|
170
181
|
validate_pixel_buffer!(buffer)
|
|
171
182
|
validate_algo!(algo)
|
|
172
183
|
validate_min_ssim!(min_ssim)
|
|
@@ -175,6 +186,8 @@ module ImagePack
|
|
|
175
186
|
validate_drop_alpha!(drop_alpha)
|
|
176
187
|
validate_boolean!(:exact_size, exact_size)
|
|
177
188
|
validate_boolean!(:cancellable, cancellable)
|
|
189
|
+
validate_boolean!(:report, report)
|
|
190
|
+
validate_boolean!(:strict, strict)
|
|
178
191
|
quality_was_given = !quality.nil?
|
|
179
192
|
effective_quality = quality_was_given ? quality : DEFAULT_QUALITY
|
|
180
193
|
effective_quality = 1 if min_ssim && !quality_was_given
|
|
@@ -214,7 +227,9 @@ module ImagePack
|
|
|
214
227
|
exact_size ? 1 : 0,
|
|
215
228
|
execution,
|
|
216
229
|
cancellable ? 1 : 0,
|
|
217
|
-
has_scheduler ? 1 : 0
|
|
230
|
+
has_scheduler ? 1 : 0,
|
|
231
|
+
report ? 1 : 0,
|
|
232
|
+
strict ? 1 : 0)
|
|
218
233
|
end
|
|
219
234
|
|
|
220
235
|
def inspect_image(input)
|
|
@@ -251,7 +266,7 @@ module ImagePack
|
|
|
251
266
|
return :return_string if output.nil?
|
|
252
267
|
return :path if output.is_a?(String) || output.is_a?(Pathname)
|
|
253
268
|
|
|
254
|
-
raise InvalidArgumentError, "output must be nil, String path, or Pathname
|
|
269
|
+
raise InvalidArgumentError, "output must be nil, String path, or Pathname"
|
|
255
270
|
end
|
|
256
271
|
|
|
257
272
|
def validate_algo!(algo)
|
|
@@ -296,7 +311,7 @@ module ImagePack
|
|
|
296
311
|
return if offload_safe?
|
|
297
312
|
|
|
298
313
|
raise UnsupportedError,
|
|
299
|
-
"execution: :offload requires Ruby >= 3.4.0
|
|
314
|
+
"execution: :offload is unavailable in this runtime; it requires Ruby >= 3.4.0 and IMAGE_PACK_DISABLE_OFFLOAD must not be set"
|
|
300
315
|
end
|
|
301
316
|
|
|
302
317
|
def validate_dimensions!(width, height, channels)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: image_pack
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Roman Haydarov
|
|
@@ -80,9 +80,9 @@ dependencies:
|
|
|
80
80
|
- - "~>"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '2.21'
|
|
83
|
-
description: Single API, vendored pure-C MozJPEG/libjpeg codec,
|
|
84
|
-
3.1+ native JPEG execution; Ruby 3.4+ enables Fiber::Scheduler-aware
|
|
85
|
-
vendored C sources — no system libjpeg, git, or CMake required.
|
|
83
|
+
description: Single API, vendored pure-C MozJPEG/libjpeg codec, in-memory compression
|
|
84
|
+
and atomic file output, Ruby 3.1+ native JPEG execution; Ruby 3.4+ enables Fiber::Scheduler-aware
|
|
85
|
+
offload. Ships vendored C sources — no system libjpeg, git, or CMake required.
|
|
86
86
|
email:
|
|
87
87
|
- romnhajdarov@gmail.com
|
|
88
88
|
executables: []
|