Skip to content

Processing Steps

Bartłomiej Mucha edited this page Sep 13, 2026 · 4 revisions

Optional processing steps

Two steps, both off by default, each living at the seam its coordinate space demands: colour correction in the page domain (before scale, so it can be excluded per page) and text/bubble overlays in the strip domain (after assembly, so an overlay can span a slice boundary).

A project using neither renders byte-identically to a build without them. That is not a courtesy — it is what lets the steps be added to a released pipeline without invalidating anyone's output.

Render flow


ColourCorrection (optional processing step — page domain)

iccToSRGB         : bool             // convert to sRGB via the embedded ICC profile (P3→sRGB gamut fix)
curves            : ColourCurves     // per-channel master + R/G/B tone curves (empty = identity)
brightness        : float            // additive lift, ~[-1,1]; 0 = no change
contrast          : float            // multiplicative around mid-grey; 1 = no change
saturation        : float            // chroma scale; 1 = no change, 0 = greyscale
excludedInputUids : string[]         // input uids this grade skips (e.g. a title/end page)

ColourCurves holds four CurvePoint[] (master, r, g, b); a CurvePoint is { x, y } in normalised [0,1]. The effective map is channelCurve(masterCurve(v)). Point operations, so a project-wide grade equals a per-page one — which is what lets it be project-wide yet allow exclusions.

There is no master toggle: a neutral grade is no grade. isNeutral(cc) — every knob at its no-change value and no curve points — is the single test the pipeline gates on, the staleness signature gates on, and a consumer previewing the grade gates on, so none of them can answer "is this project graded" differently. It is what keeps an ungraded project byte-identical to a build without this step. A stored flag beside the values could disagree with them, and a consumer would then have two answers and no rule for which wins; switching a grade off is resetting it. excludedInputUids is not consulted — excluding pages from a grade that changes nothing excludes them from nothing.

StripOverlay (optional processing step — strip domain)

uid        : uid        // "ovl-…", minted by ProjectItem::addOverlay()
assetPath  : string     // absolute path to the consumer's artwork — PNG or SVG (lib never copies it)
sha256     : string     // content hash — feeds dedup + staleness
x, y       : int        // top-left placement, in the pixels the overlay was authored at
enabled    : bool       // per-overlay toggle
blend      : BlendMode  // Over (default) | Multiply | Screen | Overlay | Darken | Lighten

Positioned in the continuous strip's coordinates, so one overlay can straddle a slice cut and lands on both slices (§7). Treated as a resource inventory parallel to input files (see ProjectItem).

Raster or vector. libvips dispatches on content, so a PNG loads through its own loader and an SVG through librsvg; neither the compositor nor its caller has to know which arrived. A vector asset is the better choice for authored text and bubbles, because it answers a re-profiled chapter with a genuine re-render rather than a resample — and an SVG carries only resolved geometry, so this does not put a text engine in the library.

Re-profiling is a no-op on the record. Placement and artwork width are stored as fractions of the render's target width (xFrac, yFrac, wFrac), so 0.42 means the same place at 800 px and at 1600 px and there is no authored width to record, carry or lose. resolveOverlayAnchors() turns the fractions into absolute strip pixels against the width in hand and returns PlacedOverlay, which the compositor draws — one conversion, one call, so size and placement cannot be derived from two different scales. wFrac == 0 means "the asset's own pixel size", which is what artwork placed without a stated size wants.

Optional processing steps (two seams)

Two optional, non-destructive steps hook into the pipeline at two distinct seams, in two different coordinate spaces (mirroring DaVinci Resolve's Color page vs Fusion). Both are opt-in / default-off, so a project that uses neither renders byte-identically to a build without them, and both are passed to the pipeline as RenderRequest fields (colourCorrection, stripOverlays).

  • Page domain — colour correction. Between scale and strip.append, for each input not in colourCorrection.excludedInputUids: ImageIO::decode(convertToSRGB = iccToSRGB) then ColourCorrector::applyToBuffer(buffer, cc). The CC-off path is exactly the historical load/scale, untouched. Being point operations, per-page grading equals grading the whole strip, so the grade is project-wide while exclusions and per-source ICC still work.
  • Strip domain — text/bubble overlays. Inside the slice loop, before each slice is saved: StripOverlayCompositor::composite(slice.image, slice.stripTopY, overlays). Overlays are positioned in strip coordinates; the compositor draws each onto every slice its box intersects (clipped by libvips), so an overlay straddling a cut appears on both adjacent slices. A slice no overlay intersects is returned unchanged.

Staleness (a fourth axis). Editing the grade or an overlay changes output bytes but touches no input or output file, so — like the output-profile signature — a stored fingerprint is what catches it. Models::processingConfigSignature(colourCorrection, overlays) produces that fingerprint (empty when nothing is configured, so a pre-feature project is never falsely invalidated); it is stored as ProjectItem::processingSignature and folded into the consumer's "config changed → full re-render" decision, alongside the output-profile / canvas / input-composition axes (§7.5.3).

Extensibility. The two steps are described by a compile-time table Models::k_processingStepDefs (id, name, kind, domain) — the enumerable contract a GUI renders a step stack from. A future step is a new config struct + a stateless Core applier at one of the two seams + one table row + a signature contribution, with no change to the existing steps.


See also: Home · Workspace · Project · Render · Preview · Canvas-Profiles · Processing-Steps · Components · CLI · Platform · Roadmap · Diagrams

Clone this wiki locally