InternationalColorConsortium/DemoIccMAX

Lut16 / Lut8 Handling of PCSXYZ Color Space doesn't set up curves/matrix

Opened this issue ยท 1 comments

The Lut16 & Lut8 types are supposed to have special handling when the input color space is PCSXYZ, which is the only time the matrix coefficients get used. From what I can tell, this special handling exists but never actually loads the matrix coefficients in or loads new curves into the B curves section. Is this correct, in that "in-the-wild" ICC profiles don't ever actually use the matrix coefficients correctly and they should thus be ignored, or should they be loaded in? If they should be ignored, perhaps the code that generates a new matrix and curves can be removed?

I took the Lut8 section of code I'm talking about and annotated it below:

void CIccTagLut8::SetColorSpaces(icColorSpaceSignature csInput, icColorSpaceSignature csOutput)
{
  if (csInput==icSigXYZData) {
    int i;

    if (!m_CurvesM && IsInputMatrix()) { //Transfer ownership of curves
      m_CurvesM = m_CurvesB;
      m_CurvesB = NULL;

      // ๐Ÿ‘‡ Why does this code allocate a new set of curves and do nothing with it?
      LPIccCurve *pCurves = NewCurvesB();
      CIccTagCurve *pCurve; 
      for (i=0; i<m_nInput; i++) {
        pCurves[i] = pCurve = (CIccTagCurve*)CIccTag::Create(icSigCurveType);
        pCurve->SetSize(0); 
      } 

      m_bUseMCurvesAsBCurves = true;
    } 

    if (!m_Matrix) {
      // ๐Ÿ‘‡ Why does this code allocate a new matrix and do nothing with it?
      CIccMatrix *pMatrix = NewMatrix();
      for (i=0; i<9; i++) {
        pMatrix->m_e[i] = icFtoD(m_XYZMatrix[i]);
      }

      pMatrix->m_bUseConstants=false;
    } 
  } 
  else {
    m_XYZMatrix[0] = m_XYZMatrix[4] = m_XYZMatrix[8] = icDtoF(1.0);
    m_XYZMatrix[1] = m_XYZMatrix[2] = m_XYZMatrix[3] = 
    m_XYZMatrix[5] = m_XYZMatrix[6] = m_XYZMatrix[7] = 0;
  } 

  CIccMBB::SetColorSpaces(csInput, csOutput);
}

I'm in no way qualified to answer this - I'm not involved in the project and I have little experience with C - but it looks like the XYZ matrix is applied as expected. And it appears unrelated to the above CIccTagLut8::SetColorSpaces code that seems to do nothing.

I hope someone who knows the codebase could give some clarification ๐Ÿ™

Using the 3D LUT as an example: in IccCmm.cpp line 5,586 m_ApplyMatrixPtr is set with matrix data from the tag...

icStatusCMM CIccXform3DLut::Begin()
{
  // ... some other work...

  m_ApplyMatrixPtr = NULL;
  if (m_pTag->m_Matrix) {
    if (m_pTag->m_bInputMatrix) {
      if (m_pTag->m_nInput!=3) {
        return icCmmStatInvalidProfile;
      }
    }
    else {
      if (m_pTag->m_nOutput!=3) {
        return icCmmStatInvalidProfile;
      }
    }

    if (!m_pTag->m_Matrix->IsIdentity()) {
      m_ApplyMatrixPtr = m_pTag->m_Matrix; // ๐Ÿ‘ˆ here
    }
  }

  return icCmmStatOk;
}

... and later in IccCmm.cpp line 5,624 m_ApplyMatrixPtr is used when applying the Xform to a pixel:

void CIccXform3DLut::Apply(CIccApplyXform* pApply, icFloatNumber *DstPixel, const icFloatNumber *SrcPixel) const
{

  // ... some other work...

  if (m_pTag->m_bInputMatrix) {
    if (m_ApplyCurvePtrB) {
      Pixel[0] = m_ApplyCurvePtrB[0]->Apply(Pixel[0]);
      Pixel[1] = m_ApplyCurvePtrB[1]->Apply(Pixel[1]);
      Pixel[2] = m_ApplyCurvePtrB[2]->Apply(Pixel[2]);
    }

    if (m_ApplyMatrixPtr) {
      m_ApplyMatrixPtr->Apply(Pixel); // ๐Ÿ‘ˆ here
    }

    if (m_ApplyCurvePtrM) {
      Pixel[0] = m_ApplyCurvePtrM[0]->Apply(Pixel[0]);
      Pixel[1] = m_ApplyCurvePtrM[1]->Apply(Pixel[1]);
      Pixel[2] = m_ApplyCurvePtrM[2]->Apply(Pixel[2]);
    }

    if (m_pTag->m_CLUT) {
      if (m_nInterp==icInterpLinear)
        m_pTag->m_CLUT->Interp3d(Pixel, Pixel);
      else
        m_pTag->m_CLUT->Interp3dTetra(Pixel, Pixel);
    }

    if (m_ApplyCurvePtrA) {
      for (i=0; i<m_pTag->m_nOutput; i++) {
        Pixel[i] = m_ApplyCurvePtrA[i]->Apply(Pixel[i]);
      }
    }

  }

  // ... some other work...
}

From the limited tests I've managed to get working (where can I find an ICC profile with an XYZ PCS that contains A2B / B2A tags?) I saw evidence that this was multiplying using the XYZ matrix in the LUT tag.

There is also similar code in 4D and ND LUT, but only when !m_pTag->m_bInputMatrix. I'm guessing this is only triggered for LutAToB workflows?