Replies: 1 comment
|
Superseded by #72 — the actionable implementation plan lives there. Closing to keep a single source of truth. |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Goal
Extend the TI2 layout editor (and its New-chart / Add-patches dialogs) so it can create and edit CMY + N-colorant charts, not just RGB — with a targen device-type selector, dynamically-revealed colorant slots (like the manual Create Chart tab's
-d/-D), and the patch generators (#37) made available where they are meaningful.The hard finding from the design discussion: the perceptual generators (skin tones, blues, greens, sunrises, pastels, image-palette, near-neutral) are colour-appearance constructs and have no meaning in an N-channel device space without a colour model. A preconditioning ICC profile supplies exactly that model, breaking the chicken-and-egg. So the feature is gated in three states, and only the smallest tier requires the profile.
The three-state UX model
-d2) / Video RGB (-d3)-dchosen (or a non-RGB.ti2loaded)-g), white/black anchors, literal N-channel corners. Preconditioning field shown but optional. Perceptual generators greyed.xicclubackward → device values; XYZ for the.ti1fromxiccluforward.Rationale, gating scope, and caveats (separation-collapse, refine-only) were settled in discussion. The key UX rule: gate the perceptual-region subset only, never the whole generator panel — a first-ever CMY+N chart has no profile by definition and must still be buildable (via targen).
UI mockups (New-chart / Add-patches dialog —
_NewChartDialog)State 1 — RGB (unchanged; no profile field)
State 2 — CMY+N chosen, no profile yet (perceptual sets greyed; profile field revealed, optional)
State 3 — CMY+N + valid profile (perceptual sets unlocked)
Validation behaviour: when a profile is picked whose colorspace/channel-count does not match the chosen device type, reject inline at the field (don't defer to a targen/xicclu failure). Auto-fill from the current run's
preconditioning.icc/ a loaded chart's recorded profile when present.UI consistency: reuse the inline progressive-reveal pattern (check a colorant slot → next slot appears), mirroring the manual tab's
-Dstacking — not a modal pop-up.Verified current state (RGB-only chokepoints)
Patch generators —
workflow/patch_generators.py(972 lines): every generator returns(R,G,B)on 0..100. Constructs are intrinsically 3-D perceptual:rgb_cube(N³),gamut_edges/gamut_faces(literal RGB-cube wireframe/surface),skin_tones(CIELab→sRGB),blues/greens/sunrises/pastels(HSV),near_neutral_greys(RGB neutral axis rings),image_palette(Lab k-means→sRGB),fill_gaps/deduplicate(3-D RGB Voronoi/grid).deduplicate,fill_gaps,white_black,count_white_black,overlap_count,dedupe_against,only_newall assume 3-tuples.Editor core —
workflow/ti2_relayout.py(1669 lines):ChartSpec.from_ti2(L124) is already channel-generic —dev_fieldsparsed fromCOLOR_REP. Good.ChartSpec.new(L197) hardcodes["RGB_R","RGB_G","RGB_B"],iRGB.write_ti1(L592) raisesNotImplementedErrorfor any non-RGB (L619) and delegates to the RGB-only emitter.load_rgb_program(L489) rejects non-RGB sources (L513).seed_from_targen(L532) parses RGB only (_first_table_rgb, L564).recolor_spacers(L1261) +_imread_rgb(L1356) do.convert("RGB")and writephotometric="rgb"→ would collapse/corrupt device channels on a Separated TIFF.analyze_randomisation/_read_ti2_strips(L786) hardcodeRGB_R/G/B..ti1emitter —workflow/i1profiler_import.py(write_ti1, L298): emits a fixed 7-field tableSAMPLE_ID RGB_R RGB_G RGB_B XYZ_X XYZ_Y XYZ_Z,COLOR_REP "iRGB", RGB cube-corner_DENSITY_EXTREMES(8 rows) +_DEVICE_COMBINATIONS(9 rows), XYZ synthesized from sRGB with 1% flare. RGB-only by construction.Dialog —
ui/dialogs/ti2_relayout_dialog.py(5455 lines):_NewChartDialog(L699) has instrument/paper combos, source-mode radios,_build_generate_panel(L1380),_build_generated_program(L1915),_collect_gen_state/_apply_gen_state(L1224) for recipe persistence, and callsChartSpec.new(self._instr.currentData(), paper_code)at L2283._AddPatchesDialog(_NewChartDialog)(L2346). Main editorTi2RelayoutDialog(L2578).Already multi-channel (reuse, don't rebuild):
-d0..15+ add/remove colorant-D—data/parameters.yaml(device_type L~20-51,-DL53-96), assembled inworkflow/chart_creator.py::_build_targen_args(L952).ui/tiff_preview.py:_D_TYPE_CHANNELS(device→ordered ink codes),_D_FLAG_CODE,_N_CHANNELS_FALLBACK,resolve_ink_channels(L181), the Generic-CMYK ICC transform (_cmyk_to_srgb_transform)._load_frame/_tifffile_load_frameread Separated TIFFs (>4 ch via tifffile) and composite to RGB. Read-only.workflow/icc_info.py::read_iccexposes the colorspace signature (RGB/CMYK/nCLR), usable for channel-match validation.chart_creator._import_external_preconditioning(L637),Run.preconditioning_icc,Project.new_run(preconditioning_from=…).Verified Argyll facts (printtarg + xicclu)
From
target/printtarg.c(3.5.0):COLOR_REP→icx_char2inkmask→nmask(L3485-3488). Device fields are read by colorant-mask-derived names"<bident>_<inkchar>"(e.g.CMYK_C, orGRAY_<c>for W/K), L3513-3527.-rthey must be present.error ("Expect second set of data to have 8 sets…")) with the N device columns + XYZ. It is the spacer palette + min/max-density refs; white-first ordering matters for label rendering.if (icg->ntables >= 3), L3639).-rsetsrand = 0(L3131); withrand=0the strip-reorder optimization (simulated annealing) is skipped (L1506if (… || !rand || npat < 3)). So under-rXYZ values affect spacer-colour choice but not patch order → XYZ need only be plausible, not exact.col_convert→icx_XYZ2Tdens(density) +icx_XYZ2sRGB(display) for spacer contrast (L1055-1083).From
xicclu(-h):-f f|b|ib(forward / backward / inverted-backward),-p x|X|l|L|…PCS format (X= XYZ×100,l= Lab),-i a|r|p|sintent,-k z|h|x|r|…black generation (GCR/UCR control — confirms separation-collapse is real and tunable),-s scaledevice-range scale. Device I/O is 0..1 (scale ×100 for the TI1). Reads queries on stdin, writes results to stdout. (Exact stdin/stdout token format must be smoke-tested — see Tasks.)Recommended architecture
Two generation engines, one
.ti1path:Coverage (states 2 & 3): run targen with the chosen
-d/-D(+-g/-e/-B). targen is the authority — it emits a complete, valid N-channel.ti1with correctCOLOR_REP, plausible XYZ (its own device model), the 8-row density-extremes table, and the device-combinations table. Reuse_build_targen_argslogic.Perceptual (state 3 only): each generator produces its Lab/appearance target (skin_tones already builds in Lab; HSV sets → sRGB → Lab; image_palette already Lab). Feed Lab →
xicclu -fb(backward) through the preconditioning profile → N-channel device values. Get each patch's XYZ viaxicclu -ff(forward) for the.ti1..ti1assembly — never hand-reconstruct the N-channel format. Always obtain a targen-emitted skeleton for the device type (run targen once), then overwrite table 1's data rows with the union of kept coverage patches + perceptual/white/black/grey patches, attaching XYZ fromxicclu -ff(state 3) or a device-model fallback (state 2: the Generic-CMYK ICC for CMYK, naive subtractive for extended inks). Borrow tables 2 & 3 +COLOR_REPfrom the skeleton verbatim. This sidesteps reconstructingicx_char2inkmaskmapping and the 8-row density-extremes by hand. Then the editor relayouts viaprinttarg -r, exactly as today.Spacer recolour in N-channel: the TIFF post-process path (
recolor_spacers) is RGB-only and corrupts Separated TIFFs. For v1, restrict N-channel charts to the native-palette spacers (from the targen skeleton's density-extremes) and disable custom per-spacer recolour; defer Separated-TIFF read/paint/write to a later tier. Reorder + generate are the core asks and don't need it.Work breakdown (by tier)
Tier A — N-channel
.ti1+ editor relayout (no generators yet)workflow/ti2_relayout.py:ChartSpec.new(device_type, instrument, paper)— derivedev_fields+color_repfrom the device type (via a targen skeleton or_D_TYPE_CHANNELS+ an ink-code→COLOR_REP map; prefer reading them back from the skeleton).write_ti1_nchannel(...)(or generalizewrite_ti1): assemble table 1 from device tuples + XYZ, borrow tables 2/3 + header from a targen skeleton. Drop theNotImplementedErrorgate for the N-channel path._read_ti2_strips/analyze_randomisationto N device columns (useChartSpec.dev_fields).seed_from_targen→ return N-tuples (generalize_first_table_rgb).workflow/i1profiler_import.py: either generalizewrite_ti1/_tableto N device columns + parametricCOLOR_REP, or move N-channel emission intoti2_relayout. Keep the RGB path byte-identical (regression-sensitive — see its density-extremes ordering comments).workflow/xicclu_runner.py: forward (device→XYZ) + backward (Lab→device) lookups via QProcess/subprocess; intent +-kknobs; stdin/stdout parsing. Smoke-test format first.Tier B — device-type + colorant + profile UI
ui/dialogs/ti2_relayout_dialog.py::_NewChartDialog:-D). Compute effective colorant set + count.icc_info.read_iccvs_D_TYPE_CHANNELS[device].length. Auto-fill from run / loaded chart.device_type+ profile intoChartSpec.new(replaces the hardcoded call at L2283) and into_collect_gen_state/_apply_gen_state+editor_recipe/meta.jsonpersistence._AddPatchesDialog: same device-type/profile awareness; loading a non-RGB.ti2puts the dialog in state 2/3.Tier C — generators in N-channel
_build_generated_program, L1915).xicclubackward → device.skin_tones/image_paletteexpose Lab directly; add Lab-target variants (or a thin*_lab()companion) for the HSV sets. Reuse existing count functions for the live totals.deduplicate/fill_gaps/white_black/overlap_count(Euclidean in N-D device space) — generalize the 3-tuple assumption. Newwhite_black/corners in N-D.Tier D (later) — Separated-TIFF custom spacer recolour
_imread/recolor that preserves device channels (tifffile, paint device values, write Separated TIFF). Generalizeassert_patches_untouched. Out of scope for v1.Hard problems / decisions to confirm
-r.icx_char2inkmask. Decision: read it back from the targen skeleton rather than reconstruct.xicclu -k): profile-inverted patches sample one separation. Document as placement hints; expose nothing fancy in v1 (default-k r)._load_frame).Testing
write_ti1round-trips throughChartSpec.from_ti2;printtarg -raccepts it (golden CMYK + CMYKOG.ti1); density-extremes = 8 rows, white-first;assert_data_integritygeneralized to N device values.xicclurunner: forward/backward round-trip a known patch within tolerance against a real profile.deduplicate/fill_gapsuniqueness in N-D; counts match.QT_QPA_PLATFORM=offscreen pytest(see CLAUDE.md). GUI is intrusive — prefer headless.Open questions
xiccluintent/black-generation in the UI, or hardcode relative-colorimetric + ramp-K for v1? (Leaning hardcode.)Plan generated from a verified read of the codebase + ArgyllCMS 3.5.0 source. No code changes made.
All reactions