Repository navigation
Conversation
Color Lookup adjustment layers load .cube (1D/3D, domains, shapers) and .3dl tables, interpolate tetrahedrally on the CPU and GPU, keep the file inside the document and map to PSD's clrL block. File > Export > Color Lookup Table bakes the adjustment stack (and optionally the active RAW layer's per-pixel Camera Raw colour) through the compositor into a .cube. Camera Raw gains shadows/midtones/highlights/global grading wheels with blending and balance, stored beside ScRw in a private ScCg block. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Saved-action shape validation required exactly the fifteen global Camera Raw values, so every RAW development step recorded with the grading values was refused. Accept the grading keys with their ranges, still requiring all fifteen global ones and rejecting unknown keys. LutTable now shares its source bytes as Arc<Vec<u8>>, so the browser's picked-file Arc is used without copying the table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IAmJSD
force-pushed
the
feat/luts-colour-grading
branch
from
October 4, 2026 17:51
2a31588 to
12efea6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
.cube(1D, 3D up to 129³,DOMAIN_MIN/MAX, Resolve-style 1D shaper + 3D withLUT_*_INPUT_RANGE) and.3dl(blue-fastest, bit depth from theMeshheader or inferred; uneven meshes such as 0, 64, …, 1023 go through a shaper). It uses tetrahedral interpolation. Input can be Document values (what Photoshop does) or Linear light. Opacity, blend mode, mask and clipping come from the layer as usual..cube. It does this by compositing an identity lattice through the active compositor. It can optionally fold in the active RAW layer's per-pixel Camera Raw colour settings.Design notes
Storage. The LUT file's original bytes live in the layer's params JSON, zlib-compressed and base64-encoded, so documents are self-contained. The parsed table is shared behind an
Arc.Caching.
schist_adjustments::resolvecaches large payloads, keyed by their exact bytes (full compare). The compositor resolves params per tile and would otherwise re-parse the table hundreds of times a frame.GPU. A new direct-adjustment kind
D_COLOR_LOOKUP(19) sends the shaper and lattice themselves as coefficients, so the GPU interpolates the same data as the CPU. This follows the existing "rather than resampling a 3D LUT" rule ingpu.rs. The destructive buffer path gets the same branch.PSD
clrL. Written as version 1 + descriptor:lookupType,Nm,Dthr,LUTFormat,dataOrder,tableOrder,LUT3DFileData(the file bytes) andLUT3DFileName. Field names follow the public MIT-licensed ag-psd implementation and our own files; no Adobe headers or SDK sources were read.CgEd)..lookfiles and BGR orders parse as Unsupported: the layer is a no-op and its bytes are preserved..cubeon save.LUT export. Only visible, unmasked, unclipped adjustments at top level or in pass-through groups are included, with layer and group opacity and blend modes. Masked, clipped and isolated-group adjustments are counted and reported in the status message. For RAW layers, temperature, tint and exposure are sensor-domain, and clarity, dehaze, sharpening, noise, vignette and local masks are spatial, so all of those are left out.
Grading math (
plugins/filters-core/src/color_grading.rs) is our own construction, not Adobe's algorithm:It runs after presence and before detail, with a CPU reference and a GPU stage (
color_grading.wgsl) in the Camera Raw graph.Persistence and merge friendliness. Grading is an additive field on
RawSettings(grading: ColorGrading), saved in a separate privateScCgblock besideScRw. It follows theScRmpattern from Camera Raw local adjustments (masks) #197 and leavesScRw's fixed record alone. Rebased onto main after Camera Raw local adjustments (masks) #197 merged.Recorded actions. Color Lookup layer steps record their full params, including the table. Camera Raw steps recorded before grading existed still validate and replay, with neutral wheels.
Verification done
cargo fmt --all --check.cargo clippy --all-targets -D warningson core, adjustments, compositor, compositor-gpu, codec-psd, filters-core, app-actions and editor.cargo checkof mcp, codec-affinity and app..cubeparsing: identity, affine-exact, grey-axis preservation, a known 33³ function, domains, 1D and shaper+3D;.3dl: uniform 12-bit with header, Lustre uneven 10-bit mesh, malformed;compositor/src/lut_bake.rs): a curves + hue/sat + colour balance + 25% invert stack is baked at 33³ and re-applied as a Color Lookup. The worst error is under 0.02. Spatial layers are skipped and counted; pass-through group opacity and the pre-pass hook are covered.codec-psd/tests/color_lookup.rs):clrL;ScCggrading round-trips without duplication.adjustment_coverageadds four Color Lookup cases (3D alone and shaper+3D, each in both input modes);remaining_opportunitiesCamera Raw variants now set every grading control. Both pass, along with Camera Raw local adjustments (masks) #197's local-mask parity test.filters-coregrading unit tests and theall_filterssuite (before rebase).check-i18nscript suite: strict audit for all locales,sync-i18n --check,test-check-i18n.py, the web node test andschist-i18ncrate tests.no/share generated by sync).Not verified
camera_raw_steps_from_before_grading_still_resolvetest compiles under clippy--all-targets, but the editor test binary was not run (disk).clrL. The interop is structural only, against our own reader..cube/.3dlfiles from other tools were not tested, only synthetic ones.Known limits
🤖 Generated with Claude Code