oximg gem
Ruby bindings for oximg — image compression and resizing in Rust. JPEG, PNG, WebP and AVIF, resized in linear light at measurably higher output quality than the usual stack (see the benchmarks).
The point of the gem: no libvips, no ImageMagick. Platform gems
bundle the executable, so bundle install is the whole installation —
no apt-get install libvips42, no ImageMagick policy file, no native
extension compiled against a system library that the next base-image
bump moves.
gem "oximg"Oximg.resize("in.jpg", "out.jpg", width: 750)Oximg.resize("in.jpg", "out.webp", width: 750, height: 500, quality: 80)Oximg.probe("in.jpg")#=> {content_type: "image/jpeg", format: :jpeg, width: 4000, height: 3000}A GIF source is read and probed like the others (format: :gif), but
nothing encodes GIF: it leaves as the destination’s format, WebP when
the name does not say, and :gif is not a format: you can ask for.
Resizing
Section titled “Resizing”Oximg.resize(source, destination, width: 0, height: 0, quality: nil, format: nil, preset: nil)The source is fitted within width x height and never enlarged. A
zero axis is unconstrained, so width: 750 alone is width-only — what
an srcset w descriptor means — and the default 0 x 0 re-encodes at
the source’s own size, which is how you ask for compression without a
resize:
Oximg.resize("photo.jpg", "smaller.jpg", quality: 70)| Option | Meaning |
|---|---|
width / height |
Non-negative Integers; 0 is an unconstrained axis |
quality |
1–100, default 80. JPEG quality |
format |
:jpg, :jpeg, :png, :webp, :avif. Defaults to the destination’s extension, else the source’s own format |
preset |
:jpegli (default — maximum quality per byte), :fast, :small |
resize returns the destination path and raises Oximg::ProcessingError
on failure, carrying the binary’s own message and exit status. Paths are
expanded before they reach the CLI, so an uploaded file called -q.jpg
cannot be read as a flag.
Everything the server tunes through OXIMG_* environment variables
applies here too, and is validated the same fail-closed way: a typo’d
knob is a startup error, never a silent default.
Where the binary comes from
Section titled “Where the binary comes from”Oximg.executable resolves, first hit wins:
Oximg.executable = "/path/to/oximg", orOXIMG_BIN— an explicit path always wins, and a wrong one raises instead of falling through to something else.- The binary bundled in this gem (platform gems ship one).
oximgon PATH — Homebrew,cargo install oximg, or a Docker image.
Oximg.available? #=> trueOximg.executable #=> "/…/gems/oximg-0.10.1-arm64-darwin/exe/oximg"Oximg.version #=> "0.10.1" # the binary's version, not the gem'sThe gem shells out to that binary — one process per image, which is noise next to the encode itself and invisible inside the background job where variants are generated anyway. An in-process native extension is a later option; it would not change the API above.
Rails and ActiveStorage integration — and URL building for a remote
oximg server, if you run one rather than processing locally — live
in the separate oximg-rails gem, mirroring how
imgproxy and imgproxy-rails are split.
Versioning
Section titled “Versioning”The gem version tracks the oximg release it bundles, the same
convention the npm package uses. Oximg.version reports what the
resolved binary actually is, which can differ when it comes from PATH.
License
Section titled “License”Apache-2.0, same as oximg. See LICENSE.