Skip to content

CMM: MCS port receives the XYZ black-point affine on channels 0-2 #2330

Description

@maxderhak

Describe the Bug

A CIccXform whose destination is an MCS port receives the XYZ media-white /
black-point affine on its first three channels, because AdjustPCS() treats
pixel[0..2] as X, Y, Z regardless of what the port actually carries. Channels
3 and above pass through untouched.

Reached via an IIccAdjustPCSXform hint — in practice CIccApplyBPCHint, used by
iccApplyToLink, IccWrapper and IccConnect.

Why it happens

Three facts combine:

  1. m_bAdjustPCS is set with no port test. The hint path in
    CIccXform::Begin() sets it purely on CalcFactors() succeeding.
    CIccApplyBPC::CalcFactors() constrains the profile — it rejects absolute
    intent and the icSigLinkClass / icSigAbstractClass / icSigNamedColorClass
    device classes — but never the port.
  2. GetDstSpace() returns m_Header.mcs when m_nMCS == icToMCS, with no
    colorimetric test.
  3. No handover happens. CheckPCSConnections()'s two chain-edge blocks gate on
    IsSpaceColorimetricPCS(), which an MCS signature never satisfies, so no
    CIccPcsXform is built and the xform performs the adjustment itself.

CIccXformMpe's override adds only an intent test, and at perceptual intent that
test is satisfied rather than violated, so it does not exclude the case.

Build Instructions

git clone https://github.com/InternationalColorConsortium/DemoIccMAX.git
cd DemoIccMAX
cmake --preset vs2022-x64 -B msvc -S Build/Cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build msvc -- /m /maxcpucount

Reproduce Bug or Crash

Construct an icSigInputClass v5 profile carrying both AToB0 and AToM0, apply it
with icXformLutMCS at icPerceptual with a CIccApplyBPCHint attached, and feed an
identity AToM0.

Input 0.20 0.40 0.60 0.80:

observed  0.213048  0.409379  0.587843  0.800000
expected  0.200000  0.400000  0.600000  0.800000
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^  ← XYZ affine applied to MCS channels

A regression test that pins this exists on branch refactor/pcs-adjust-in-pcsxform
as pcsAdjustHintReachesANonPcsPort() in
.github/ci/regression/pcs-adjust-placement.cpp.

Root cause shared with #2331

m_bAdjustPCS can be set on an xform with no guarantee its port is XYZ or Lab
the two IsSpacePCS(m_Header.pcs) setters test the profile header, not the port,
and the hint path tests nothing at all — while AdjustPCS() unconditionally reads
pixel[0..2] as X, Y, Z. A spectral PCS port and an MCS port are two consequences of
that single assumption.

Status and the open question

Branch refactor/pcs-adjust-in-pcsxform incidentally stops this — it retires the
in-Apply() adjustment path entirely, so nothing applies the affine to an MCS port any
more. That was not the branch's goal; it fell out of the retirement, and it is a
behaviour change relative to master.

The open question is whether stopping is the whole answer:

  • If yes — the same reasoning already applied to spectral PCS ports, where an XYZ
    affine over samples that are not X, Y, Z is simply wrong — then no further work is
    needed and this issue closes with that branch.
  • If no — if an MCS port should receive some defined adjustment of its own — then
    that needs specifying, and CheckPCSConnections() would need to hand MCS ports to
    CIccPcsXform the way it does colorimetric ones.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

BuildBuild system, CMake, compiler, or packagingQuestionQuestion or support requestTestingCTest, regression, or test coverageWindowsWindows, MSVC, clang-cl, or PowerShell scopeciContinuous integration workflow changesneeds-triageMaintainer triage required

Type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions