ImagineProvider
Processes images locally using imagine/imagine with the GD, Imagick, or Gmagick driver. Writes processed files to a cache filesystem.
use Imagine\Gd\Imagine; // or Imagine\Imagick\Imagine / Imagine\Gmagick\Imagine
use Timber\Chainsaw\Provider\Imagine\ImagineProvider;
use Timber\Chainsaw\Source\FlysystemSourceAdapter;
$provider = new ImagineProvider(
sourceReader: new FlysystemSourceAdapter($sourceFilesystem),
cache: $cacheFilesystem,
cachePublicUrl: '/images/cache',
imagine: new Imagine(),
);
Imagine reads source bytes straight from a string and encodes back to a string — no temporary files on either side. Its GD driver converts palette images to truecolor on load, so palette sources encode to WebP/AVIF without a workaround.
Shared local-provider machinery
ImagineProvider and InterventionProvider extend the same AbstractLocalProvider base, so everything backend-independent behaves identically:
- Variant cache + existence checks — three-rung ladder (in-memory, optional PSR-6
existsPool, cache filesystem). See Existence cache — the same guidance (remote cache storage only) applies here. - Decompression-bomb guard — optional
maxSourcePixelsceiling enforced before decoding. See Source pixel budget. Format::Auto— resolved by anAutoFormatStrategyInterfaceyou inject (autoFormat:); throws without one. See Advanced > Format negotiation.- Data URIs — implements
InlineProviderfor$image->toDataUri(). - Purging — implements
Purgeable;purge($source)deletes every cached variant and evicts matching PSR-6 entries. - Events — dispatches
PreProcess/PostProcesson cache miss when a PSR-14dispatcher:is wired. - Cache addressing — owned by a
PathStrategyInterface(defaultHashedPathStrategy).
Manipulation support
| Manipulation | Supported | Notes |
|---|---|---|
| Width | ✅ | |
| Height | ✅ | |
| Scale | ✅ | |
| Cover | ✅ | Compass + focal anchors; detection anchors (smart/face/…) throw |
| ManualCrop | ✅ | |
| Contain | ✅ | |
| Pad | ✅ | Background painted in the letterbox bars only |
| Stretch | ✅ | |
| CropToRatio | ✅ | |
| PadToRatio | ✅ | |
| Trim | ❌ | Throws UnsupportedManipulator |
| Blur | ✅ | |
| Sharpen | ✅ | |
| Brightness | ✅ | |
| Contrast | ❌ | Throws UnsupportedManipulator |
| Gamma | ✅ | |
| Pixelate | ❌ | Throws UnsupportedManipulator |
| Greyscale | ✅ | |
| Sepia | ❌ | Throws UnsupportedManipulator |
| Saturation | ✅ | Shared Raster\Hsl codec via the native GD/Imagick resource |
| Hue | ✅ | Shared Raster\Hsl codec via the native GD/Imagick resource |
| Negate | ✅ | |
| Background | ✅ | |
| Border | ✅ | |
| Flip | ✅ | |
| Rotate | ✅ | Arbitrary angles with optional background fill |
| AutoOrient | ✅ | EXIF-based |
| Watermark | ✅ | WatermarkFromUrl only; pixel and percent units computed locally |
| BlurHash | ✅ | Shared Raster\BlurHash codec; single-frame LQIP |
| ThumbHash | ✅ | Shared Raster\ThumbHash codec; single-frame LQIP |
| Dither | ✅ | Shared Raster\Dither codec |
Unsupported manipulators (Trim, Contrast, Pixelate, Sepia) are intentionally unregistered — Imagine has no native primitive for them — and throw UnsupportedManipulator. If you need them locally, use InterventionProvider, which supports every manipulator in the library.
Anchor support: Anchor::compass() and Anchor::focal() work; detection-based anchors (smart(), entropy(), attention(), face(), object()) throw. Server-default watermarks (WatermarkFromServerDefault) throw — only URL watermark sources are supported.
Encoding
| Format | Supported |
|---|---|
| JPEG | ✅ |
| PNG | ✅ |
| WebP | ✅ (capability-probed, lazy) |
| AVIF | ✅ (capability-probed, lazy) |
| GIF | ✅ |
| Auto format | ✅ (needs an injected AutoFormatStrategy) |
| Quality | ✅ |
JPEG/PNG/GIF are universally available and never probed. WebP and AVIF vary by build, so ImagineFormatSupport runs a one-off 1×1 trial encode per format and memoizes the result. An explicit request for a format the driver can’t encode throws UnsupportedOperation. Pass your own formatSupport: to override or skip probing.
Animation
Animated GIF/WebP handling depends on the driver:
- Imagick driver — geometry-only chains (resize/crop-based manipulators) keep every frame, and encode animated output when the target format is GIF or WebP. Frame delays are preserved; the loop count is forced to infinite by Imagine’s
animate(). Handlers built on single-frame APIs (effects, paste, rotate, flip) freeze to frame 0, as does encoding to a static target format (JPEG/PNG/AVIF).Saturation/Huebypass Imagine’s single-frame APIs and modulate the native handle per frame, so they keep animation too. - GD driver — GD has no multi-frame support; animated sources decode to their first frame.
BlurHash/ThumbHash placeholders always flatten to frame 0 before encoding (single-frame LQIP by design).
Notes
- Why pick Imagine over Intervention? Mostly when imagine/imagine is already in your dependency tree, or you need the Gmagick driver. Feature-wise it’s a strict subset of
InterventionProvider(27 of 31 manipulators, no detection anchors, no Vips driver). - The
qualityencoding default is 85 when noEncodingquality is set. - Pad paints the background into the letterbox bars only, never over source pixels.