AgX
An open-source photo editing library and CLI written in Rust, with a portable, human-readable preset format.
AgX is the chemical notation for silver halide — the light-sensitive compound in photographic film. Ag is silver, X is a halide. Silver halide crystals capture light and undergo oxidation-reduction to form the latent image — the invisible precursor to every photograph ever developed in a darkroom. A photo editing library written in Rust, named after the chemistry that started it all.
Philosophy
AgX is a preset-first photo editing tool. The goal is not to replace Lightroom or Capture One — it's to make photo editing accessible through composable, shareable, human-readable presets.
- Presets as recipes: A preset is a complete editing recipe — tone, white balance, HSL, LUT — in a single TOML file. Presets can extend other presets, override selectively, and compose into layered looks.
- Batch-oriented: Apply a look to an entire shoot in one command. The CLI and library API are designed for processing many images quickly, not pixel-level retouching.
- Shareable by design: Presets are plain text, version-controllable, and portable. The format is meant to be shared, forked, and remixed — the foundation for a preset marketplace.
- CLI and API first: AgX is a library and a command-line tool. UI may come later, but the core value lives in the editing engine and preset format.
Features
- Tone adjustments: exposure, contrast, highlights, shadows, whites, blacks
- White balance: temperature and tint shifts
- HSL adjustments: per-channel hue, saturation, and luminance targeting
- 3D LUT support: apply
.cubeLUT files for color grading and film emulation - EXIF orientation: automatic orientation correction for standard formats (JPEG, PNG, TIFF)
- Preset composability: presets can
extenda base preset, with Option-style inheritance - Raw format support: decode CR2, CR3, NEF, ARW, RAF, DNG, and 1000+ camera formats via LibRaw
- Batch processing: process entire directories with parallel execution via rayon
- TOML presets: human-readable, shareable, version-controllable editing presets
- Metadata preservation: EXIF and ICC profiles carried through the pipeline
- Library + CLI: use as a Rust library or through the command-line interface
Install
The CLI is published as agx-cli. The library is published as agx-photo (the bare agx name is taken on crates.io by an unrelated crate).
For source builds, see the contributing docs.
Sample Images
These before/after pairs are from the e2e test suite — each processed through the full decode, engine render, and encode pipeline with a film-inspired look preset and 3D LUT.
Before & After
| Original | Look | Result |
|---|---|---|
![]() |
Portra 400 | ![]() |
![]() |
Neo Noir | ![]() |
![]() |
B&W High Contrast | ![]() |
Quick Start
# Apply a preset to an image
# Edit with inline parameters
# Apply a .cube LUT
# Combine adjustments with a LUT
# Process a raw file (CR2, NEF, ARW, DNG, etc.)
# Batch process a directory
# Set JPEG output quality (default: 92)
# Specify output format explicitly
Metadata Preservation
Metadata (EXIF, ICC profiles) is automatically preserved from input to output:
- JPEG/PNG: Lossless byte-level copy via
img-parts - TIFF-based raw (CR2, NEF, DNG, ARW): EXIF extracted via
kamadak-exif - Non-TIFF raw (RAF, RW2, CR3): Key shooting data reconstructed from LibRaw fields
Preset Format
Presets are TOML files with a simple, declarative structure:
[]
= "Golden Hour"
= "1.0"
= "agx"
[]
= 0.5 # stops, -5.0 to +5.0
= 15.0 # -100 to +100
= -30.0 # -100 to +100
= 25.0 # -100 to +100
= 10.0 # -100 to +100
= -5.0 # -100 to +100
[]
= 40.0 # warm (+) / cool (-)
= 5.0 # magenta (+) / green (-)
[]
= 10.0 # -180 to +180
= 15.0 # -100 to +100
= -5.0 # -100 to +100
[]
= "film-emulation.cube" # resolved relative to the preset file
Presets can extend a base preset with extends:
= "base_cinematic.toml" # inherit parameters, override selectively
[]
= 30.0 # override base contrast
Missing values default to neutral (no change). See example/presets/ for more examples.
Library Usage
use ;
use decode;
use encode_to_file;
// Decode an image (auto-detects format: JPEG, PNG, TIFF, CR2, NEF, DNG, etc.)
let image = decode.unwrap;
// Create engine and apply a preset
let mut engine = new;
let preset = load_from_file.unwrap;
engine.apply_preset;
// Or set parameters directly
engine.params_mut.exposure = 1.0;
engine.params_mut.contrast = 20.0;
// Apply a .cube LUT
let lut = from_cube_file.unwrap;
engine.set_lut;
// Render and save
let result = engine.render;
encode_to_file.unwrap;
Project Structure
agx/
├── crates/
│ ├── agx/ # core library
│ │ └── src/
│ │ ├── adjust/ # adjustment algorithms (per-pixel)
│ │ ├── decode/ # image decoding (sRGB → linear) + EXIF orientation
│ │ ├── encode/ # image encoding (linear → sRGB)
│ │ ├── engine/ # rendering engine
│ │ ├── lut/ # 3D LUT parsing and interpolation
│ │ ├── preset/ # TOML preset serialization + composability
│ │ └── error.rs # error types
│ ├── agx-cli/ # CLI wrapper (edit, apply, batch-edit)
│ ├── agx-e2e/ # e2e test suite (golden file comparison)
│ └── agx-lut-gen/ # dev tool for generating .cube LUT files
├── example/ # sample images, presets, and LUTs
├── scripts/ # verify.sh, e2e.sh
└── docs/ # design docs, ideas, and contributing guides
Architecture
The engine uses an always-re-render-from-original model: the original image is stored immutably, and every render applies all adjustments from scratch. This makes the system order-independent from the user's perspective — presets are purely declarative parameter values, not operation sequences.
All processing happens in sRGB color space. Exposure and white balance operate in linear sRGB; contrast, highlights, shadows, whites, blacks, HSL, and LUTs operate in sRGB gamma space. See docs/book/src/reference/concepts/color-spaces.md for a detailed explanation.
Testing
# Fast checks (format, clippy, unit tests, architecture tests, doc links)
# Full e2e suite (builds CLI in release, runs 54 golden comparisons)
# Regenerate golden files
GOLDEN_UPDATE=1
The e2e suite tests every fixture image against 9 film-inspired look presets (6 color + 3 B&W), each combining parameter adjustments with generated 33x33x33 .cube LUTs. See crates/agx-e2e/README.md for details.
Building with Raw Support
Raw format decoding requires LibRaw installed on your system:
# macOS
# Ubuntu/Debian
The CLI enables raw support by default. To use the library without raw support (no LibRaw dependency):
# Cargo.toml — no "raw" feature, only standard formats
[]
= "0.1"
The library is published as agx-photo but its Rust crate name remains agx, so existing use agx::... imports work unchanged. With raw support:
[]
= { = "0.1", = ["raw"] }
License
Licensed under either of
at your option.





