ruby-vips
| Language | Ruby |
|---|---|
| License | MIT (libvips: LGPL-2.1) |
| Repository | github.com/libvips/ruby-vips |
| Documentation | ruby-vips API, webpsave options |
| Depends on | libvips (8.x) built with libwebp, loaded through ffi |
| Read / write | yes / yes, lossy, lossless, alpha, animation, metadata |
ruby-vips calls libvips, a fast image processing library that streams images in small pieces and uses little memory. libvips reads and writes WebP with libwebp, including animated files. It’s what Rails Active Storage and the image_processing gem use by default to make WebP variants.
Installation#
Install libvips first, then the gem:
# Debian / Ubuntu
sudo apt install libvips42
# macOS
brew install vips
gem install ruby-vips
Basic usage: convert to WebP#
require "vips"
image = Vips::Image.new_from_file("photo.jpg")
puts "#{image.width} x #{image.height}, #{image.bands} bands"
image.webpsave("photo.webp", Q: 80, effort: 6) # Q 1-100, effort 0 (fast) .. 6 (smallest)
# or let the extension choose the saver, options after "[":
image.write_to_file("photo.webp[Q=80]")
Reading a WebP is the same Vips::Image.new_from_file("in.webp").
How to save a lossless WebP#
image = Vips::Image.new_from_file("logo.png")
image.webpsave("logo.webp", lossless: true)
# near-lossless: smaller files, almost no visible change
image.webpsave("logo-nl.webp", near_lossless: true, Q: 60)
How to make a WebP thumbnail#
thumbnail decodes at reduced size where possible and is the fastest way to resize:
thumb = Vips::Image.thumbnail("photo.jpg", 400) # 400 px wide, keeps aspect ratio
thumb.webpsave("thumb.webp", Q: 75, smart_subsample: true)
How to read the frames of an animated WebP#
Load all pages with n: -1; frames are stacked vertically in one tall image:
anim = Vips::Image.new_from_file("anim.webp", n: -1)
frame_height = anim.get("page-height")
frames = anim.height / frame_height
puts "#{frames} frames, delays #{anim.get("delay").inspect} ms, loop #{anim.get("loop")}"
frames.times do |i|
anim.crop(0, i * frame_height, anim.width, frame_height).write_to_file("frame#{i}.png")
end
Saving that tall image with webpsave writes an animated WebP again; resize it with Vips::Image.thumbnail("anim.gif[n=-1]", 200) to convert GIF to animated WebP.
How to get WebP dimensions without decoding#
libvips opens files lazily, so new_from_file reads only the header until you access pixels:
image = Vips::Image.new_from_file("in.webp")
puts image.width, image.height, image.has_alpha?
Tips#
- Pass
access: :sequentialtonew_from_filefor one-pass conversions of large images. - Metadata (Exif, XMP, ICC) is copied by default; drop it with
image.write_to_file("out.webp[Q=80,keep=none]")(libvips 8.15+).