QVector <double> LcmsColorProfileContainer::getEstimatedTRC() const { QVector <double> TRCtriplet(3); if (d->hasColorants) { if (cmsIsToneCurveLinear(d->redTRC)) { TRCtriplet[0] = 1.0; } else { TRCtriplet[0] = cmsEstimateGamma(d->redTRC, 0.01); } if (cmsIsToneCurveLinear(d->greenTRC)) { TRCtriplet[1] = 1.0; } else { TRCtriplet[1] = cmsEstimateGamma(d->greenTRC, 0.01); } if (cmsIsToneCurveLinear(d->blueTRC)) { TRCtriplet[2] = 1.0; } else { TRCtriplet[2] = cmsEstimateGamma(d->blueTRC, 0.01); } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag)) { if (cmsIsToneCurveLinear(d->grayTRC)) { TRCtriplet.fill(1.0); } else { TRCtriplet.fill(cmsEstimateGamma(d->grayTRC, 0.01)); } } else { TRCtriplet.fill(1.0); } } return TRCtriplet; }
void LcmsColorProfileContainer::DelinearizeFloatValueFast(QVector <double> & Value) const { const qreal scale = 65535.0; const qreal invScale = 1.0 / scale; if (d->hasColorants) { //we can only reliably delinearise in the 0-1.0 range, outside of that leave the value alone. QVector <quint16> TRCtriplet(3); TRCtriplet[0] = Value[0] * scale; TRCtriplet[1] = Value[1] * scale; TRCtriplet[2] = Value[2] * scale; if (!cmsIsToneCurveLinear(d->redTRC) && Value[0]<1.0) { TRCtriplet[0] = cmsEvalToneCurve16(d->redTRCReverse, TRCtriplet[0]); Value[0] = TRCtriplet[0] * invScale; } if (!cmsIsToneCurveLinear(d->greenTRC) && Value[1]<1.0) { TRCtriplet[1] = cmsEvalToneCurve16(d->greenTRCReverse, TRCtriplet[1]); Value[1] = TRCtriplet[1] * invScale; } if (!cmsIsToneCurveLinear(d->blueTRC) && Value[2]<1.0) { TRCtriplet[2] = cmsEvalToneCurve16(d->blueTRCReverse, TRCtriplet[2]); Value[2] = TRCtriplet[2] * invScale; } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag) && Value[0]<1.0) { quint16 newValue = cmsEvalToneCurve16(d->grayTRCReverse, Value[0] * scale); Value[0] = newValue * invScale; } } }
void LcmsColorProfileContainer::LinearizeFloatValueFast(QVector <double> & Value) const { //we can only reliably delinearise in the 0-1.0 range, outside of that leave the value alone. QVector <quint16> TRCtriplet(3); TRCtriplet[0] = Value[0]*65535; TRCtriplet[1] = Value[1]*65535; TRCtriplet[2] = Value[2]*65535; if (d->hasColorants) { if (!cmsIsToneCurveLinear(d->redTRC) && Value[0]<1.0) { TRCtriplet[0] = cmsEvalToneCurve16(d->redTRC, TRCtriplet[0]); Value[0] = TRCtriplet[0]/65535.0; } if (!cmsIsToneCurveLinear(d->greenTRC) && Value[1]<1.0) { TRCtriplet[1] = cmsEvalToneCurve16(d->greenTRC, TRCtriplet[1]); Value[1] = TRCtriplet[1]/65535.0; } if (!cmsIsToneCurveLinear(d->blueTRC) && Value[2]<1.0) { TRCtriplet[2] = cmsEvalToneCurve16(d->blueTRC, TRCtriplet[2]); Value[2] = TRCtriplet[2]/65535.0; } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag) && Value[0]<1.0) { TRCtriplet[0] = (cmsEvalToneCurve16(d->grayTRC, Value[0]*65535)); Value.fill(TRCtriplet[0]/65535.0); } } }
void LcmsColorProfileContainer::DelinearizeFloatValue(QVector <double> & Value) const { QVector <double> TRCtriplet(3); TRCtriplet[0] = Value[0]; TRCtriplet[1] = Value[1]; TRCtriplet[2] = Value[2]; if (cmsIsTag(d->profile, cmsSigRedTRCTag)) { if (cmsIsToneCurveLinear(d->redTRC)) { TRCtriplet[0] = Value[0]; } else { TRCtriplet[0] = cmsEvalToneCurveFloat(d->redTRCReverse, Value[0]); } if (cmsIsToneCurveLinear(d->greenTRC)) { TRCtriplet[1] = Value[1]; } else { TRCtriplet[1] = cmsEvalToneCurveFloat(d->greenTRCReverse, Value[1]); } if (cmsIsToneCurveLinear(d->blueTRC)) { TRCtriplet[2] = Value[2]; } else { TRCtriplet[2] = cmsEvalToneCurveFloat(d->blueTRCReverse, Value[2]); } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag)) { TRCtriplet.fill(cmsEvalToneCurveFloat(d->grayTRCReverse, Value[0])); } } Value = TRCtriplet; }
/** * gimp_color_profile_is_linear: * @profile: a #GimpColorProfile * * This function determines is the ICC profile represented by a GimpColorProfile * is a linear RGB profile or not, some profiles that are LUTs though linear * will also return FALSE; * * Return value: %TRUE if the profile is a matrix shaping profile with linear * TRCs, %FALSE otherwise. * * Since: 2.10 **/ gboolean gimp_color_profile_is_linear (GimpColorProfile *profile) { cmsHPROFILE prof; cmsToneCurve *curve; g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE); prof = profile->priv->lcms_profile; if (! cmsIsMatrixShaper (prof)) return FALSE; if (cmsIsCLUT (prof, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) return FALSE; if (cmsIsCLUT (prof, INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT)) return FALSE; curve = cmsReadTag(prof, cmsSigRedTRCTag); if (curve == NULL || ! cmsIsToneCurveLinear (curve)) return FALSE; curve = cmsReadTag (prof, cmsSigGreenTRCTag); if (curve == NULL || ! cmsIsToneCurveLinear (curve)) return FALSE; curve = cmsReadTag (prof, cmsSigBlueTRCTag); if (curve == NULL || ! cmsIsToneCurveLinear (curve)) return FALSE; return TRUE; }
void LcmsColorProfileContainer::DelinearizeFloatValue(QVector <double> & Value) const { if (d->hasColorants) { if (!cmsIsToneCurveLinear(d->redTRC)) { Value[0] = cmsEvalToneCurveFloat(d->redTRCReverse, Value[0]); } if (!cmsIsToneCurveLinear(d->greenTRC)) { Value[1] = cmsEvalToneCurveFloat(d->greenTRCReverse, Value[1]); } if (!cmsIsToneCurveLinear(d->blueTRC)) { Value[2] = cmsEvalToneCurveFloat(d->blueTRCReverse, Value[2]); } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag)) { Value[0] = cmsEvalToneCurveFloat(d->grayTRCReverse, Value[0]); } } }
// Smooths a curve sampled at regular intervals. cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda) { cmsFloat32Number w[MAX_NODES_IN_CURVE], y[MAX_NODES_IN_CURVE], z[MAX_NODES_IN_CURVE]; int i, nItems, Zeros, Poles; if (Tab == NULL) return FALSE; if (cmsIsToneCurveLinear(Tab)) return FALSE; // Nothing to do nItems = Tab -> nEntries; if (nItems >= MAX_NODES_IN_CURVE) { cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: too many points."); return FALSE; } memset(w, 0, nItems * sizeof(cmsFloat32Number)); memset(y, 0, nItems * sizeof(cmsFloat32Number)); memset(z, 0, nItems * sizeof(cmsFloat32Number)); for (i=0; i < nItems; i++) { y[i+1] = (cmsFloat32Number) Tab -> Table16[i]; w[i+1] = 1.0; } if (!smooth2(Tab ->InterpParams->ContextID, w, y, z, (cmsFloat32Number) lambda, nItems)) return FALSE; // Do some reality - checking... Zeros = Poles = 0; for (i=nItems; i > 1; --i) { if (z[i] == 0.) Zeros++; if (z[i] >= 65535.) Poles++; if (z[i] < z[i-1]) return FALSE; // Non-Monotonic } if (Zeros > (nItems / 3)) return FALSE; // Degenerated, mostly zeros if (Poles > (nItems / 3)) return FALSE; // Degenerated, mostly poles // Seems ok for (i=0; i < nItems; i++) { // Clamp to cmsUInt16Number Tab -> Table16[i] = _cmsQuickSaturateWord(z[i+1]); } return TRUE; }
static void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table) { cmsUInt32Number i; cmsFloat64Number gamma; if (Table ->nEntries <= 0) return; // Empty table // Suppress whole if identity if (cmsIsToneCurveLinear(Table)) return; // Check if is really an exponential. If so, emit "exp" gamma = cmsEstimateGamma(Table, 0.001); if (gamma > 0) { _cmsIOPrintf(m, "{ %g exp } bind ", gamma); return; } _cmsIOPrintf(m, "{ "); // Bounds check EmitRangeCheck(m); // Emit intepolation code // PostScript code Stack // =============== ======================== // v _cmsIOPrintf(m, " ["); for (i=0; i < Table->nEntries; i++) { _cmsIOPrintf(m, "%d ", Table->Table16[i]); } _cmsIOPrintf(m, "] "); // v tab _cmsIOPrintf(m, "dup "); // v tab tab _cmsIOPrintf(m, "length 1 sub "); // v tab dom _cmsIOPrintf(m, "3 -1 roll "); // tab dom v _cmsIOPrintf(m, "mul "); // tab val2 _cmsIOPrintf(m, "dup "); // tab val2 val2 _cmsIOPrintf(m, "dup "); // tab val2 val2 val2 _cmsIOPrintf(m, "floor cvi "); // tab val2 val2 cell0 _cmsIOPrintf(m, "exch "); // tab val2 cell0 val2 _cmsIOPrintf(m, "ceiling cvi "); // tab val2 cell0 cell1 _cmsIOPrintf(m, "3 index "); // tab val2 cell0 cell1 tab _cmsIOPrintf(m, "exch "); // tab val2 cell0 tab cell1 _cmsIOPrintf(m, "get "); // tab val2 cell0 y1 _cmsIOPrintf(m, "4 -1 roll "); // val2 cell0 y1 tab _cmsIOPrintf(m, "3 -1 roll "); // val2 y1 tab cell0 _cmsIOPrintf(m, "get "); // val2 y1 y0 _cmsIOPrintf(m, "dup "); // val2 y1 y0 y0 _cmsIOPrintf(m, "3 1 roll "); // val2 y0 y1 y0 _cmsIOPrintf(m, "sub "); // val2 y0 (y1-y0) _cmsIOPrintf(m, "3 -1 roll "); // y0 (y1-y0) val2 _cmsIOPrintf(m, "dup "); // y0 (y1-y0) val2 val2 _cmsIOPrintf(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2) _cmsIOPrintf(m, "sub "); // y0 (y1-y0) rest _cmsIOPrintf(m, "mul "); // y0 t1 _cmsIOPrintf(m, "add "); // y _cmsIOPrintf(m, "65535 div "); // result _cmsIOPrintf(m, " } bind "); }
static int dt_colorspaces_get_matrix_from_profile (cmsHPROFILE prof, float *matrix, float *lutr, float *lutg, float* lutb, const int lutsize, const int input) { // create an OpenCL processable matrix + tone curves from an cmsHPROFILE: // check this first: if(!cmsIsMatrixShaper(prof)) return 1; cmsToneCurve* red_curve = cmsReadTag(prof, cmsSigRedTRCTag); cmsToneCurve* green_curve = cmsReadTag(prof, cmsSigGreenTRCTag); cmsToneCurve* blue_curve = cmsReadTag(prof, cmsSigBlueTRCTag); cmsCIEXYZ *red_color = cmsReadTag(prof, cmsSigRedColorantTag); cmsCIEXYZ *green_color = cmsReadTag(prof, cmsSigGreenColorantTag); cmsCIEXYZ *blue_color = cmsReadTag(prof, cmsSigBlueColorantTag); if(!red_curve || !green_curve || !blue_curve || !red_color || !green_color || !blue_color) return 2; matrix[0] = red_color->X; matrix[1] = green_color->X; matrix[2] = blue_color->X; matrix[3] = red_color->Y; matrix[4] = green_color->Y; matrix[5] = blue_color->Y; matrix[6] = red_color->Z; matrix[7] = green_color->Z; matrix[8] = blue_color->Z; // some camera ICC profiles claim to have color locations for red, green and blue base colors defined, // but in fact these are all set to zero. we catch this case here. float sum = 0.0f; for(int k=0; k<9; k++) sum += matrix[k]; if(sum == 0.0f) return 3; if(input) { // mark as linear, if they are: if(cmsIsToneCurveLinear(red_curve)) lutr[0] = -1.0f; else for(int k=0; k<lutsize; k++) lutr[k] = cmsEvalToneCurveFloat(red_curve, k/(lutsize-1.0f)); if(cmsIsToneCurveLinear(green_curve)) lutg[0] = -1.0f; else for(int k=0; k<lutsize; k++) lutg[k] = cmsEvalToneCurveFloat(green_curve, k/(lutsize-1.0f)); if(cmsIsToneCurveLinear(blue_curve)) lutb[0] = -1.0f; else for(int k=0; k<lutsize; k++) lutb[k] = cmsEvalToneCurveFloat(blue_curve, k/(lutsize-1.0f)); } else { // invert profile->XYZ matrix for output profiles float tmp[9]; memcpy(tmp, matrix, sizeof(float)*9); if(mat3inv (matrix, tmp)) return 3; // also need to reverse gamma, to apply reverse before matrix multiplication: cmsToneCurve* rev_red = cmsReverseToneCurveEx(0x8000, red_curve); cmsToneCurve* rev_green = cmsReverseToneCurveEx(0x8000, green_curve); cmsToneCurve* rev_blue = cmsReverseToneCurveEx(0x8000, blue_curve); if(!rev_red || !rev_green || !rev_blue) { cmsFreeToneCurve(rev_red); cmsFreeToneCurve(rev_green); cmsFreeToneCurve(rev_blue); return 4; } // pass on tonecurves, in case lutsize > 0: if(cmsIsToneCurveLinear(red_curve)) lutr[0] = -1.0f; else for(int k=0; k<lutsize; k++) lutr[k] = cmsEvalToneCurveFloat(rev_red, k/(lutsize-1.0f)); if(cmsIsToneCurveLinear(green_curve)) lutg[0] = -1.0f; else for(int k=0; k<lutsize; k++) lutg[k] = cmsEvalToneCurveFloat(rev_green, k/(lutsize-1.0f)); if(cmsIsToneCurveLinear(blue_curve)) lutb[0] = -1.0f; else for(int k=0; k<lutsize; k++) lutb[k] = cmsEvalToneCurveFloat(rev_blue, k/(lutsize-1.0f)); cmsFreeToneCurve(rev_red); cmsFreeToneCurve(rev_green); cmsFreeToneCurve(rev_blue); } return 0; }