Image Processing Pipeline
The transformation phase, which runs after imgforge has validated the request and fetched the source. Read it alongside Processing Options: the order below is what makes directives interact the way they do.
plan ─▶ dpr ─▶ load ─▶ geometry ─▶ canvas ─▶ effects ─▶ encode
crop padding blur format
resize extend sharpen quality
background pixelate metadata
zoom
min-width/height
watermarkThe stages
- Plan normalization – Parsed directives become a plan with explicit defaults. A missing width or height becomes
0, which preserves aspect ratio. Quality defaults to85, EXIF auto-rotation starts enabled, and the background defaults to transparent or black depending on the output format. - DPR scaling – When
dpris above1.0, the resize width, height, and padding are multiplied by it.min-widthandmin-heightare not scaled, so a minimum expressed in CSS pixels stays in CSS pixels while the resize target moves to device pixels. - Image loading – libvips reads the source buffer, converts the colour profile when needed, and applies EXIF orientation unless
auto_rotate:false. - Geometry – Crop runs first, then resizing (
resize,size,width,height) using the activeresizing_type. Gravity positions the crop window and the fill canvas. Upscaling is refused unlessenlarge:true. - Canvas – Padding,
extend, andbackgroundapply after resizing, so they operate on the final viewport. Output formats without an alpha channel are flattened against the background colour. - Effects and safeguards – Blur, sharpen, pixelate, and zoom run after geometry.
min-widthandmin-heightcan trigger one more upscale if the image is still too small. Watermarks load here, clamped to the canvas; a watermark that cannot be fetched or decoded fails the request. - Encoding – The image is encoded to the requested format. An explicit
formatdirective beats the format implied by@extension. Quality follows thequalitydirective, defaulting to85for lossy codecs.
How options interact
- Resizing and padding – Padding is additive:
resize:fit:800:600withpadding:20yields an 840×640 canvas before flattening. Padding inheritsdprscaling, so check both together. - Crop and gravity –
crop:x:y:width:heightuses absolute coordinates and ignores gravity. Gravity only matters for the implicit crop thatfillperforms, and for watermark placement. - Zoom and minimums –
zoommultiplies the dimensions produced by resizing, and the minimum checks run afterward. Azoombelow 1.0 can still be pulled back up bymin-widthormin-height. - Watermark precedence – A
watermark_urlin the request beatsIMGFORGE_WATERMARK_PATH. If the directive repeats, the last one wins. rawmode – Skips the worker semaphore but changes nothing about the order above.
Failure modes
- Invalid numbers — negative widths, NaN, out-of-range blur sigma — are rejected with
400 Bad Requestbefore libvips runs. - The resolution and file-size guards apply to the source image and run before this pipeline starts. Nothing here —
dpr, padding, minimums — can trip them, and there is no ceiling on the size of the output you ask for. - Watermark fetches share the timeout and size limits of the main source. A failure fails the request.
Observability
| Metric | Covers |
|---|---|
image_processing_duration_seconds | This pipeline. |
image_operation_execution_duration_seconds | This pipeline plus source decoding and validation. |
image_operation_semaphore_wait_duration_seconds | Waiting for an imgforge worker permit. |
image_operation_blocking_queue_duration_seconds | Waiting for a Tokio blocking thread. |
processed_images_total{format="..."} | Throughput per output format. |
The two wait histograms separate imgforge worker saturation from blocking-pool saturation — see Performance Tips. Logs carry the request ID; pair them with Request Lifecycle when a transformation misbehaves.