pura-png
A pure Ruby PNG decoder/encoder without additional image-processing libraries.
Part of the pura-* series — pure Ruby image codec gems.
Features
- Color types: Grayscale (0), RGB (2), Indexed (3), Grayscale+Alpha (4), RGBA (6)
- Bit depths: 1, 2, 4, 8, 16
- All 5 filter types: None, Sub, Up, Average, Paeth
- Zlib compression/decompression via Ruby stdlib
- Image resizing (bilinear / nearest-neighbor / fit / fill)
- Uses Ruby standard-library
zlibfor compression; no additional image library needed - CLI tool included
Installation
gem install pura-png
Usage
require "pura-png"
# Decode
image = Pura::Png.decode("photo.png")
image.width #=> 800
image.height #=> 600
image.pixels #=> Raw RGB byte string
image.pixel_at(x, y) #=> [r, g, b]
# Encode
Pura::Png.encode(image, "output.png")
# Resize
thumb = image.resize(200, 200)
fitted = image.resize_fit(800, 600)
fill = image.resize_fill(200, 200)
# From raw pixels
image = Pura::Png::Image.new(width, height, rgb_pixels_string)
Pura::Png.encode(image, "output.png")
CLI
pura-png decode input.png --info
pura-png decode input.png --out pixels.dat
pura-png encode input.dat --width W --height H --out output.png
pura-png resize input.png --width 200 --height 200 --out thumb.png
pura-png resize input.png --fit 800x600 --out fitted.png
Benchmark
These historical measurements include ffmpeg process startup. They do not compare against an in-process C codec or establish Rails pipeline throughput.
400×400 image, Ruby 4.0.2 + YJIT.
Decode
| Decoder | Time | Notes |
|---|---|---|
| chunky_png (Ruby) | 59 ms | Uses Zlib C extension |
| ffmpeg (C) | 60 ms | C |
| pura-png | 111 ms | Pure Ruby (uses Zlib C ext for compression) |
Encode
| Encoder | Time | vs ffmpeg |
|---|---|---|
| pura-png | 52 ms | 0.8× — faster than ffmpeg! |
| ffmpeg (C) | 61 ms | — |
pura-png is slower than chunky_png for decoding, but supports more color types and bit depths, and is part of the pura-* ecosystem for seamless format conversion. Both use Ruby's built-in Zlib (a C extension) for compression.
Why pure Ruby?
gem installand go — nobrew install, noapt install, no C compiler needed- Non-interlaced PNG color types and valid bit depths; decoded pixels are 8-bit RGB
- Part of pura-* — convert between JPEG, PNG, BMP, GIF, TIFF, WebP seamlessly
Related gems
| Gem | Format | Status |
|---|---|---|
| pura-jpeg | JPEG | ✅ Available |
| pura-png | PNG | ✅ Available |
| pura-bmp | BMP | ✅ Available |
| pura-gif | GIF | ✅ Available |
| pura-tiff | TIFF | ✅ Available |
| pura-ico | ICO | ✅ Available |
| pura-webp | WebP | ✅ Available |
| pura-image | All formats | ✅ Available |
Pixel model and limitations
Images contain 8-bit RGB pixels. Alpha and tRNS transparency are discarded, not composited. Adam7 interlacing is unsupported. Sixteen-bit samples are reduced to eight bits.
crop(x, y, width, height) requires integer coordinates, positive dimensions, and a region entirely inside the image; invalid regions raise ArgumentError.
Decode limits
Pura::Png.decode(input, max_input_bytes: 64 * 1024 * 1024, max_pixels: 40_000_000, max_decoded_bytes: 256 * 1024 * 1024) accepts a path or binary data.
All limits must be positive integers. These defaults bound input, pixel count, and decoded data size; they are not a cap on the Ruby process's total memory or CPU time.
Invalid image data raises Pura::Png::DecodeError; exceeding a configured limit raises its subclass Pura::Png::LimitExceeded.
PNG decoding validates chunk CRCs and the IHDR layout, rejects invalid color/depth combinations, and checks scanline length during bounded decompression.
License
MIT