webp-ffi 1.0.0-x86_64-linux-musl
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 +7 -0
- data/CHANGELOG.md +135 -0
- data/LICENSE.txt +22 -0
- data/README.md +308 -0
- data/ext/webp_ffi/Rakefile +3 -0
- data/ext/webp_ffi/build_config.rb +119 -0
- data/ext/webp_ffi/jpegdec.c +174 -0
- data/ext/webp_ffi/jpegdec.h +20 -0
- data/ext/webp_ffi/metadata.c +138 -0
- data/ext/webp_ffi/metadata.h +49 -0
- data/ext/webp_ffi/pngdec.c +389 -0
- data/ext/webp_ffi/pngdec.h +29 -0
- data/ext/webp_ffi/tiffdec.c +361 -0
- data/ext/webp_ffi/tiffdec.h +31 -0
- data/ext/webp_ffi/util.c +216 -0
- data/ext/webp_ffi/util.h +63 -0
- data/ext/webp_ffi/webp_ffi.c +618 -0
- data/ext/webp_ffi/webp_ffi.h +110 -0
- data/ext/webp_ffi/x86_64-linux/libwebp_ffi.so +0 -0
- data/ext/webp_ffi/x86_64-linux/licenses/libjpeg-turbo-LICENSE.md +147 -0
- data/ext/webp_ffi/x86_64-linux/licenses/libjpeg-turbo-README.ijg +260 -0
- data/ext/webp_ffi/x86_64-linux/licenses/libpng-LICENSE +134 -0
- data/ext/webp_ffi/x86_64-linux/licenses/libtiff-LICENSE.md +49 -0
- data/ext/webp_ffi/x86_64-linux/licenses/libwebp-COPYING +30 -0
- data/ext/webp_ffi/x86_64-linux/licenses/libwebp-PATENTS +23 -0
- data/ext/webp_ffi/x86_64-linux/licenses/zlib-LICENSE +22 -0
- data/ext/webp_ffi/x86_64-linux/versions.txt +5 -0
- data/lib/webp/c.rb +101 -0
- data/lib/webp/error.rb +71 -0
- data/lib/webp/info.rb +13 -0
- data/lib/webp/options.rb +171 -0
- data/lib/webp/version.rb +3 -0
- data/lib/webp/webp.rb +179 -0
- data/lib/webp-ffi.rb +1 -0
- data/lib/webp_ffi.rb +16 -0
- data/sig/webp.rbs +74 -0
- metadata +145 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 60753f7640bd06508559f08cb0e3298449cb17550e36cfdaee7959629719881a
|
|
4
|
+
data.tar.gz: c05a98da0d1876c7d311ae2667508f1e334d2509f34b870922c50f7baf5ae8f0
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b8ed8983d1b811ad79c5c2c981208e498170bcaa69fd373c8520a1083e716ad334a4faa8562e779c6a1dfd8b74d4e17a3372112e049dafd4efbdfc82438a885c
|
|
7
|
+
data.tar.gz: b980e9d6426d3728c54d59a264e9d22bc2d0277991f0e35e7b3205c1046b444c8ed498950f4f53a8b735651e1d6e875774e9165494ce304c80c9cbad25777726
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
## v1.0.0
|
|
2
|
+
|
|
3
|
+
Breaking changes:
|
|
4
|
+
|
|
5
|
+
* Require Ruby 3.2 or newer
|
|
6
|
+
* The source gem requires libwebp 1.0.0 or newer
|
|
7
|
+
* Raise `ArgumentError` for unknown options (they were ignored), with a suggestion for typos
|
|
8
|
+
* Raise `ArgumentError` for an unknown `output_format` instead of silently writing PNG
|
|
9
|
+
* Raise `ArgumentError` for option values that are not numbers
|
|
10
|
+
* `false` now means 0 for integer options, instead of the libwebp default
|
|
11
|
+
* `near_lossless` now works, so it produces lossless (larger) files; it was ignored before
|
|
12
|
+
* Decode raises `WebP::DecoderError` when the output file cannot be written; it reported success before
|
|
13
|
+
* Multi-page TIFF files use the first page instead of the last
|
|
14
|
+
* Remove the unused `WebP::LibC` module and `WebP::C.test_c`
|
|
15
|
+
* The functions in `WebP::C` changed; use the `WebP` methods instead
|
|
16
|
+
|
|
17
|
+
New:
|
|
18
|
+
|
|
19
|
+
* `WebP.encode_data` and `WebP.decode_data` encode and decode binary strings in memory
|
|
20
|
+
* `WebP.info` returns the size, format (lossy, lossless or mixed), alpha, animation and metadata of WebP data
|
|
21
|
+
* `WebP.encode_rgba` and `WebP.decode_rgba` work with raw RGBA pixels
|
|
22
|
+
* Copy ICC, EXIF and XMP metadata from PNG, JPEG and TIFF input with the `metadata` option, byte for byte like `cwebp -metadata`
|
|
23
|
+
* New encode options: `preset`, `level` (lossless preset), `image_hint`, `exact`, `use_sharp_yuv`, `thread_level`, `low_memory` and `emulate_jpeg_size`
|
|
24
|
+
* The in-memory, raw pixel and info methods also accept an IO
|
|
25
|
+
* `WebP::Error` is the base class of every error; encoder and decoder errors carry the error `code` and libwebp's `reason`, which is also in the message
|
|
26
|
+
* RBS type signatures in `sig/webp.rbs`
|
|
27
|
+
* Prebuilt gems for Linux (glibc and musl, x86_64 and aarch64) and macOS 11+ (arm64 and x86_64) with libwebp 1.6.0, libpng, libjpeg-turbo, libtiff and zlib included
|
|
28
|
+
* Release the Global VM Lock during encode and decode, so other Ruby threads keep running
|
|
29
|
+
* Accept `Pathname` and other objects with `#to_path` as file names
|
|
30
|
+
* Accept `true` and `false` for integer options, and the decode `output_format` as a string
|
|
31
|
+
* Include the file path in error messages about input and output files
|
|
32
|
+
|
|
33
|
+
Fixes:
|
|
34
|
+
|
|
35
|
+
* `target_size` and `target_PSNR` had no effect, because libwebp needs more than one pass; like cwebp, 6 passes are used now
|
|
36
|
+
* A failed encode left an empty output file, which the README's Rails task would then keep forever; the output is now written only after encoding succeeds
|
|
37
|
+
* `output_format: :yuv` wrote the same PGM file as `:pgm`; it now writes the raw YUV planes
|
|
38
|
+
* TIFF input with alpha lost color, because libtiff returns premultiplied pixels; they are now converted back
|
|
39
|
+
* Reading a PNG relied on undefined behavior (a signed left shift)
|
|
40
|
+
* Only regular files are read as input; on Linux a folder made the reader ask for a huge allocation
|
|
41
|
+
* JPEG and TIFF input was converted to YUV before lossless or sharp YUV encoding, so lossless output was not exact
|
|
42
|
+
* Check image sizes before allocating memory in the PNG, JPEG and TIFF readers
|
|
43
|
+
* Fix memory leaks when reading broken PNG and JPEG files
|
|
44
|
+
* Fix wrong error message when the decode input file cannot be read
|
|
45
|
+
* Detect write errors when closing output files
|
|
46
|
+
* Stop printing errors and warnings to stderr (libtiff older than 4.5.0 still prints its own)
|
|
47
|
+
* Always initialize every decode option passed to C
|
|
48
|
+
|
|
49
|
+
Build and release:
|
|
50
|
+
|
|
51
|
+
* Find libraries from Homebrew on Apple Silicon and from MacPorts without extra flags
|
|
52
|
+
* `C_FLAGS` and `LD_FLAGS` now take priority over the default library folders
|
|
53
|
+
* Stop the build early with install instructions when libwebp, libpng, libjpeg or libtiff is missing
|
|
54
|
+
* Ship a smaller gem without specs and test images
|
|
55
|
+
* Add rake as a runtime dependency, because gem install uses it to build the extension
|
|
56
|
+
* Release from a `v*` tag through GitHub Actions and RubyGems.org trusted publishing
|
|
57
|
+
* Pin GitHub Actions and Docker images to digests; Dependabot keeps the actions updated
|
|
58
|
+
* CI runs the specs with AddressSanitizer and UndefinedBehaviorSanitizer, and compares the output with libwebp's cwebp
|
|
59
|
+
|
|
60
|
+
## v0.4.0
|
|
61
|
+
|
|
62
|
+
* Update CI
|
|
63
|
+
* Fix C code deprecations warnings
|
|
64
|
+
* Update test Ruby versions
|
|
65
|
+
|
|
66
|
+
## v0.2.7
|
|
67
|
+
|
|
68
|
+
* Fix build for old libwebp versions
|
|
69
|
+
|
|
70
|
+
## v0.2.6
|
|
71
|
+
|
|
72
|
+
* Tested on webp 1.0.2
|
|
73
|
+
* Added near_lossless option
|
|
74
|
+
|
|
75
|
+
## v0.2.5
|
|
76
|
+
|
|
77
|
+
* Fix build for Travis CI docker containers
|
|
78
|
+
|
|
79
|
+
## v0.2.4
|
|
80
|
+
|
|
81
|
+
* DRY C code and split into files each read/write formats
|
|
82
|
+
|
|
83
|
+
## v0.2.3
|
|
84
|
+
|
|
85
|
+
* Fix jpg read in C code
|
|
86
|
+
|
|
87
|
+
## v0.2.2
|
|
88
|
+
|
|
89
|
+
* Fix png lossless
|
|
90
|
+
|
|
91
|
+
## v0.2.1
|
|
92
|
+
|
|
93
|
+
* Fix output for bmp, tiff and yuv
|
|
94
|
+
|
|
95
|
+
## v0.2.0
|
|
96
|
+
|
|
97
|
+
* Checked and fixed for webp 0.4.0
|
|
98
|
+
|
|
99
|
+
## v0.1.8
|
|
100
|
+
|
|
101
|
+
* Fix encoder options (didn't work fully)
|
|
102
|
+
|
|
103
|
+
## v0.1.7
|
|
104
|
+
|
|
105
|
+
* Bump FFI dependency to ~> 1.9.0
|
|
106
|
+
|
|
107
|
+
## v0.1.6
|
|
108
|
+
|
|
109
|
+
* Support libwebp 0.3.1
|
|
110
|
+
|
|
111
|
+
## v0.1.5
|
|
112
|
+
|
|
113
|
+
* Code cleanup
|
|
114
|
+
|
|
115
|
+
## v0.1.4
|
|
116
|
+
|
|
117
|
+
* Added decode options
|
|
118
|
+
* Added error class for decode errors
|
|
119
|
+
|
|
120
|
+
## v0.1.3
|
|
121
|
+
|
|
122
|
+
* Rename module to WebP
|
|
123
|
+
|
|
124
|
+
## v0.1.2
|
|
125
|
+
|
|
126
|
+
* Added encode options
|
|
127
|
+
* Added error class for encode errors
|
|
128
|
+
|
|
129
|
+
## v0.1.1
|
|
130
|
+
|
|
131
|
+
* Added encode options
|
|
132
|
+
|
|
133
|
+
## v0.1.0
|
|
134
|
+
|
|
135
|
+
* First release
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Oleksii Vasyliev
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# Webp-ffi
|
|
2
|
+
|
|
3
|
+
[](https://github.com/le0pard/webp-ffi/actions/workflows/build.yml)
|
|
4
|
+
|
|
5
|
+
Ruby wrapper for libwebp. What is WebP?
|
|
6
|
+
|
|
7
|
+
WebP is a new image format that provides lossless and lossy compression for images on the web. WebP lossless images are 26% smaller in size compared to PNGs. WebP lossy images are 25-34% smaller in size compared to JPEG images at equivalent SSIM index. WebP supports lossless transparency (also known as alpha channel) with just 22% additional bytes. Transparency is also supported with lossy compression and typically provides 3x smaller file sizes compared to PNG when lossy compression is acceptable for the red/green/blue color channels.
|
|
8
|
+
|
|
9
|
+
## [WebP Gallery](https://developers.google.com/speed/webp/gallery)
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
### Prebuilt gems
|
|
14
|
+
|
|
15
|
+
On these platforms `gem install webp-ffi` (or Bundler) picks a prebuilt gem, so nothing is compiled and no system libraries are needed:
|
|
16
|
+
|
|
17
|
+
* Linux with glibc 2.28 or newer: `x86_64-linux-gnu`, `aarch64-linux-gnu`
|
|
18
|
+
* Linux with musl (Alpine): `x86_64-linux-musl`, `aarch64-linux-musl`
|
|
19
|
+
* macOS 11 or newer: `arm64-darwin` (Apple Silicon), `x86_64-darwin` (Intel)
|
|
20
|
+
|
|
21
|
+
The prebuilt gems include libwebp 1.6.0 together with libpng, libjpeg-turbo, libtiff and zlib. Their versions and licenses are in the gem, next to the library (`ext/webp_ffi/<platform>/versions.txt` and `licenses/`).
|
|
22
|
+
|
|
23
|
+
If your `Gemfile.lock` lists only the `ruby` platform, add yours, for example:
|
|
24
|
+
|
|
25
|
+
bundle lock --add-platform x86_64-linux-gnu
|
|
26
|
+
|
|
27
|
+
To build against the system libraries instead, install the source gem:
|
|
28
|
+
|
|
29
|
+
gem install webp-ffi --platform ruby
|
|
30
|
+
|
|
31
|
+
or in a `Gemfile`:
|
|
32
|
+
|
|
33
|
+
gem "webp-ffi", force_ruby_platform: true
|
|
34
|
+
|
|
35
|
+
The rest of this section applies to the source gem.
|
|
36
|
+
|
|
37
|
+
### Requirements
|
|
38
|
+
|
|
39
|
+
First of all you should have install libraries: libpng, libjpeg and libtiff.
|
|
40
|
+
|
|
41
|
+
For Ubuntu, Debian:
|
|
42
|
+
|
|
43
|
+
sudo apt-get install libjpeg-dev libpng-dev libtiff-dev libwebp-dev
|
|
44
|
+
|
|
45
|
+
For Fedora, CentOS:
|
|
46
|
+
|
|
47
|
+
sudo dnf install libjpeg-devel libpng-devel libtiff-devel libwebp-devel
|
|
48
|
+
|
|
49
|
+
For Mac OS:
|
|
50
|
+
|
|
51
|
+
brew install webp libpng jpeg-turbo libtiff
|
|
52
|
+
|
|
53
|
+
or (for MacPorts):
|
|
54
|
+
|
|
55
|
+
sudo port install webp jpeg libpng tiff
|
|
56
|
+
|
|
57
|
+
The build finds libraries installed by Homebrew (`/opt/homebrew` or `/usr/local`) and MacPorts (`/opt/local`). For any other location, pass the paths with `C_FLAGS` and `LD_FLAGS`:
|
|
58
|
+
|
|
59
|
+
C_FLAGS="-I/path/to/include" LD_FLAGS="-L/path/to/lib" gem install webp-ffi
|
|
60
|
+
|
|
61
|
+
Next, you should [install libwebp](https://developers.google.com/speed/webp/docs/compiling) (if you didn't install it by `brew` in Mac OS or by `apt-get` in Ubuntu or Debian). The source gem needs libwebp 1.0.0 or newer. This gem does not support Windows.
|
|
62
|
+
|
|
63
|
+
### Final part
|
|
64
|
+
|
|
65
|
+
Add this line to your application's Gemfile:
|
|
66
|
+
|
|
67
|
+
gem 'webp-ffi'
|
|
68
|
+
|
|
69
|
+
And then execute:
|
|
70
|
+
|
|
71
|
+
$ bundle
|
|
72
|
+
|
|
73
|
+
Or install it yourself as:
|
|
74
|
+
|
|
75
|
+
$ gem install webp-ffi
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
### Encoder and Decoder versions
|
|
80
|
+
|
|
81
|
+
Basic info about libwebp (encoder and decoder versions):
|
|
82
|
+
|
|
83
|
+
require 'webp_ffi'
|
|
84
|
+
WebP.decoder_version # => "1.6.0"
|
|
85
|
+
WebP.encoder_version # => "1.6.0"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
### WebP size and details
|
|
89
|
+
|
|
90
|
+
Get the size (width and height) of a WebP image:
|
|
91
|
+
|
|
92
|
+
WebP.webp_size(File.binread("photo.webp")) # => [1024, 772]
|
|
93
|
+
|
|
94
|
+
`WebP.info` returns more details as a `WebP::Info`:
|
|
95
|
+
|
|
96
|
+
info = WebP.info(File.binread("photo.webp"))
|
|
97
|
+
info.width # => 1024
|
|
98
|
+
info.height # => 772
|
|
99
|
+
info.format # => :lossy, :lossless or :mixed (animations)
|
|
100
|
+
info.alpha? # => false
|
|
101
|
+
info.animated? # => false
|
|
102
|
+
info.metadata # => [:icc, :exif] (metadata chunks in the file)
|
|
103
|
+
|
|
104
|
+
Both accept a binary String or an IO, and raise `WebP::InvalidImageFormatError` for data that is not WebP.
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
### Encode WebP image
|
|
108
|
+
|
|
109
|
+
Encode png, jpg or tiff image to webp (only the first page of a multi-page tiff is used):
|
|
110
|
+
|
|
111
|
+
filename = File.expand_path(File.join(File.dirname(__FILE__), "spec/factories/4.png"))
|
|
112
|
+
out_filename = File.expand_path(File.join(File.dirname(__FILE__), "tmp/4.webp"))
|
|
113
|
+
WebP.encode(filename, out_filename)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
Encode png, jpg or tiff image to webp with options:
|
|
117
|
+
|
|
118
|
+
WebP.encode(filename, out_filename, quality: 50, resize_w: 100, resize_h: 200)
|
|
119
|
+
WebP.encode(filename, out_filename, quality: 75, crop_x: 0, crop_y: 0, crop_w: 100, crop_h: 100)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
File names can be strings or `Pathname` objects.
|
|
123
|
+
|
|
124
|
+
Integer options also accept `true` and `false` (as 1 and 0). Unknown option names and values that are not numbers raise `ArgumentError`, so typos are easy to spot.
|
|
125
|
+
|
|
126
|
+
Possible encode options:
|
|
127
|
+
|
|
128
|
+
* **preset** (symbol) - start from libwebp's settings for a kind of image: `:default`, `:picture`, `:photo`, `:drawing`, `:icon` or `:text`. Other options still apply on top
|
|
129
|
+
* **lossless** (int) - Lossless encoding (0=lossy(default), 1=lossless)
|
|
130
|
+
* **level** (int) - lossless preset from 0 (fastest) to 9 (smallest), like `cwebp -z`. Turns on lossless encoding and picks its own quality and method unless you set them
|
|
131
|
+
* **near_lossless** (int) - Use near-lossless image preprocessing (0=maximum preprocessing, 100=no preprocessing(default)). Turns on lossless encoding
|
|
132
|
+
* **quality** (float) - between 0 (smallest file) and 100 (biggest). Default is 100
|
|
133
|
+
* **method** (int) - quality/speed trade-off (0=fast, 6=slower-better)
|
|
134
|
+
* **target\_size** (int) - if non-zero, set the desired target size in bytes. Takes precedence over the 'compression' parameter. Like cwebp, 6 passes are used unless **pass** is set
|
|
135
|
+
* **target\_PSNR** (float) - if non-zero, specifies the minimal distortion to try to achieve. Takes precedence over target\_size
|
|
136
|
+
* **segments** (int) - maximum number of segments to use, in [1..4]
|
|
137
|
+
* **sns_strength** (int) - Spatial Noise Shaping. 0=off, 100=maximum
|
|
138
|
+
* **filter\_strength** (int) - range: [0 = off .. 100 = strongest]
|
|
139
|
+
* **filter\_sharpness** (int) - range: [0 = off .. 7 = least sharp]
|
|
140
|
+
* **filter\_type** (int) - filtering type: 0 = simple, 1 = strong (only used if filter\_strength > 0 or autofilter > 0)
|
|
141
|
+
* **autofilter** (int) - Auto adjust filter's strength [0 = off, 1 = on]
|
|
142
|
+
* **alpha\_compression** (int) - Algorithm for encoding the alpha plane (0 = none, 1 = compressed with WebP lossless). Default is 1
|
|
143
|
+
* **alpha\_filtering** (int) - Predictive filtering method for alpha plane. 0: none, 1: fast, 2: best. Default if 1
|
|
144
|
+
* **alpha\_quality** (int) - Between 0 (smallest size) and 100 (lossless). Default is 100
|
|
145
|
+
* **pass** (int) - number of entropy-analysis passes (in [1..10])
|
|
146
|
+
* **show\_compressed** (int) - if true, export the compressed picture back. In-loop filtering is not applied
|
|
147
|
+
* **preprocessing** (int) - preprocessing filter (0=none, 1=segment-smooth)
|
|
148
|
+
* **partitions** (int) - log2(number of token partitions) in [0..3]. Default is set to 0 for easier progressive decoding. libwebp only uses it together with **low\_memory**
|
|
149
|
+
* **partition\_limit** (int) - quality degradation allowed to fit the 512k limit on prediction modes coding (0: no degradation, 100: maximum possible degradation)
|
|
150
|
+
* **image\_hint** (symbol) - kind of image for lossless encoding: `:default`, `:picture`, `:photo` or `:graph`
|
|
151
|
+
* **exact** (bool) - keep the RGB values under transparent areas, which libwebp otherwise changes to compress better
|
|
152
|
+
* **use\_sharp\_yuv** (bool) - sharper (and slower) RGB to YUV conversion for lossy encoding
|
|
153
|
+
* **thread\_level** (bool) - use more than one thread where libwebp can
|
|
154
|
+
* **low\_memory** (bool) - use less memory and more CPU
|
|
155
|
+
* **emulate\_jpeg\_size** (bool) - pick settings that give about the size of a JPEG of the same quality
|
|
156
|
+
* **metadata** - copy metadata from the input: `:all`, `:none` (default) or some of `:icc`, `:exif` and `:xmp` (see below)
|
|
157
|
+
* **width** (int), **height** (int) - Input size (width x height) for YUV
|
|
158
|
+
* **crop\_x** (int), **crop\_y** (int), **crop\_w** (int), **crop\_h** (int) - crop picture with the given rectangle
|
|
159
|
+
* **resize\_w** (int), **resize\_h** (int) - resize picture (after any cropping)
|
|
160
|
+
|
|
161
|
+
### Metadata
|
|
162
|
+
|
|
163
|
+
By default, like `cwebp`, no metadata is copied. The **metadata** option copies the ICC color profile, EXIF and XMP data from the input into the WebP file:
|
|
164
|
+
|
|
165
|
+
WebP.encode("photo.jpg", "photo.webp", metadata: :all)
|
|
166
|
+
WebP.encode_data(File.binread("photo.png"), metadata: [:icc])
|
|
167
|
+
|
|
168
|
+
PNG and JPEG files can carry all three. In PNG files the gem reads the iCCP and eXIf chunks, XMP text and the "raw profile" text chunks that ImageMagick and ExifTool write. Like `cwebp`, only the ICC profile and XMP data are read from TIFF files, and broken metadata fails the encode only when it was requested.
|
|
169
|
+
|
|
170
|
+
### Decode WebP image
|
|
171
|
+
|
|
172
|
+
Decode webp image (default format is png):
|
|
173
|
+
|
|
174
|
+
filename = File.expand_path(File.join(File.dirname(__FILE__), "spec/factories/4.webp"))
|
|
175
|
+
out_filename = File.expand_path(File.join(File.dirname(__FILE__), "tmp/4.png"))
|
|
176
|
+
WebP.decode(filename, out_filename)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
Decode webp image to pam, ppm, pgm, bmp, tiff or yuv format of image:
|
|
180
|
+
|
|
181
|
+
filename = File.expand_path(File.join(File.dirname(__FILE__), "spec/factories/4.webp"))
|
|
182
|
+
out_filename = File.expand_path(File.join(File.dirname(__FILE__), "tmp/4.png"))
|
|
183
|
+
WebP.decode(filename, out_filename, output_format: :pam)
|
|
184
|
+
WebP.decode(filename, out_filename, output_format: :ppm)
|
|
185
|
+
WebP.decode(filename, out_filename, output_format: :pgm)
|
|
186
|
+
WebP.decode(filename, out_filename, output_format: :bmp)
|
|
187
|
+
WebP.decode(filename, out_filename, output_format: :tiff)
|
|
188
|
+
WebP.decode(filename, out_filename, output_format: :yuv)
|
|
189
|
+
|
|
190
|
+
`:yuv` writes the raw planes (Y, then U and V at half size, then alpha when the image has it), which `WebP.encode_data` reads back with the `width` and `height` options. `:pgm` writes the same planes as a grayscale PGM image.
|
|
191
|
+
|
|
192
|
+
The output file is written only after decoding succeeds, and encoding reads the whole input first, so the input and output can be the same file. When writing fails, a file the gem created is removed again.
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
Decode webp image with options:
|
|
196
|
+
|
|
197
|
+
WebP.decode(filename, out_filename, resize_w: 100, resize_h: 200)
|
|
198
|
+
WebP.decode(filename, out_filename, crop_x: 0, crop_y: 0, crop_w: 100, crop_h: 100)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
Possible decode options (unknown options and an unknown `output_format` raise `ArgumentError`):
|
|
202
|
+
|
|
203
|
+
* **bypass\_filtering** (bool) - disable in-loop filtering
|
|
204
|
+
* **no\_fancy\_upsampling** (bool) - don't use the fancy YUV420 upscaler
|
|
205
|
+
* **use\_threads** (bool) - use multi-threading
|
|
206
|
+
* **crop\_x** (int), **crop\_y** (int), **crop\_w** (int), **crop\_h** (int) - crop picture with the given rectangle
|
|
207
|
+
* **resize\_w** (int), **resize\_h** (int) - resize picture (after any cropping)
|
|
208
|
+
|
|
209
|
+
### Encode and decode in memory
|
|
210
|
+
|
|
211
|
+
`WebP.encode_data` and `WebP.decode_data` work with binary strings instead of files, for example for uploads or images stored in S3. They take the same options as `WebP.encode` and `WebP.decode`.
|
|
212
|
+
|
|
213
|
+
webp = WebP.encode_data(File.binread("photo.jpg"), quality: 80)
|
|
214
|
+
WebP.webp_size(webp) # => [1024, 772]
|
|
215
|
+
|
|
216
|
+
png = WebP.decode_data(webp)
|
|
217
|
+
tiff = WebP.decode_data(webp, output_format: :tiff, resize_w: 200, resize_h: 150)
|
|
218
|
+
|
|
219
|
+
`encode_data` reads PNG, JPEG and TIFF data, or raw YUV 4:2:0 data when `width` and `height` are given. Both methods also accept an IO, such as an open file or a `StringIO`, and read it from its current position.
|
|
220
|
+
|
|
221
|
+
### Raw pixels
|
|
222
|
+
|
|
223
|
+
`WebP.encode_rgba` encodes raw RGBA pixels (4 bytes per pixel, row after row), and `WebP.decode_rgba` returns them, for example to work with ChunkyPNG, libvips or OpenCV:
|
|
224
|
+
|
|
225
|
+
webp = WebP.encode_rgba(pixels, width, height, lossless: true)
|
|
226
|
+
image = WebP.decode_rgba(webp, resize_w: 200, resize_h: 150)
|
|
227
|
+
image.rgba # => binary String of 200 * 150 * 4 bytes
|
|
228
|
+
image.width # => 200
|
|
229
|
+
image.height # => 150
|
|
230
|
+
|
|
231
|
+
They take the same options as `encode_data` and `decode_data`, except the ones for files: **width**, **height**, **metadata** and **output\_format**.
|
|
232
|
+
|
|
233
|
+
### Errors
|
|
234
|
+
|
|
235
|
+
Every error the gem raises inherits from `WebP::Error`. `WebP::EncoderError` and `WebP::DecoderError` also carry the gem's error `code` and libwebp's `reason`, when it gives one:
|
|
236
|
+
|
|
237
|
+
begin
|
|
238
|
+
WebP.encode("huge.png", "huge.webp")
|
|
239
|
+
rescue WebP::EncoderError => e
|
|
240
|
+
e.message # => "Cannot encode picture as WebP (the picture size is not supported, WebP allows up to 16383 x 16383 pixels)"
|
|
241
|
+
e.reason # => :bad_dimension
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
Invalid arguments, like unknown options, raise `ArgumentError` or `TypeError`.
|
|
245
|
+
|
|
246
|
+
### Threads
|
|
247
|
+
|
|
248
|
+
`WebP.encode`, `WebP.decode` and the in-memory and raw pixel methods release Ruby's Global VM Lock while libwebp works, so other threads (for example in Puma or Sidekiq) keep running during long conversions.
|
|
249
|
+
|
|
250
|
+
### Type signatures
|
|
251
|
+
|
|
252
|
+
The gem ships RBS type signatures in `sig/webp.rbs` for Steep and editors with RBS support.
|
|
253
|
+
|
|
254
|
+
## Rails assets pipeline integration
|
|
255
|
+
|
|
256
|
+
For integration with Rails 3+ you can use very simple rake task:
|
|
257
|
+
|
|
258
|
+
```ruby
|
|
259
|
+
# Place this code in lib/tasks/assets.rake
|
|
260
|
+
require 'webp-ffi'
|
|
261
|
+
|
|
262
|
+
namespace :assets do
|
|
263
|
+
desc "Create .webp versions of assets"
|
|
264
|
+
task :webp => :environment do
|
|
265
|
+
image_types = /\.(?:png|jpe?g)$/
|
|
266
|
+
|
|
267
|
+
public_assets = File.join(
|
|
268
|
+
Rails.root,
|
|
269
|
+
"public",
|
|
270
|
+
Rails.application.config.assets.prefix)
|
|
271
|
+
|
|
272
|
+
Dir["#{public_assets}/**/*"].each do |filename|
|
|
273
|
+
next unless filename =~ image_types
|
|
274
|
+
|
|
275
|
+
mtime = File.mtime(filename)
|
|
276
|
+
webp_file = "#{filename}.webp"
|
|
277
|
+
next if File.exist?(webp_file) && File.mtime(webp_file) >= mtime
|
|
278
|
+
begin
|
|
279
|
+
WebP.encode(filename, webp_file)
|
|
280
|
+
File.utime(mtime, mtime, webp_file)
|
|
281
|
+
puts "Webp converted image #{webp_file}"
|
|
282
|
+
rescue => e
|
|
283
|
+
puts "Webp convertion error of image #{webp_file}. Error info: #{e.message}"
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Hook into existing assets:precompile task
|
|
289
|
+
Rake::Task["assets:precompile"].enhance do
|
|
290
|
+
Rake::Task["assets:webp"].invoke
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Releasing
|
|
296
|
+
|
|
297
|
+
1. Update `WebP::VERSION` in `lib/webp/version.rb` and the CHANGELOG.
|
|
298
|
+
2. Commit, then push a tag that matches the version: `git tag v1.0.0 && git push origin v1.0.0`.
|
|
299
|
+
|
|
300
|
+
The Release workflow builds the source gem and every prebuilt gem, tests each one after installing it, and publishes them to RubyGems.org with trusted publishing. To try the builds without publishing, run the workflow by hand from the Actions tab.
|
|
301
|
+
|
|
302
|
+
## Contributing
|
|
303
|
+
|
|
304
|
+
1. Fork it
|
|
305
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
306
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
307
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
308
|
+
5. Create new Pull Request
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require 'open3'
|
|
2
|
+
require 'rbconfig'
|
|
3
|
+
require 'shellwords'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
|
|
6
|
+
module WebPFFI
|
|
7
|
+
# Build settings shared by the gem install Rakefile (ext/webp_ffi/Rakefile)
|
|
8
|
+
# and the development Rakefile in the project root.
|
|
9
|
+
class BuildConfig
|
|
10
|
+
class MissingDependencyError < StandardError; end
|
|
11
|
+
|
|
12
|
+
# Homebrew uses /opt/homebrew on Apple Silicon and /usr/local on Intel Macs.
|
|
13
|
+
# MacPorts uses /opt/local. Libraries anywhere else need C_FLAGS and LD_FLAGS.
|
|
14
|
+
LIBRARY_PREFIXES = %w[/opt/homebrew /usr/local /opt/local].freeze
|
|
15
|
+
|
|
16
|
+
# 'requirement' is an optional preprocessor condition, such as a version check
|
|
17
|
+
Dependency = Struct.new(:name, :header, :library, :code, :requirement, keyword_init: true)
|
|
18
|
+
|
|
19
|
+
DEPENDENCIES = [
|
|
20
|
+
Dependency.new(name: "libwebp 1.0.0 or newer", header: "webp/encode.h", library: "webp", code: "WebPGetEncoderVersion();",
|
|
21
|
+
requirement: "WEBP_ENCODER_ABI_VERSION >= 0x020e"),
|
|
22
|
+
Dependency.new(name: "libpng", header: "png.h", library: "png", code: "png_access_version_number();"),
|
|
23
|
+
Dependency.new(name: "libjpeg", header: "jpeglib.h", library: "jpeg", code: "struct jpeg_error_mgr err; jpeg_std_error(&err);"),
|
|
24
|
+
Dependency.new(name: "libtiff", header: "tiffio.h", library: "tiff", code: "TIFFGetVersion();"),
|
|
25
|
+
Dependency.new(name: "zlib", header: "zlib.h", library: "z", code: "zlibVersion();")
|
|
26
|
+
].freeze
|
|
27
|
+
|
|
28
|
+
INSTALL_HINT = <<~TEXT.freeze
|
|
29
|
+
Install the development packages first, for example:
|
|
30
|
+
macOS (Homebrew): brew install webp libpng jpeg-turbo libtiff
|
|
31
|
+
macOS (MacPorts): sudo port install webp libpng jpeg libtiff
|
|
32
|
+
Debian, Ubuntu: sudo apt-get install libwebp-dev libpng-dev libjpeg-dev libtiff-dev
|
|
33
|
+
Fedora: sudo dnf install libwebp-devel libpng-devel libjpeg-devel libtiff-devel
|
|
34
|
+
If they are installed somewhere else, point the build at them:
|
|
35
|
+
C_FLAGS="-I/path/to/include" LD_FLAGS="-L/path/to/lib"
|
|
36
|
+
TEXT
|
|
37
|
+
|
|
38
|
+
attr_reader :cflags, :ldflags, :libs
|
|
39
|
+
|
|
40
|
+
def initialize(env: ENV, prefixes: LIBRARY_PREFIXES, dependencies: DEPENDENCIES)
|
|
41
|
+
@env = env
|
|
42
|
+
@dependencies = dependencies
|
|
43
|
+
# user flags go first, so they win over the default prefixes
|
|
44
|
+
@cflags = split(env['C_FLAGS'])
|
|
45
|
+
@ldflags = split(env['LD_FLAGS'])
|
|
46
|
+
prefixes.each do |prefix|
|
|
47
|
+
include_dir = File.join(prefix, 'include')
|
|
48
|
+
lib_dir = File.join(prefix, 'lib')
|
|
49
|
+
@cflags << "-I#{include_dir}" if File.directory?(include_dir)
|
|
50
|
+
@ldflags << "-L#{lib_dir}" if File.directory?(lib_dir)
|
|
51
|
+
end
|
|
52
|
+
@libs = dependencies.map { |dependency| "-l#{dependency.library}" }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Defines the compile task and a check that runs before it. Call it inside
|
|
56
|
+
# a namespace to keep the task names apart.
|
|
57
|
+
def self.define_tasks(library_name, env: ENV)
|
|
58
|
+
require 'rake'
|
|
59
|
+
require 'ffi-compiler/compile_task'
|
|
60
|
+
config = new(env: env)
|
|
61
|
+
|
|
62
|
+
Rake.application.last_description = "Check that libwebp and the image libraries can be found"
|
|
63
|
+
Rake::Task.define_task(:check_dependencies) { config.check_dependencies! }
|
|
64
|
+
# defined before CompileTask, so the check runs before compiling
|
|
65
|
+
Rake::Task.define_task(default: :check_dependencies)
|
|
66
|
+
|
|
67
|
+
FFI::Compiler::CompileTask.new(library_name) { |task| config.apply(task) }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def apply(task)
|
|
71
|
+
@cflags.each { |flag| task.cflags << Shellwords.escape(flag) }
|
|
72
|
+
@ldflags.each { |flag| task.ldflags << Shellwords.escape(flag) }
|
|
73
|
+
task.libs.concat(@libs)
|
|
74
|
+
task
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def missing_dependencies
|
|
78
|
+
@dependencies.reject { |dependency| links?(dependency) }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def check_dependencies!
|
|
82
|
+
missing = missing_dependencies
|
|
83
|
+
return true if missing.empty?
|
|
84
|
+
|
|
85
|
+
names = missing.map { |dependency| "#{dependency.name} (#{dependency.header}, -l#{dependency.library})" }
|
|
86
|
+
raise MissingDependencyError, "webp-ffi cannot find: #{names.join(', ')}.\n#{INSTALL_HINT}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# compiles and links a tiny program the same way the extension is built
|
|
92
|
+
def links?(dependency)
|
|
93
|
+
Dir.mktmpdir("webp-ffi-check") do |dir|
|
|
94
|
+
source = File.join(dir, "check.c")
|
|
95
|
+
requirement = dependency.requirement ? "#if !(#{dependency.requirement})\n#error too old\n#endif\n" : ""
|
|
96
|
+
File.write(source, <<~C)
|
|
97
|
+
#include <stdio.h>
|
|
98
|
+
#include <#{dependency.header}>
|
|
99
|
+
#{requirement}int main(void) { #{dependency.code} return 0; }
|
|
100
|
+
C
|
|
101
|
+
command = compiler + split(@env['CFLAGS']) + @cflags +
|
|
102
|
+
[source, "-o", File.join(dir, "check")] +
|
|
103
|
+
split(@env['LDFLAGS']) + @ldflags + ["-l#{dependency.library}"]
|
|
104
|
+
_output, status = Open3.capture2e(*command)
|
|
105
|
+
status.success?
|
|
106
|
+
end
|
|
107
|
+
rescue SystemCallError
|
|
108
|
+
false
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def compiler
|
|
112
|
+
split(@env['CC'] || RbConfig::CONFIG['CC'] || 'cc')
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def split(flags)
|
|
116
|
+
flags ? Shellwords.split(flags) : []
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|