//--------------------------------------------------------------------------------- // // Little Color Management System, fast floating point extensions // Copyright (c) 1998-2023 Marti Maria Saguer, all rights reserved // // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // //--------------------------------------------------------------------------------- #include "fast_float_internal.h" // Curves, optimization is valid for 8 bits only typedef struct { cmsContext ContextID; int nCurves; cmsUInt8Number Curves[cmsMAXCHANNELS][256]; } Curves8Data; // Evaluator for RGB 8-bit curves. This are just 1D tables static void FastEvaluateRGBCurves8(struct _cmstransform_struct *CMMcargo, const void* Input, void* Output, cmsUInt32Number PixelsPerLine, cmsUInt32Number LineCount, const cmsStride* Stride) { cmsUInt32Number i, ii; cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS]; cmsUInt32Number SourceIncrements[cmsMAXCHANNELS]; cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS]; cmsUInt32Number DestIncrements[cmsMAXCHANNELS]; const cmsUInt8Number* rin; const cmsUInt8Number* gin; const cmsUInt8Number* bin; const cmsUInt8Number* ain = NULL; cmsUInt8Number* rout; cmsUInt8Number* gout; cmsUInt8Number* bout; cmsUInt8Number* aout = NULL; cmsUInt32Number nalpha, strideIn, strideOut; Curves8Data* Data = (Curves8Data*)_cmsGetTransformUserData(CMMcargo); _cmsComputeComponentIncrements(cmsGetTransformInputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneIn, NULL, &nalpha, SourceStartingOrder, SourceIncrements); _cmsComputeComponentIncrements(cmsGetTransformOutputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneOut, NULL, &nalpha, DestStartingOrder, DestIncrements); if (!(_cmsGetTransformFlags((cmsHTRANSFORM)CMMcargo) & cmsFLAGS_COPY_ALPHA)) nalpha = 0; strideIn = strideOut = 0; for (i = 0; i < LineCount; i++) { rin = (const cmsUInt8Number*)Input + SourceStartingOrder[0] + strideIn; gin = (const cmsUInt8Number*)Input + SourceStartingOrder[1] + strideIn; bin = (const cmsUInt8Number*)Input + SourceStartingOrder[2] + strideIn; if (nalpha) ain = (const cmsUInt8Number*)Input + SourceStartingOrder[3] + strideIn; rout = (cmsUInt8Number*)Output + DestStartingOrder[0] + strideOut; gout = (cmsUInt8Number*)Output + DestStartingOrder[1] + strideOut; bout = (cmsUInt8Number*)Output + DestStartingOrder[2] + strideOut; if (nalpha) aout = (cmsUInt8Number*)Output + DestStartingOrder[3] + strideOut; for (ii = 0; ii < PixelsPerLine; ii++) { *rout = Data->Curves[0][*rin]; *gout = Data->Curves[1][*gin]; *bout = Data->Curves[2][*bin]; // Handle alpha if (ain) { *aout = *ain; } rin += SourceIncrements[0]; gin += SourceIncrements[1]; bin += SourceIncrements[2]; if (ain) ain += SourceIncrements[3]; rout += DestIncrements[0]; gout += DestIncrements[1]; bout += DestIncrements[2]; if (aout) aout += DestIncrements[3]; } strideIn += Stride->BytesPerLineIn; strideOut += Stride->BytesPerLineOut; } } // Do nothing but arrange the format. RGB static void FastRGBIdentity8(struct _cmstransform_struct *CMMcargo, const void* Input, void* Output, cmsUInt32Number PixelsPerLine, cmsUInt32Number LineCount, const cmsStride* Stride) { cmsUInt32Number i, ii; cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS]; cmsUInt32Number SourceIncrements[cmsMAXCHANNELS]; cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS]; cmsUInt32Number DestIncrements[cmsMAXCHANNELS]; const cmsUInt8Number* rin; const cmsUInt8Number* gin; const cmsUInt8Number* bin; const cmsUInt8Number* ain = NULL; cmsUInt8Number* rout; cmsUInt8Number* gout; cmsUInt8Number* bout; cmsUInt8Number* aout = NULL; cmsUInt32Number nalpha, strideIn, strideOut; _cmsComputeComponentIncrements(cmsGetTransformInputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneIn, NULL, &nalpha, SourceStartingOrder, SourceIncrements); _cmsComputeComponentIncrements(cmsGetTransformOutputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneOut, NULL, &nalpha, DestStartingOrder, DestIncrements); if (!(_cmsGetTransformFlags((cmsHTRANSFORM)CMMcargo) & cmsFLAGS_COPY_ALPHA)) nalpha = 0; strideIn = strideOut = 0; for (i = 0; i < LineCount; i++) { rin = (const cmsUInt8Number*)Input + SourceStartingOrder[0] + strideIn; gin = (const cmsUInt8Number*)Input + SourceStartingOrder[1] + strideIn; bin = (const cmsUInt8Number*)Input + SourceStartingOrder[2] + strideIn; if (nalpha) ain = (const cmsUInt8Number*)Input + SourceStartingOrder[3] + strideIn; rout = (cmsUInt8Number*)Output + DestStartingOrder[0] + strideOut; gout = (cmsUInt8Number*)Output + DestStartingOrder[1] + strideOut; bout = (cmsUInt8Number*)Output + DestStartingOrder[2] + strideOut; if (nalpha) aout = (cmsUInt8Number*)Output + DestStartingOrder[3] + strideOut; for (ii = 0; ii < PixelsPerLine; ii++) { *rout = *rin; *gout = *gin; *bout = *bin; // Handle alpha if (ain) { *aout = *ain; } rin += SourceIncrements[0]; gin += SourceIncrements[1]; bin += SourceIncrements[2]; if (ain) ain += SourceIncrements[3]; rout += DestIncrements[0]; gout += DestIncrements[1]; bout += DestIncrements[2]; if (aout) aout += DestIncrements[3]; } strideIn += Stride->BytesPerLineIn; strideOut += Stride->BytesPerLineOut; } } // Evaluate 1 channel only static void FastEvaluateGrayCurves8(struct _cmstransform_struct *CMMcargo, const void* Input, void* Output, cmsUInt32Number PixelsPerLine, cmsUInt32Number LineCount, const cmsStride* Stride) { cmsUInt32Number i, ii; cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS]; cmsUInt32Number SourceIncrements[cmsMAXCHANNELS]; cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS]; cmsUInt32Number DestIncrements[cmsMAXCHANNELS]; const cmsUInt8Number* gin; const cmsUInt8Number* ain = NULL; cmsUInt8Number* gout; cmsUInt8Number* aout = NULL; cmsUInt32Number nalpha, strideIn, strideOut; Curves8Data* Data = (Curves8Data*)_cmsGetTransformUserData(CMMcargo); _cmsComputeComponentIncrements(cmsGetTransformInputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneIn, NULL, &nalpha, SourceStartingOrder, SourceIncrements); _cmsComputeComponentIncrements(cmsGetTransformOutputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneOut, NULL, &nalpha, DestStartingOrder, DestIncrements); if (!(_cmsGetTransformFlags((cmsHTRANSFORM)CMMcargo) & cmsFLAGS_COPY_ALPHA)) nalpha = 0; strideIn = strideOut = 0; for (i = 0; i < LineCount; i++) { gin = (const cmsUInt8Number*)Input + SourceStartingOrder[0] + strideIn; if (nalpha) ain = (const cmsUInt8Number*)Input + SourceStartingOrder[1] + strideIn; gout = (cmsUInt8Number*)Output + DestStartingOrder[0] + strideOut; if (nalpha) aout = (cmsUInt8Number*)Output + DestStartingOrder[1] + strideOut; for (ii = 0; ii < PixelsPerLine; ii++) { *gout = Data->Curves[0][*gin]; // Handle alpha if (ain) { *aout = *ain; } gin += SourceIncrements[0]; if (ain) ain += SourceIncrements[1]; gout += DestIncrements[0]; if (aout) aout += DestIncrements[1]; } strideIn += Stride->BytesPerLineIn; strideOut += Stride->BytesPerLineOut; } } static void FastGrayIdentity8(struct _cmstransform_struct *CMMcargo, const void* Input, void* Output, cmsUInt32Number PixelsPerLine, cmsUInt32Number LineCount, const cmsStride* Stride) { cmsUInt32Number i, ii; cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS]; cmsUInt32Number SourceIncrements[cmsMAXCHANNELS]; cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS]; cmsUInt32Number DestIncrements[cmsMAXCHANNELS]; const cmsUInt8Number* gin; const cmsUInt8Number* ain = NULL; cmsUInt8Number* gout; cmsUInt8Number* aout = NULL; cmsUInt32Number nalpha, strideIn, strideOut; _cmsComputeComponentIncrements(cmsGetTransformInputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneIn, NULL, &nalpha, SourceStartingOrder, SourceIncrements); _cmsComputeComponentIncrements(cmsGetTransformOutputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneOut, NULL, &nalpha, DestStartingOrder, DestIncrements); if (!(_cmsGetTransformFlags((cmsHTRANSFORM)CMMcargo) & cmsFLAGS_COPY_ALPHA)) nalpha = 0; strideIn = strideOut = 0; for (i = 0; i < LineCount; i++) { gin = (const cmsUInt8Number*)Input + SourceStartingOrder[0] + strideIn; if (nalpha) ain = (const cmsUInt8Number*)Input + SourceStartingOrder[1] + strideIn; gout = (cmsUInt8Number*)Output + DestStartingOrder[0] + strideOut; if (nalpha) aout = (cmsUInt8Number*)Output + DestStartingOrder[1] + strideOut; for (ii = 0; ii < PixelsPerLine; ii++) { *gout = *gin; // Handle alpha if (ain) { *aout = *ain; } gin += SourceIncrements[0]; if (ain) ain += SourceIncrements[1]; gout += DestIncrements[0]; if (aout) aout += DestIncrements[1]; } strideIn += Stride->BytesPerLineIn; strideOut += Stride->BytesPerLineOut; } } // Try to see if the curves are linear static cmsBool AllCurvesAreLinear(Curves8Data* data) { int i, j; for (i=0; i < 3; i++) { for (j = 0; j < 256; j++) { if (data ->Curves[i][j] != j) return FALSE; } } return TRUE; } static Curves8Data* ComputeCompositeCurves(cmsUInt32Number nChan, cmsPipeline* Src) { cmsUInt32Number i, j; cmsFloat32Number InFloat[3], OutFloat[3]; Curves8Data* Data = (Curves8Data*) _cmsMallocZero(cmsGetPipelineContextID(Src), sizeof(Curves8Data)); if (Data == NULL) return NULL; // Create target curves for (i=0; i < 256; i++) { for (j=0; j Curves[j][i] = FROM_16_TO_8(_cmsSaturateWord(OutFloat[j] * 65535.0)); } return Data; } // If the target LUT holds only curves, the optimization procedure is to join all those // curves together. That only works on curves and does not work on matrices. // Any number of channels up to 16 cmsBool Optimize8ByJoiningCurves(_cmsTransform2Fn* TransformFn, void** UserData, _cmsFreeUserDataFn* FreeUserData, cmsPipeline** Lut, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) { cmsPipeline* Src = *Lut; cmsStage* mpe; Curves8Data* Data; cmsUInt32Number nChans; // This is a lossy optimization! does not apply in floating-point cases if (T_FLOAT(*InputFormat) || T_FLOAT(*OutputFormat)) return FALSE; // Only on 8-bit if (T_BYTES(*InputFormat) != 1 || T_BYTES(*OutputFormat) != 1) return FALSE; // Curves need same channels on input and output (despite extra channels may differ) nChans = T_CHANNELS(*InputFormat); if (nChans != T_CHANNELS(*OutputFormat)) return FALSE; // gray and RGB if (nChans != 1 && nChans != 3) return FALSE; // Only curves in this LUT? for (mpe = cmsPipelineGetPtrToFirstStage(Src); mpe != NULL; mpe = cmsStageNext(mpe)) { if (cmsStageType(mpe) != cmsSigCurveSetElemType) return FALSE; } Data = ComputeCompositeCurves(nChans, Src); *dwFlags |= cmsFLAGS_NOCACHE; *dwFlags &= ~cmsFLAGS_CAN_CHANGE_FORMATTER; *UserData = Data; *FreeUserData = _cmsFree; // Maybe the curves are linear at the end if (nChans == 1) *TransformFn = (AllCurvesAreLinear(Data) ? FastGrayIdentity8 : FastEvaluateGrayCurves8); else *TransformFn = (AllCurvesAreLinear(Data) ? FastRGBIdentity8 : FastEvaluateRGBCurves8); return TRUE; }