Skip to content

Latest commit

 

History

71 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jxl

A Pure Ruby JPEG XL decoder and lossless Modular encoder

Gem Version Downloads Ruby Ruby Version No Native Extensions License

Features · Installation · Quick Start · Decoding · Encoding · Development


jxl implements JPEG XL decoding and lossless Modular encoding entirely in Ruby. It supports Level 5 raw codestreams and containers without calling libjxl, FFI, or native extensions.

Features

  • Raw JPEG XL codestreams and jxlc/jxlp containers
  • Modular and VarDCT decoding, including progressive frames
  • Animation, blending, reference frames, and all EXIF orientations
  • Gaborish, EPF, upsampling, patches, splines, and noise
  • Alpha, Black/CMYK, SpotColor, Depth, and other extra channels
  • XYB, YCbCr, standard transfer functions, and embedded ICC profiles
  • PNG, PPM/PGM, PAM, PFM, PGX, and multi-frame NPY output
  • Packed RGBA8, RGBA16, and binary32 pixel output
  • Lossless RGB Modular encoding compatible with libjxl
  • Input, pixel-count, and decoded-memory limits

Installation

Add the gem to your application:

bundle add jxl

Or install it directly:

gem install jxl

Requirements

  • Ruby 3.2 or newer
  • YJIT is recommended for large images

Quick Start

Decode a JPEG XL image and write it as PNG:

require "jxl"

image = JXL.decode(File.binread("input.jxl"))
File.binwrite("output.png", JXL::IO::PNG.dump(image))

Encode an image as a lossless Modular codestream:

encoded = JXL.encode(image, bits_per_sample: 8)
File.binwrite("output.jxl", encoded)

The command-line tools provide the same basic workflow:

djxl input.jxl output.png
cjxl input.png output.jxl
jxlinfo input.jxl

Decoding

API

image = JXL.decode(
  File.binread("input.jxl"),
  color_space: :srgb,
  max_pixels: 100_000_000,
  max_memory: 2 << 30
)
Option Default Description
pixel_format :native Validates :native, :rgba8, :rgba16, or :float32 compatibility
color_space :original Returns :original, :srgb, or :linear_srgb colour values
desired_scale 1 Downsamples the returned image by 1, 2, 4, or 8
max_pixels 100_000_000 Rejects images exceeding the pixel limit
max_memory 2 GiB Rejects input whose decoded-memory estimate exceeds the limit
strict true Rejects non-finite decoded samples when enabled
apply_orientation true Applies the image orientation to decoded pixels
render_spot_colors true Composites spot-colour channels into the colour image

Read metadata without decoding pixels:

info = JXL.info(File.binread("input.jxl"))
puts "#{info.width}x#{info.height}"

Access native planes, extra channels, animation frames, or packed pixels from the decoded image:

image.channels
image.extra_channels
image.frames
image.to_rgba8
image.to_rgba16
image.to_float

Incremental Input

JXL::Decoder accepts compressed input in chunks and emits subscribed events:

decoder = JXL::Decoder.new.subscribe(:basic_info, :frame, :full_image)

File.open("input.jxl", "rb") do |file|
  decoder.feed(chunk) while (chunk = file.read(65_536))
end

decoder.finish

while (event = decoder.next_event)
  puts event.type
end

The event decoder can expose metadata before the complete input arrives, but currently buffers the full compressed image before emitting frame pixels.

Output Formats

Format Writer Notes
PNG JXL::IO::PNG 8-bit output with ICC profile
PPM/PGM JXL::IO::PPM Binary RGB or grayscale
PAM JXL::IO::PAM Supports alpha
PFM JXL::IO::PFM Floating-point RGB or grayscale
PGX JXL::IO::PGX Big-endian grayscale
NPY JXL::IO::NPY Floating-point, multi-frame capable

Encoding

The encoder writes lossless RGB Modular codestreams using RCT6, a Gradient predictor, and prefix entropy coding.

bytes = JXL.encode(image, bits_per_sample: 8)

The cjxl command accepts binary PPM/PGM and non-interlaced 8-bit PNG input:

cjxl input.png output.jxl

Encoded output round-trips through both this gem and libjxl. Lossy VarDCT encoding and JPEG recompression are not part of the version 1.0 encoder.

Compatibility

Version 1.0 passes all 23 cases in the current upstream Level 5 conformance manifest in lax pixel mode. The 20 mandatory cases that do not require optional JPEG bitstream reconstruction also pass the official strict checks, including metadata and original ICC validation.

The following optional features are not currently implemented:

  • JPEG bitstream reconstruction from jbrd
  • Brotli-compressed brob metadata
  • Preview rendering

strict: false only replaces non-finite decoded samples. Structural corruption continues to raise JXL::Error.

Development

Install dependencies and run the default test and lint tasks:

bundle install
bundle exec rake

Additional checks:

bundle exec rake fuzz:truncate
bundle exec rake fuzz:bitflip
bundle exec rake fuzz:box
bundle exec rake bench

Set JXL_CONFORMANCE_CASES to the conformance suite's main_level5.txt to enable RSpec reference comparisons. After running the official conformance runner with --results, print its measured results with:

JXL_CONFORMANCE_RESULTS=results.json bundle exec rake conformance:report

Contributing

Bug reports and pull requests are welcome at https://github.com/ydah/jxl-ruby.

License

Released under the MIT License.

About

Pure Ruby JPEG XL decoder and lossless Modular encoder with no native extensions.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages