pura-image
Pure Ruby image processing library with zero C extension dependencies. Bundles all pura-* format gems and provides an ImageProcessing adapter for Rails Active Storage.
Supported Formats
| Format | Decode | Encode | Gem |
|---|---|---|---|
| JPEG | ✅ | ✅ | pura-jpeg |
| PNG | ✅ | ✅ | pura-png |
| BMP | ✅ | ✅ | pura-bmp |
| GIF | ✅ | ✅ | pura-gif |
| TIFF | ✅ | ✅ | pura-tiff |
| ICO/CUR | ✅ | ✅ | pura-ico |
| WebP | ✅ | ✅ | pura-webp |
Format auto-detection by magic bytes — no extension guessing needed for decode.
Installation
gem install pura-imageUsage
require "pura-image"
# Load any format (auto-detected from magic bytes)
image = Pura::Image.load("photo.jpg")
image = Pura::Image.load("icon.webp")
image.width #=> 800
image.height #=> 600
# Save to any format (detected from extension)
Pura::Image.save(image, "output.png")
# Convert between formats
Pura::Image.convert("input.bmp", "output.jpg", quality: 85)
Pura::Image.convert("photo.tiff", "photo.png")Image Operations
All operations from the image_processing gem are supported:
image = Pura::Image.load("photo.jpg")
# Resize
image.resize_to_limit(800, 600) # downsize only, keep aspect ratio
image.resize_to_fit(400, 400) # resize to fit, keep aspect ratio
image.resize_to_fill(400, 400) # fill exact size, center crop excess
image.resize_and_pad(400, 400) # fit within bounds, pad with black
image.resize_to_cover(400, 400) # cover bounds, no crop
# Transform
image.crop(10, 10, 200, 200) # crop region
image.rotate(90) # rotate 90/180/270 degrees
image.grayscale # convert to grayscale
# Chain operations
result = Pura::Image.load("photo.jpg")
.resize_to_limit(800, 600)
.rotate(90)
.grayscale
Pura::Image.save(result, "thumb.jpg", quality: 80)Rails Active Storage Integration
Drop-in replacement for libvips/ImageMagick:
# Gemfile
gem "image_processing"
gem "pura-image"
# config/application.rb
config.active_storage.variant_processor = :puraModels and views stay exactly the same:
class User < ApplicationRecord
has_one_attached :avatar do |attachable|
attachable.variant :thumb, resize_to_limit: [200, 200]
end
end<%= image_tag user.avatar.variant(:thumb) %>No brew install vips. No apt install imagemagick. Just gem install and go.
ImageProcessing::Pura API
require "image_processing/pura"
# Same API as ImageProcessing::Vips
processed = ImageProcessing::Pura
.source("photo.jpg")
.resize_to_limit(400, 400)
.convert("png")
.call(destination: "output.png")
# Pipeline branching
pipeline = ImageProcessing::Pura.source("photo.jpg")
large = pipeline.resize_to_limit(800, 800).call(destination: "large.jpg")
medium = pipeline.resize_to_limit(500, 500).call(destination: "medium.jpg")
small = pipeline.resize_to_limit(300, 300).call(destination: "small.jpg")
# Validation
ImageProcessing::Pura.valid_image?("photo.jpg") #=> trueBenchmark
400×400 image, Ruby 4.0.2 + YJIT vs ffmpeg (C + SIMD).
Decode
| Format | pura-* | ffmpeg | vs ffmpeg |
|---|---|---|---|
| TIFF | 14 ms | 59 ms | 🚀 4× faster |
| BMP | 39 ms | 59 ms | 🚀 1.5× faster |
| GIF | 77 ms | 65 ms | ~1× (comparable) |
| PNG | 111 ms | 60 ms | 1.9× slower |
| WebP | 207 ms | 66 ms | 3.1× slower |
| JPEG | 304 ms | 55 ms | 5.5× slower |
Encode
| Format | pura-* | ffmpeg | vs ffmpeg |
|---|---|---|---|
| TIFF | 0.8 ms | 58 ms | 🚀 73× faster |
| BMP | 35 ms | 58 ms | 🚀 1.7× faster |
| PNG | 52 ms | 61 ms | 🚀 faster |
| JPEG | 238 ms | 62 ms | 3.8× slower |
| GIF | 377 ms | 59 ms | 6.4× slower |
5 out of 11 operations are faster than C (ffmpeg process-spawn overhead).
Why pure Ruby?
-
gem installand go — nobrew install, noapt install, no C compiler - Works everywhere Ruby works — CRuby, ruby.wasm, mruby, JRuby, TruffleRuby
- Edge/Wasm ready — browsers (ruby.wasm), sandboxed environments, no system libraries needed
-
Perfect for dev/CI — no ImageMagick/libvips setup.
rails new→ image upload → it just works - 7 formats, 1 interface — unified API across JPEG, PNG, BMP, GIF, TIFF, ICO, WebP
License
MIT