image_pack 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +108 -119
- data/ext/image_pack/extconf.rb +36 -125
- data/ext/image_pack/image_pack.c +736 -614
- data/ext/image_pack/mozjpeg_sources.rb +178 -0
- data/ext/image_pack/vendor/mozjpeg/BUILDING.md +744 -0
- data/ext/image_pack/vendor/mozjpeg/CODE_OF_CONDUCT.md +15 -0
- data/ext/image_pack/vendor/mozjpeg/ChangeLog.md +1996 -0
- data/lib/image_pack/configuration.rb +54 -8
- data/lib/image_pack/version.rb +1 -1
- data/lib/image_pack.rb +87 -25
- metadata +14 -79
- data/ext/image_pack/vendor/mozjpeg/README.ijg +0 -258
- data/ext/image_pack/vendor/mozjpeg/cdjpeg.c +0 -156
- data/ext/image_pack/vendor/mozjpeg/cjpeg.c +0 -961
- data/ext/image_pack/vendor/mozjpeg/djpeg.c +0 -855
- data/ext/image_pack/vendor/mozjpeg/jaricom.c +0 -157
- data/ext/image_pack/vendor/mozjpeg/jcarith.c +0 -972
- data/ext/image_pack/vendor/mozjpeg/jcstest.c +0 -126
- data/ext/image_pack/vendor/mozjpeg/jdarith.c +0 -782
- data/ext/image_pack/vendor/mozjpeg/jdatadst-tj.c +0 -198
- data/ext/image_pack/vendor/mozjpeg/jdatasrc-tj.c +0 -194
- data/ext/image_pack/vendor/mozjpeg/jpegtran.c +0 -827
- data/ext/image_pack/vendor/mozjpeg/jpegyuv.c +0 -172
- data/ext/image_pack/vendor/mozjpeg/rdbmp.c +0 -690
- data/ext/image_pack/vendor/mozjpeg/rdcolmap.c +0 -253
- data/ext/image_pack/vendor/mozjpeg/rdgif.c +0 -720
- data/ext/image_pack/vendor/mozjpeg/rdjpeg.c +0 -160
- data/ext/image_pack/vendor/mozjpeg/rdjpgcom.c +0 -494
- data/ext/image_pack/vendor/mozjpeg/rdpng.c +0 -194
- data/ext/image_pack/vendor/mozjpeg/rdppm.c +0 -781
- data/ext/image_pack/vendor/mozjpeg/rdswitch.c +0 -642
- data/ext/image_pack/vendor/mozjpeg/rdtarga.c +0 -508
- data/ext/image_pack/vendor/mozjpeg/simd/arm/aarch32/jccolext-neon.c +0 -148
- data/ext/image_pack/vendor/mozjpeg/simd/arm/aarch32/jchuff-neon.c +0 -334
- data/ext/image_pack/vendor/mozjpeg/simd/arm/aarch32/jsimd.c +0 -976
- data/ext/image_pack/vendor/mozjpeg/simd/i386/jsimd.c +0 -1312
- data/ext/image_pack/vendor/mozjpeg/simd/mips/jsimd.c +0 -1143
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jccolext-mmi.c +0 -455
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jccolor-mmi.c +0 -148
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jcgray-mmi.c +0 -132
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jcgryext-mmi.c +0 -374
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jcsample-mmi.c +0 -98
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jdcolext-mmi.c +0 -415
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jdcolor-mmi.c +0 -139
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jdmerge-mmi.c +0 -149
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jdmrgext-mmi.c +0 -615
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jdsample-mmi.c +0 -304
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jfdctfst-mmi.c +0 -255
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jfdctint-mmi.c +0 -398
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jidctfst-mmi.c +0 -395
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jidctint-mmi.c +0 -571
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jquanti-mmi.c +0 -124
- data/ext/image_pack/vendor/mozjpeg/simd/mips64/jsimd.c +0 -866
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jccolext-altivec.c +0 -269
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jccolor-altivec.c +0 -116
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jcgray-altivec.c +0 -111
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jcgryext-altivec.c +0 -228
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jcsample-altivec.c +0 -159
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jdcolext-altivec.c +0 -276
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jdcolor-altivec.c +0 -106
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jdmerge-altivec.c +0 -130
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jdmrgext-altivec.c +0 -329
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jdsample-altivec.c +0 -400
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jfdctfst-altivec.c +0 -154
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jfdctint-altivec.c +0 -258
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jidctfst-altivec.c +0 -255
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jidctint-altivec.c +0 -357
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jquanti-altivec.c +0 -250
- data/ext/image_pack/vendor/mozjpeg/simd/powerpc/jsimd.c +0 -884
- data/ext/image_pack/vendor/mozjpeg/strtest.c +0 -170
- data/ext/image_pack/vendor/mozjpeg/tjbench.c +0 -1044
- data/ext/image_pack/vendor/mozjpeg/tjexample.c +0 -406
- data/ext/image_pack/vendor/mozjpeg/tjunittest.c +0 -961
- data/ext/image_pack/vendor/mozjpeg/tjutil.c +0 -70
- data/ext/image_pack/vendor/mozjpeg/transupp.c +0 -2373
- data/ext/image_pack/vendor/mozjpeg/turbojpeg-jni.c +0 -1259
- data/ext/image_pack/vendor/mozjpeg/turbojpeg.c +0 -2320
- data/ext/image_pack/vendor/mozjpeg/wrbmp.c +0 -552
- data/ext/image_pack/vendor/mozjpeg/wrgif.c +0 -580
- data/ext/image_pack/vendor/mozjpeg/wrjpgcom.c +0 -577
- data/ext/image_pack/vendor/mozjpeg/wrppm.c +0 -366
- data/ext/image_pack/vendor/mozjpeg/wrtarga.c +0 -258
- data/ext/image_pack/vendor/mozjpeg/yuvjpeg.c +0 -268
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,5 +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
|
+
|
|
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
|
+
|
|
3
20
|
## 0.2.2
|
|
4
21
|
|
|
5
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,180 +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
|
-
|
|
20
|
+
small = ImagePack.compress_bytes(jpeg, quality: 82)
|
|
21
|
+
File.binwrite("photo.small.jpg", small)
|
|
46
22
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
# Start at quality 75, but raise quality if needed to satisfy min_ssim.
|
|
52
|
-
ImagePack.compress(jpeg, algo: :mozjpeg, quality: 75, min_ssim: 0.985)
|
|
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)
|
|
33
|
+
ImagePack.optimize_file("photo.jpg", output: "out.jpg")
|
|
34
|
+
```
|
|
63
35
|
|
|
64
|
-
|
|
65
|
-
width: 1920,
|
|
66
|
-
height: 1080,
|
|
67
|
-
channels: 3,
|
|
68
|
-
algo: :mozjpeg
|
|
69
|
-
)
|
|
36
|
+
## Compression
|
|
70
37
|
|
|
71
|
-
|
|
72
|
-
|
|
38
|
+
```ruby
|
|
39
|
+
ImagePack.compress_bytes(jpeg,
|
|
40
|
+
algo: :size,
|
|
41
|
+
quality: 82,
|
|
42
|
+
strip_metadata: true
|
|
43
|
+
)
|
|
73
44
|
```
|
|
74
45
|
|
|
75
|
-
|
|
46
|
+
Algorithms:
|
|
76
47
|
|
|
77
|
-
`
|
|
48
|
+
- `:size` / `:mozjpeg` — smaller files, default
|
|
49
|
+
- `:fast` / `:jpeg_turbo` — faster mode
|
|
78
50
|
|
|
79
|
-
|
|
80
|
-
- non-binary `String` that points to an existing file — file path;
|
|
81
|
-
- `Pathname` — file path;
|
|
82
|
-
- `IO::Buffer` — JPEG bytes, copied before native no-GVL execution for safety.
|
|
51
|
+
Common options:
|
|
83
52
|
|
|
84
|
-
|
|
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
|
+
```
|
|
85
59
|
|
|
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.
|
|
60
|
+
`min_ssim:` searches for the lowest acceptable quality using a fast native luma SSIM guard.
|
|
88
61
|
|
|
89
|
-
`
|
|
90
|
-
Streaming output is intentionally not supported in v0.2.2.
|
|
62
|
+
`strict: true` raises `ImagePack::InvalidImageError` on damaged/truncated JPEG warnings.
|
|
91
63
|
|
|
92
|
-
|
|
64
|
+
`report: true` returns a Hash:
|
|
93
65
|
|
|
94
|
-
|
|
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
|
+
```
|
|
95
78
|
|
|
96
|
-
|
|
79
|
+
With `output: "file.jpg"`, `output` is `true`.
|
|
97
80
|
|
|
98
|
-
|
|
99
|
-
- `strip_metadata: false` preserves APP/COM metadata by default because this path is meant to be visually/losslessly safe.
|
|
81
|
+
## Lossless optimize
|
|
100
82
|
|
|
101
|
-
|
|
83
|
+
```ruby
|
|
84
|
+
ImagePack.optimize_bytes(jpeg)
|
|
85
|
+
ImagePack.optimize_file("photo.jpg", output: "photo.optimized.jpg")
|
|
86
|
+
```
|
|
102
87
|
|
|
103
|
-
|
|
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.
|
|
104
89
|
|
|
105
|
-
|
|
90
|
+
Defaults: `progressive: true`, `strip_metadata: false`.
|
|
106
91
|
|
|
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`.
|
|
92
|
+
If `strip_metadata: true` would remove EXIF Orientation, `optimize_jpeg` raises `UnsupportedError` instead of silently changing visual orientation.
|
|
112
93
|
|
|
113
|
-
|
|
114
|
-
and `min_ssim:` are provided, `quality:` is treated as the lower starting point and
|
|
115
|
-
the encoder raises quality only if the candidate violates the SSIM floor.
|
|
94
|
+
## Raw pixels
|
|
116
95
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
96
|
+
```ruby
|
|
97
|
+
ImagePack.compress_pixels(rgb,
|
|
98
|
+
width: 1920,
|
|
99
|
+
height: 1080,
|
|
100
|
+
channels: 3,
|
|
101
|
+
output: "frame.jpg"
|
|
102
|
+
)
|
|
103
|
+
```
|
|
121
104
|
|
|
122
|
-
|
|
105
|
+
`channels` must be `1`, `3`, or `4`. JPEG cannot store alpha, so RGBA input needs explicit opt-in:
|
|
123
106
|
|
|
124
|
-
|
|
107
|
+
```ruby
|
|
108
|
+
ImagePack.compress_pixels(rgba, width: 100, height: 100, channels: 4, drop_alpha: true)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Inspect
|
|
125
112
|
|
|
126
113
|
```ruby
|
|
127
|
-
ImagePack.
|
|
128
|
-
|
|
129
|
-
ImagePack.compress(jpeg, execution: :offload)
|
|
130
|
-
ImagePack.compress(jpeg, execution: :auto)
|
|
114
|
+
ImagePack.inspect_image(jpeg)
|
|
115
|
+
# => { format: :jpeg, width: 1920, height: 1080, channels: 3, bit_depth: 8, decoded_bytes: 6220800 }
|
|
131
116
|
```
|
|
132
117
|
|
|
133
|
-
|
|
134
|
-
- `:nogvl` — uses `rb_nogvl(..., flags: 0)`.
|
|
135
|
-
- `:offload` — uses `rb_nogvl(..., RB_NOGVL_OFFLOAD_SAFE)` on Ruby 3.4+.
|
|
136
|
-
- `:auto` — header-first policy chooses direct/nogvl/offload.
|
|
118
|
+
## Execution
|
|
137
119
|
|
|
138
|
-
|
|
120
|
+
Default mode is `:auto`.
|
|
139
121
|
|
|
140
|
-
|
|
141
|
-
|
|
122
|
+
```ruby
|
|
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
|
|
127
|
+
```
|
|
142
128
|
|
|
143
|
-
|
|
129
|
+
Use `ImagePack.offload_safe?` or `ImagePack.build_info` to inspect runtime support.
|
|
144
130
|
|
|
145
|
-
|
|
131
|
+
Set `IMAGE_PACK_DISABLE_OFFLOAD=1` before loading the gem to disable offload.
|
|
146
132
|
|
|
147
|
-
|
|
133
|
+
Long no-GVL/offload calls can be interrupted by raising into the worker thread:
|
|
148
134
|
|
|
149
135
|
```ruby
|
|
150
|
-
ImagePack.
|
|
151
|
-
|
|
136
|
+
worker = Thread.new { ImagePack.compress_bytes(jpeg, execution: :nogvl, cancellable: true) }
|
|
137
|
+
worker.raise(ImagePack::CancelledError, "cancelled")
|
|
138
|
+
worker.join
|
|
152
139
|
```
|
|
153
140
|
|
|
154
|
-
##
|
|
141
|
+
## Configuration
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
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
|
|
150
|
+
```
|
|
155
151
|
|
|
156
|
-
|
|
157
|
-
For repository preparation:
|
|
152
|
+
## Development
|
|
158
153
|
|
|
159
154
|
```bash
|
|
160
155
|
bundle exec rake vendor
|
|
161
156
|
bundle exec rake compile
|
|
157
|
+
bundle exec rake test
|
|
162
158
|
```
|
|
163
159
|
|
|
164
160
|
`rake vendor` pins MozJPEG `v4.1.5`.
|
|
165
161
|
|
|
166
|
-
##
|
|
167
|
-
|
|
168
|
-
- 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.
|
|
169
|
-
- Arithmetic-coded JPEG support is disabled in the vendored `jconfig.h` for v0.2.2.
|
|
170
|
-
- The SSIM guard is a fast 8x8 luma block metric and still assumes quality/SSIM monotonicity during binary search.
|
|
171
|
-
- `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.
|
|
172
|
-
|
|
173
|
-
## What is intentionally not included
|
|
162
|
+
## Limits
|
|
174
163
|
|
|
175
|
-
-
|
|
176
|
-
-
|
|
177
|
-
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
180
|
-
-
|
|
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
|
@@ -4,127 +4,19 @@ require "mkmf"
|
|
|
4
4
|
require "fileutils"
|
|
5
5
|
require "rbconfig"
|
|
6
6
|
|
|
7
|
-
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.
|
|
8
|
-
abort "image_pack requires Ruby >= 3.
|
|
7
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.1.0")
|
|
8
|
+
abort "image_pack requires Ruby >= 3.1.0; got #{RUBY_VERSION}"
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
jcinit.c
|
|
21
|
-
jcmainct.c
|
|
22
|
-
jcmarker.c
|
|
23
|
-
jcmaster.c
|
|
24
|
-
jcomapi.c
|
|
25
|
-
jcparam.c
|
|
26
|
-
jcphuff.c
|
|
27
|
-
jcprepct.c
|
|
28
|
-
jcsample.c
|
|
29
|
-
jctrans.c
|
|
30
|
-
jdapimin.c
|
|
31
|
-
jdapistd.c
|
|
32
|
-
jdatadst.c
|
|
33
|
-
jdatasrc.c
|
|
34
|
-
jdcoefct.c
|
|
35
|
-
jdcolor.c
|
|
36
|
-
jddctmgr.c
|
|
37
|
-
jdhuff.c
|
|
38
|
-
jdicc.c
|
|
39
|
-
jdinput.c
|
|
40
|
-
jdmainct.c
|
|
41
|
-
jdmarker.c
|
|
42
|
-
jdmaster.c
|
|
43
|
-
jdmerge.c
|
|
44
|
-
jdphuff.c
|
|
45
|
-
jdpostct.c
|
|
46
|
-
jdsample.c
|
|
47
|
-
jdtrans.c
|
|
48
|
-
jerror.c
|
|
49
|
-
jfdctflt.c
|
|
50
|
-
jfdctfst.c
|
|
51
|
-
jfdctint.c
|
|
52
|
-
jidctflt.c
|
|
53
|
-
jidctfst.c
|
|
54
|
-
jidctint.c
|
|
55
|
-
jidctred.c
|
|
56
|
-
jquant1.c
|
|
57
|
-
jquant2.c
|
|
58
|
-
jutils.c
|
|
59
|
-
jmemmgr.c
|
|
60
|
-
jmemnobs.c
|
|
61
|
-
].freeze
|
|
62
|
-
|
|
63
|
-
NEON_AARCH64_SOURCES = [
|
|
64
|
-
"simd/arm/aarch64/jsimd.c",
|
|
65
|
-
"simd/arm/aarch64/jchuff-neon.c",
|
|
66
|
-
"simd/arm/jccolor-neon.c",
|
|
67
|
-
"simd/arm/jcgray-neon.c",
|
|
68
|
-
"simd/arm/jcphuff-neon.c",
|
|
69
|
-
"simd/arm/jcsample-neon.c",
|
|
70
|
-
"simd/arm/jdcolor-neon.c",
|
|
71
|
-
"simd/arm/jdmerge-neon.c",
|
|
72
|
-
"simd/arm/jdsample-neon.c",
|
|
73
|
-
"simd/arm/jfdctfst-neon.c",
|
|
74
|
-
"simd/arm/jfdctint-neon.c",
|
|
75
|
-
"simd/arm/jidctfst-neon.c",
|
|
76
|
-
"simd/arm/jidctint-neon.c",
|
|
77
|
-
"simd/arm/jidctred-neon.c",
|
|
78
|
-
"simd/arm/jquanti-neon.c",
|
|
79
|
-
].freeze
|
|
80
|
-
|
|
81
|
-
X86_64_C_SOURCES = [
|
|
82
|
-
"simd/x86_64/jsimd.c",
|
|
83
|
-
].freeze
|
|
84
|
-
|
|
85
|
-
X86_64_ASM_SOURCES = %w[
|
|
86
|
-
simd/x86_64/jsimdcpu.asm
|
|
87
|
-
simd/x86_64/jfdctflt-sse.asm
|
|
88
|
-
simd/x86_64/jccolor-sse2.asm
|
|
89
|
-
simd/x86_64/jcgray-sse2.asm
|
|
90
|
-
simd/x86_64/jchuff-sse2.asm
|
|
91
|
-
simd/x86_64/jcphuff-sse2.asm
|
|
92
|
-
simd/x86_64/jcsample-sse2.asm
|
|
93
|
-
simd/x86_64/jdcolor-sse2.asm
|
|
94
|
-
simd/x86_64/jdmerge-sse2.asm
|
|
95
|
-
simd/x86_64/jdsample-sse2.asm
|
|
96
|
-
simd/x86_64/jfdctfst-sse2.asm
|
|
97
|
-
simd/x86_64/jfdctint-sse2.asm
|
|
98
|
-
simd/x86_64/jidctflt-sse2.asm
|
|
99
|
-
simd/x86_64/jidctfst-sse2.asm
|
|
100
|
-
simd/x86_64/jidctint-sse2.asm
|
|
101
|
-
simd/x86_64/jidctred-sse2.asm
|
|
102
|
-
simd/x86_64/jquantf-sse2.asm
|
|
103
|
-
simd/x86_64/jquanti-sse2.asm
|
|
104
|
-
simd/x86_64/jccolor-avx2.asm
|
|
105
|
-
simd/x86_64/jcgray-avx2.asm
|
|
106
|
-
simd/x86_64/jcsample-avx2.asm
|
|
107
|
-
simd/x86_64/jdcolor-avx2.asm
|
|
108
|
-
simd/x86_64/jdmerge-avx2.asm
|
|
109
|
-
simd/x86_64/jdsample-avx2.asm
|
|
110
|
-
simd/x86_64/jfdctint-avx2.asm
|
|
111
|
-
simd/x86_64/jidctint-avx2.asm
|
|
112
|
-
simd/x86_64/jquanti-avx2.asm
|
|
113
|
-
].freeze
|
|
114
|
-
|
|
115
|
-
X86_64_ASM_TEMPLATE_SOURCES = %w[
|
|
116
|
-
simd/x86_64/jccolext-sse2.asm
|
|
117
|
-
simd/x86_64/jcgryext-sse2.asm
|
|
118
|
-
simd/x86_64/jdcolext-sse2.asm
|
|
119
|
-
simd/x86_64/jdmrgext-sse2.asm
|
|
120
|
-
simd/x86_64/jccolext-avx2.asm
|
|
121
|
-
simd/x86_64/jcgryext-avx2.asm
|
|
122
|
-
simd/x86_64/jdcolext-avx2.asm
|
|
123
|
-
simd/x86_64/jdmrgext-avx2.asm
|
|
124
|
-
].freeze
|
|
125
|
-
|
|
126
|
-
MOZJPEG_VERSION = "4.1.5"
|
|
127
|
-
MOZJPEG_VERSION_NUMBER = "4001005"
|
|
11
|
+
require_relative "mozjpeg_sources"
|
|
12
|
+
|
|
13
|
+
MOZJPEG_CORE_SOURCES = ImagePackMozjpegSources::TOPLEVEL_C_SOURCES
|
|
14
|
+
MOZJPEG_INCLUDE_TEMPLATES = ImagePackMozjpegSources::TOPLEVEL_INCLUDE_TEMPLATES
|
|
15
|
+
NEON_AARCH64_SOURCES = ImagePackMozjpegSources::NEON_AARCH64_C_SOURCES
|
|
16
|
+
X86_64_C_SOURCES = ImagePackMozjpegSources::X86_64_C_SOURCES
|
|
17
|
+
X86_64_ASM_SOURCES = ImagePackMozjpegSources::X86_64_ASM_SOURCES
|
|
18
|
+
MOZJPEG_VERSION = ImagePackMozjpegSources::VERSION
|
|
19
|
+
MOZJPEG_VERSION_NUMBER = ImagePackMozjpegSources::VERSION_NUMBER
|
|
128
20
|
|
|
129
21
|
def find_vendor_dir
|
|
130
22
|
candidates = [
|
|
@@ -285,8 +177,14 @@ def select_simd_backend(mozjpeg_dir, arch)
|
|
|
285
177
|
else
|
|
286
178
|
missing = NEON_AARCH64_SOURCES.reject { |rel| File.exist?(File.join(mozjpeg_dir, rel)) }
|
|
287
179
|
missing << "simd/arm/neon-compat.h.in" unless template_present
|
|
288
|
-
|
|
289
|
-
|
|
180
|
+
message = "image_pack: ARM64 detected but NEON sources are incomplete under #{mozjpeg_dir}:
|
|
181
|
+
" "#{missing.map { |m| " - #{m}" }.join("
|
|
182
|
+
")}"
|
|
183
|
+
if ENV["IMAGE_PACK_REQUIRE_SIMD"] == "1"
|
|
184
|
+
abort "#{message}
|
|
185
|
+
image_pack: refusing scalar fallback because IMAGE_PACK_REQUIRE_SIMD=1."
|
|
186
|
+
end
|
|
187
|
+
warn message
|
|
290
188
|
warn "image_pack: re-run `bundle exec rake vendor` after updating script/vendor_libs.rb."
|
|
291
189
|
warn "image_pack: falling back to scalar."
|
|
292
190
|
{ kind: :none }
|
|
@@ -301,13 +199,25 @@ def select_simd_backend(mozjpeg_dir, arch)
|
|
|
301
199
|
warn "#{message} Falling back to scalar; install nasm for ~3-4x speedup. Set IMAGE_PACK_REQUIRE_SIMD=1 to fail instead."
|
|
302
200
|
{ kind: :none }
|
|
303
201
|
elsif !X86_64_C_SOURCES.all? { |rel| File.exist?(File.join(mozjpeg_dir, rel)) }
|
|
304
|
-
|
|
202
|
+
missing = X86_64_C_SOURCES.reject { |rel| File.exist?(File.join(mozjpeg_dir, rel)) }
|
|
203
|
+
message = "image_pack: x86_64 SIMD C sources missing under #{mozjpeg_dir}: #{missing.join(', ')}"
|
|
204
|
+
ENV["IMAGE_PACK_REQUIRE_SIMD"] == "1" ? abort(message) : warn("#{message}. Falling back to scalar.")
|
|
305
205
|
{ kind: :none }
|
|
306
206
|
else
|
|
207
|
+
missing_asm = X86_64_ASM_SOURCES.reject { |rel| File.exist?(File.join(mozjpeg_dir, rel)) }
|
|
208
|
+
unless missing_asm.empty?
|
|
209
|
+
message = "image_pack: x86_64 SIMD ASM sources missing under #{mozjpeg_dir}: #{missing_asm.join(', ')}"
|
|
210
|
+
if ENV["IMAGE_PACK_REQUIRE_SIMD"] == "1"
|
|
211
|
+
abort message
|
|
212
|
+
end
|
|
213
|
+
warn "#{message}. Falling back to scalar."
|
|
214
|
+
return { kind: :none }
|
|
215
|
+
end
|
|
216
|
+
|
|
307
217
|
{
|
|
308
218
|
kind: :x86_64_simd,
|
|
309
219
|
c_sources: X86_64_C_SOURCES,
|
|
310
|
-
asm_sources: X86_64_ASM_SOURCES
|
|
220
|
+
asm_sources: X86_64_ASM_SOURCES,
|
|
311
221
|
extra_includes: [
|
|
312
222
|
File.join(mozjpeg_dir, "simd"),
|
|
313
223
|
File.join(mozjpeg_dir, "simd", "nasm"),
|
|
@@ -367,7 +277,8 @@ def configure_vendored_mozjpeg(vendor_dir)
|
|
|
367
277
|
|
|
368
278
|
abort "vendored MozJPEG sources not found in #{mozjpeg_dir}" unless Dir.exist?(mozjpeg_dir)
|
|
369
279
|
|
|
370
|
-
|
|
280
|
+
required_sources = MOZJPEG_CORE_SOURCES + MOZJPEG_INCLUDE_TEMPLATES
|
|
281
|
+
missing_sources = required_sources.reject { |source| File.exist?(File.join(mozjpeg_dir, source)) }
|
|
371
282
|
abort "vendored MozJPEG sources are incomplete: #{missing_sources.join(', ')}" unless missing_sources.empty?
|
|
372
283
|
|
|
373
284
|
arch = detect_simd_arch
|
|
@@ -505,8 +416,8 @@ else
|
|
|
505
416
|
$CFLAGS += " -O2"
|
|
506
417
|
end
|
|
507
418
|
|
|
508
|
-
$CFLAGS += " -DIMAGE_PACK_RUBY34_ONLY=1"
|
|
509
419
|
$CFLAGS += build_info[:with_simd] ? " -DIMAGE_PACK_HAS_SIMD=1" : " -DIMAGE_PACK_PURE_C=1"
|
|
420
|
+
$CPPFLAGS += " -DIMAGE_PACK_MOZJPEG_VERSION=\\\"#{MOZJPEG_VERSION}\\\""
|
|
510
421
|
$LIBS += " -lm" unless msvc?
|
|
511
422
|
|
|
512
423
|
have_header("ruby/thread.h") or abort "ruby/thread.h not found"
|