Hwaro::Content::Processors::ImageProcessor
Constants
Only formats that stb can both read AND write back correctly.
Maximum pixel count to prevent excessive memory allocation (64 megapixels)
resize_image(op=…) operations and crop anchors.
(?:px\s*)? rather than (?:px)?\s*: the two \s* runs must not
be able to split the same whitespace, or a long run backtracks
quadratically into PCRE's match limit.
Instance methods
Read the intrinsic {width, height} of an image from its file header
without decoding pixel data (pure Crystal, no stb round-trip).
Supports the formats image? accepts (PNG/JPEG/BMP) plus GIF, WebP
and SVG (read-only formats, measured for content <img> sizes);
returns nil for anything unreadable so callers can fall back to
other heuristics. Used by the warm-build variant-reuse check: inferring
the source width from the largest on-disk variant silently ignored
newly configured LARGER widths (the variant set "matched" after
clamping to the stale inferred width).
Compute dominant color as a hex string (e.g., "#a3b2c1"). Channels: 1=gray, 2=gray+alpha, 3=RGB, 4=RGBA
Pixels are weighted by alpha: a transparent pixel's color bytes are invisible (usually black), so averaging them in darkened every logo on a transparent background. A fully transparent image has no color and gets the white its LQIP is flattened onto.
Generate LQIP data URI and dominant color from the thumbnail in one pass. Returns {data_uri, dominant_color_hex} or nil on failure.
EXIF Orientation (1-8) of the picture a decoder returns for path:
1 (upright, or no tag) for everything but a JPEG that carries one.
Turn decoded pixels (w×h, channels interleaved) upright for
EXIF orientation 2-8, into a freshly allocated buffer, and free
the input. stb decodes the stored pixels and the encoders write no
EXIF, so a variant cut from them would show a phone photo on its
side. Orientation 1 (and anything unknown) hands the input back.
Returns {pixels, width, height}; nil if the new buffer cannot be
allocated (the input is then untouched).
Combined resize + LQIP in a single decode pass. Returns {width_map, lqip_data_uri_or_nil, dominant_color_hex}
authored (when given) is asked about each <name>_<w>w.<ext> this
would write: true means a file the site itself publishes under that
name, which is left alone — that width is simply not generated.
extra names further output formats by its keys ("webp"); each
width is then also written as <name>_<w>w.<format> from the same
resized pixels, and the format's width => path map is filled in.
One fill / crop variant of source written to dest.
fill: scale to cover width×height, then crop to exactly that box
around anchor (may upscale — the box is the contract).
crop: no scaling; cut a width×height region at anchor, clamped
to the source size.
Returns the written {width, height}, or nil when the source cannot
be decoded or the variant cannot be written.