Module: Oximg

Defined in:
lib/oximg.rb,
lib/oximg/binary.rb,
lib/oximg/version.rb

Overview

Ruby bindings for oximg (https://github.com/oximg/oximg): high-performance image compression and resizing, in-process work for a Ruby app without libvips or ImageMagick on the box.

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, ...}

Rails/ActiveStorage integration, and URL building for a remote oximg server, live in the separate oximg-rails gem.

Defined Under Namespace

Modules: Binary Classes: Error, ExecutableNotFound, ProcessingError

Constant Summary collapse

FORMATS =

Output formats the CLI accepts for -f/--format.

%i[jpg jpeg png webp avif].freeze
PRESETS =

Encoder profiles: jpegli maximizes quality per byte (the default), mozjpeg's fast/small trade differently.

%i[jpegli fast small].freeze
CONTENT_TYPE_FORMATS =
{
  "image/jpeg" => :jpeg,
  "image/png" => :png,
  "image/webp" => :webp,
  "image/avif" => :avif
}.freeze
PROBE_LINE =
/:\s+(\S+)\s+(\d+)x(\d+)\s+\(\d+\s+stored\s+pixels\)\s*\z/
VERSION =

The gem version is the version of the oximg it bundles — the same convention the npm package uses. A gem-only fix therefore waits for the next oximg release rather than shipping under a version that names a binary it does not carry. Oximg.version reports what the resolved executable actually is, which can differ when it comes from PATH.

"0.11.0"

Class Method Summary collapse

Class Method Details

.available?Boolean

True when an executable can be found — for a boot-time check, or for code that falls back to another processor.

Returns:

  • (Boolean)


68
69
70
# File 'lib/oximg.rb', line 68

def available?
  Binary.available?
end

.executableObject

Absolute path to the executable in use; raises ExecutableNotFound if there is none.



56
57
58
# File 'lib/oximg.rb', line 56

def executable
  Binary.path
end

.executable=(path) ⇒ Object

Pins the executable, overriding discovery and OXIMG_BIN.



61
62
63
64
# File 'lib/oximg.rb', line 61

def executable=(path)
  Binary.reset!
  Binary.path = path
end

.probe(source) ⇒ Object

Header-only inspection — no pixels are decoded. Returns {content_type:, format:, width:, height:}, where the dimensions are the stored ones (an EXIF rotation is not applied).

Raises:



97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/oximg.rb', line 97

def probe(source)
  out, = Binary.run("probe", expand(source, "source"))
  match = out.match(PROBE_LINE)
  raise ProcessingError, "unparsable probe output: #{out.inspect}" unless match

  {
    content_type: match[1],
    format: CONTENT_TYPE_FORMATS[match[1]],
    width: Integer(match[2]),
    height: Integer(match[3])
  }
end

.resize(source, destination, width: 0, height: 0, quality: nil, format: nil, preset: nil) ⇒ Object

Fits source within width x height and writes the re-encoded image to destination. The source is never enlarged, and a zero axis is unconstrained — width: 750 alone is width-only, and the default 0 x 0 re-encodes at the source's own size, which is how you ask for compression without a resize.

The output format follows format:, else +destination+'s extension, else the source's own format. Returns the destination path.



87
88
89
90
91
92
# File 'lib/oximg.rb', line 87

def resize(source, destination, width: 0, height: 0, quality: nil, format: nil, preset: nil)
  output = expand(destination, "destination")
  Binary.run(*resize_argv(source, output,
    width: width, height: height, quality: quality, format: format, preset: preset))
  output
end

.resize_argv(source, destination, width: 0, height: 0, quality: nil, format: nil, preset: nil) ⇒ Object

The argv resize would run, minus the executable. Public because it is the honest way to see what a call turns into — and the way to test that without a binary present.



113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/oximg.rb', line 113

def resize_argv(source, destination, width: 0, height: 0, quality: nil, format: nil, preset: nil)
  argv = [
    "resize",
    expand(source, "source"),
    dimension(width, "width").to_s,
    dimension(height, "height").to_s,
    expand(destination, "destination")
  ]
  argv.push("-q", bounded(quality, "quality", 1, 100).to_s) unless quality.nil?
  argv.push("-f", token(format, FORMATS, "format")) unless format.nil?
  argv.push("--preset", token(preset, PRESETS, "preset")) unless preset.nil?
  argv
end

.versionObject

Version of the executable, which is not necessarily this gem's VERSION when the binary comes from PATH.



74
75
76
# File 'lib/oximg.rb', line 74

def version
  Binary.version
end