1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System, fast floating point extensions
4 //  Copyright (c) 1998-2020 Marti Maria Saguer, all rights reserved
5 //
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation, either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 //
20 //---------------------------------------------------------------------------------
21 
22 #include "fast_float_internal.h"
23 
24 // Curves, optimization is valid for 8 bits only
25 typedef struct {
26 
27     cmsContext ContextID;
28     int nCurves;
29     cmsUInt8Number Curves[cmsMAXCHANNELS][256];
30 
31 } Curves8Data;
32 
33 
34 // Evaluator for RGB 8-bit curves. This are just 1D tables
FastEvaluateRGBCurves8(struct _cmstransform_struct * CMMcargo,const void * Input,void * Output,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)35 static void FastEvaluateRGBCurves8(struct _cmstransform_struct *CMMcargo,
36                                    const void* Input,
37                                    void* Output,
38                                    cmsUInt32Number PixelsPerLine,
39                                    cmsUInt32Number LineCount,
40                                    const cmsStride* Stride)
41 {
42        cmsUInt32Number i, ii;
43 
44        cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS];
45        cmsUInt32Number SourceIncrements[cmsMAXCHANNELS];
46        cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS];
47        cmsUInt32Number DestIncrements[cmsMAXCHANNELS];
48 
49        const cmsUInt8Number* rin;
50        const cmsUInt8Number* gin;
51        const cmsUInt8Number* bin;
52        const cmsUInt8Number* ain = NULL;
53 
54        cmsUInt8Number* rout;
55        cmsUInt8Number* gout;
56        cmsUInt8Number* bout;
57        cmsUInt8Number* aout = NULL;
58 
59        cmsUInt32Number nalpha, strideIn, strideOut;
60 
61        Curves8Data* Data = (Curves8Data*)_cmsGetTransformUserData(CMMcargo);
62 
63        _cmsComputeComponentIncrements(cmsGetTransformInputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneIn, NULL, &nalpha, SourceStartingOrder, SourceIncrements);
64        _cmsComputeComponentIncrements(cmsGetTransformOutputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneOut, NULL, &nalpha, DestStartingOrder, DestIncrements);
65 
66        if (!(_cmsGetTransformFlags((cmsHTRANSFORM)CMMcargo) & cmsFLAGS_COPY_ALPHA))
67            nalpha = 0;
68 
69        strideIn = strideOut = 0;
70        for (i = 0; i < LineCount; i++) {
71 
72               rin = (const cmsUInt8Number*)Input + SourceStartingOrder[0] + strideIn;
73               gin = (const cmsUInt8Number*)Input + SourceStartingOrder[1] + strideIn;
74               bin = (const cmsUInt8Number*)Input + SourceStartingOrder[2] + strideIn;
75               if (nalpha)
76                      ain = (const cmsUInt8Number*)Input + SourceStartingOrder[3] + strideIn;
77 
78               rout = (cmsUInt8Number*)Output + DestStartingOrder[0] + strideOut;
79               gout = (cmsUInt8Number*)Output + DestStartingOrder[1] + strideOut;
80               bout = (cmsUInt8Number*)Output + DestStartingOrder[2] + strideOut;
81               if (nalpha)
82                      aout = (cmsUInt8Number*)Output + DestStartingOrder[3] + strideOut;
83 
84               for (ii = 0; ii < PixelsPerLine; ii++) {
85 
86 
87                      *rout = Data->Curves[0][*rin];
88                      *gout = Data->Curves[1][*gin];
89                      *bout = Data->Curves[2][*bin];
90 
91                      // Handle alpha
92                      if (ain) {
93                            *aout = *ain;
94                      }
95 
96                      rin += SourceIncrements[0];
97                      gin += SourceIncrements[1];
98                      bin += SourceIncrements[2];
99                      if (ain) ain += SourceIncrements[3];
100 
101                      rout += DestIncrements[0];
102                      gout += DestIncrements[1];
103                      bout += DestIncrements[2];
104                      if (aout) aout += DestIncrements[3];
105               }
106 
107               strideIn += Stride->BytesPerLineIn;
108               strideOut += Stride->BytesPerLineOut;
109        }
110 }
111 
112 
113 // Do nothing but arrange the format. RGB
FastRGBIdentity8(struct _cmstransform_struct * CMMcargo,const void * Input,void * Output,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)114 static void FastRGBIdentity8(struct _cmstransform_struct *CMMcargo,
115                              const void* Input,
116                              void* Output,
117                              cmsUInt32Number PixelsPerLine,
118                              cmsUInt32Number LineCount,
119                              const cmsStride* Stride)
120 {
121        cmsUInt32Number i, ii;
122 
123        cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS];
124        cmsUInt32Number SourceIncrements[cmsMAXCHANNELS];
125        cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS];
126        cmsUInt32Number DestIncrements[cmsMAXCHANNELS];
127 
128        const cmsUInt8Number* rin;
129        const cmsUInt8Number* gin;
130        const cmsUInt8Number* bin;
131        const cmsUInt8Number* ain = NULL;
132 
133        cmsUInt8Number* rout;
134        cmsUInt8Number* gout;
135        cmsUInt8Number* bout;
136        cmsUInt8Number* aout = NULL;
137 
138        cmsUInt32Number nalpha, strideIn, strideOut;
139 
140        _cmsComputeComponentIncrements(cmsGetTransformInputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneIn, NULL, &nalpha, SourceStartingOrder, SourceIncrements);
141        _cmsComputeComponentIncrements(cmsGetTransformOutputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneOut, NULL, &nalpha, DestStartingOrder, DestIncrements);
142 
143        if (!(_cmsGetTransformFlags((cmsHTRANSFORM)CMMcargo) & cmsFLAGS_COPY_ALPHA))
144            nalpha = 0;
145 
146        strideIn = strideOut = 0;
147        for (i = 0; i < LineCount; i++) {
148 
149               rin = (const cmsUInt8Number*)Input + SourceStartingOrder[0] + strideIn;
150               gin = (const cmsUInt8Number*)Input + SourceStartingOrder[1] + strideIn;
151               bin = (const cmsUInt8Number*)Input + SourceStartingOrder[2] + strideIn;
152               if (nalpha)
153                      ain = (const cmsUInt8Number*)Input + SourceStartingOrder[3] + strideIn;
154 
155               rout = (cmsUInt8Number*)Output + DestStartingOrder[0] + strideOut;
156               gout = (cmsUInt8Number*)Output + DestStartingOrder[1] + strideOut;
157               bout = (cmsUInt8Number*)Output + DestStartingOrder[2] + strideOut;
158               if (nalpha)
159                      aout = (cmsUInt8Number*)Output + DestStartingOrder[3] + strideOut;
160 
161               for (ii = 0; ii < PixelsPerLine; ii++) {
162 
163 
164                      *rout = *rin;
165                      *gout = *gin;
166                      *bout = *bin;
167 
168                      // Handle alpha
169                      if (ain) {
170                             *aout = *ain;
171                      }
172 
173                      rin += SourceIncrements[0];
174                      gin += SourceIncrements[1];
175                      bin += SourceIncrements[2];
176                      if (ain) ain += SourceIncrements[3];
177 
178                      rout += DestIncrements[0];
179                      gout += DestIncrements[1];
180                      bout += DestIncrements[2];
181                      if (aout) aout += DestIncrements[3];
182               }
183 
184               strideIn += Stride->BytesPerLineIn;
185               strideOut += Stride->BytesPerLineOut;
186        }
187 }
188 
189 
190 
191 // Evaluate 1 channel only
FastEvaluateGrayCurves8(struct _cmstransform_struct * CMMcargo,const void * Input,void * Output,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)192 static void FastEvaluateGrayCurves8(struct _cmstransform_struct *CMMcargo,
193                                     const void* Input,
194                                     void* Output,
195                                     cmsUInt32Number PixelsPerLine,
196                                     cmsUInt32Number LineCount,
197                                     const cmsStride* Stride)
198 {
199        cmsUInt32Number i, ii;
200 
201        cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS];
202        cmsUInt32Number SourceIncrements[cmsMAXCHANNELS];
203        cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS];
204        cmsUInt32Number DestIncrements[cmsMAXCHANNELS];
205 
206        const cmsUInt8Number* gin;
207        const cmsUInt8Number* ain = NULL;
208 
209        cmsUInt8Number* gout;
210        cmsUInt8Number* aout = NULL;
211 
212        cmsUInt32Number nalpha, strideIn, strideOut;
213 
214        Curves8Data* Data = (Curves8Data*)_cmsGetTransformUserData(CMMcargo);
215 
216        _cmsComputeComponentIncrements(cmsGetTransformInputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneIn, NULL, &nalpha, SourceStartingOrder, SourceIncrements);
217        _cmsComputeComponentIncrements(cmsGetTransformOutputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneOut, NULL, &nalpha, DestStartingOrder, DestIncrements);
218 
219        if (!(_cmsGetTransformFlags((cmsHTRANSFORM)CMMcargo) & cmsFLAGS_COPY_ALPHA))
220            nalpha = 0;
221 
222        strideIn = strideOut = 0;
223        for (i = 0; i < LineCount; i++) {
224 
225               gin = (const cmsUInt8Number*)Input + SourceStartingOrder[0] + strideIn;
226               if (nalpha)
227                      ain = (const cmsUInt8Number*)Input + SourceStartingOrder[1] + strideIn;
228 
229               gout = (cmsUInt8Number*)Output + DestStartingOrder[0] + strideOut;
230               if (nalpha)
231                      aout = (cmsUInt8Number*)Output + DestStartingOrder[1] + strideOut;
232 
233               for (ii = 0; ii < PixelsPerLine; ii++) {
234 
235                      *gout = Data->Curves[0][*gin];
236 
237                      // Handle alpha
238                      if (ain) {
239                             *aout = *ain;
240                      }
241 
242                      gin += SourceIncrements[0];
243 
244                      if (ain) ain += SourceIncrements[1];
245 
246                      gout += DestIncrements[0];
247 
248                      if (aout) aout += DestIncrements[1];
249               }
250 
251               strideIn += Stride->BytesPerLineIn;
252               strideOut += Stride->BytesPerLineOut;
253        }
254 }
255 
256 
FastGrayIdentity8(struct _cmstransform_struct * CMMcargo,const void * Input,void * Output,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)257 static void FastGrayIdentity8(struct _cmstransform_struct *CMMcargo,
258                              const void* Input,
259                              void* Output,
260                              cmsUInt32Number PixelsPerLine,
261                              cmsUInt32Number LineCount,
262                              const cmsStride* Stride)
263 {
264        cmsUInt32Number i, ii;
265 
266        cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS];
267        cmsUInt32Number SourceIncrements[cmsMAXCHANNELS];
268        cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS];
269        cmsUInt32Number DestIncrements[cmsMAXCHANNELS];
270 
271        const cmsUInt8Number* gin;
272        const cmsUInt8Number* ain = NULL;
273 
274        cmsUInt8Number* gout;
275        cmsUInt8Number* aout = NULL;
276 
277        cmsUInt32Number nalpha, strideIn, strideOut;
278 
279        _cmsComputeComponentIncrements(cmsGetTransformInputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneIn, NULL, &nalpha, SourceStartingOrder, SourceIncrements);
280        _cmsComputeComponentIncrements(cmsGetTransformOutputFormat((cmsHTRANSFORM)CMMcargo), Stride->BytesPerPlaneOut, NULL, &nalpha, DestStartingOrder, DestIncrements);
281 
282        if (!(_cmsGetTransformFlags((cmsHTRANSFORM)CMMcargo) & cmsFLAGS_COPY_ALPHA))
283            nalpha = 0;
284 
285        strideIn = strideOut = 0;
286        for (i = 0; i < LineCount; i++) {
287 
288               gin = (const cmsUInt8Number*)Input + SourceStartingOrder[0] + strideIn;
289               if (nalpha)
290                      ain = (const cmsUInt8Number*)Input + SourceStartingOrder[1] + strideIn;
291 
292               gout = (cmsUInt8Number*)Output + DestStartingOrder[0] + strideOut;
293               if (nalpha)
294                      aout = (cmsUInt8Number*)Output + DestStartingOrder[1] + strideOut;
295 
296               for (ii = 0; ii < PixelsPerLine; ii++) {
297 
298                      *gout = *gin;
299 
300                      // Handle alpha
301                      if (ain) {
302                             *aout = *ain;
303                      }
304 
305                      gin += SourceIncrements[0];
306 
307                      if (ain) ain += SourceIncrements[1];
308 
309                      gout += DestIncrements[0];
310 
311                      if (aout) aout += DestIncrements[1];
312               }
313 
314               strideIn += Stride->BytesPerLineIn;
315               strideOut += Stride->BytesPerLineOut;
316        }
317 }
318 
319 
320 
321 
322 
323 // Try to see if the curves are linear
324 static
AllCurvesAreLinear(Curves8Data * data)325 cmsBool AllCurvesAreLinear(Curves8Data* data)
326 {
327     int i, j;
328 
329     for (i=0; i < 3; i++) {
330         for (j = 0; j < 256; j++) {
331             if (data ->Curves[i][j] != j) return FALSE;
332         }
333     }
334 
335     return TRUE;
336 }
337 
338 
339 static
ComputeCompositeCurves(cmsUInt32Number nChan,cmsPipeline * Src)340 Curves8Data* ComputeCompositeCurves(cmsUInt32Number nChan,  cmsPipeline* Src)
341 {
342     cmsUInt32Number i, j;
343     cmsFloat32Number InFloat[3], OutFloat[3];
344 
345     Curves8Data* Data = (Curves8Data*) _cmsMallocZero(cmsGetPipelineContextID(Src), sizeof(Curves8Data));
346     if (Data == NULL) return NULL;
347 
348     // Create target curves
349     for (i=0; i < 256; i++) {
350 
351         for (j=0; j <nChan; j++)
352             InFloat[j] = (cmsFloat32Number) ((cmsFloat64Number) i / 255.0);
353 
354         cmsPipelineEvalFloat(InFloat, OutFloat, Src);
355 
356         for (j=0; j < nChan; j++)
357             Data -> Curves[j][i] = FROM_16_TO_8(_cmsSaturateWord(OutFloat[j] * 65535.0));
358     }
359 
360     return Data;
361 }
362 
363 
364 // If the target LUT holds only curves, the optimization procedure is to join all those
365 // curves together. That only works on curves and does not work on matrices.
366 // Any number of channels up to 16
Optimize8ByJoiningCurves(_cmsTransform2Fn * TransformFn,void ** UserData,_cmsFreeUserDataFn * FreeUserData,cmsPipeline ** Lut,cmsUInt32Number * InputFormat,cmsUInt32Number * OutputFormat,cmsUInt32Number * dwFlags)367 cmsBool Optimize8ByJoiningCurves(_cmsTransform2Fn* TransformFn,
368                                  void** UserData,
369                                  _cmsFreeUserDataFn* FreeUserData,
370                                  cmsPipeline** Lut,
371                                  cmsUInt32Number* InputFormat,
372                                  cmsUInt32Number* OutputFormat,
373                                  cmsUInt32Number* dwFlags)
374 {
375 
376     cmsPipeline* Src = *Lut;
377     cmsStage* mpe;
378     Curves8Data* Data;
379     cmsUInt32Number nChans;
380 
381     // This is a loosy optimization! does not apply in floating-point cases
382     if (T_FLOAT(*InputFormat) || T_FLOAT(*OutputFormat)) return FALSE;
383 
384     // Only on 8-bit
385     if (T_BYTES(*InputFormat) != 1 ||  T_BYTES(*OutputFormat) != 1) return FALSE;
386 
387     // Curves need same channels on input and output (despite extra channels may differ)
388     nChans = T_CHANNELS(*InputFormat);
389     if (nChans != T_CHANNELS(*OutputFormat)) return FALSE;
390 
391     // gray and RGB
392     if (nChans != 1 && nChans != 3) return FALSE;
393 
394     //  Only curves in this LUT?
395     for (mpe = cmsPipelineGetPtrToFirstStage(Src);
396         mpe != NULL;
397         mpe = cmsStageNext(mpe)) {
398 
399             if (cmsStageType(mpe) != cmsSigCurveSetElemType) return FALSE;
400     }
401 
402     Data = ComputeCompositeCurves(nChans, Src);
403 
404     *dwFlags |= cmsFLAGS_NOCACHE;
405     *dwFlags &= ~cmsFLAGS_CAN_CHANGE_FORMATTER;
406     *UserData = Data;
407     *FreeUserData = _cmsFree;
408 
409     // Maybe the curves are linear at the end
410     if (nChans == 1)
411         *TransformFn = (AllCurvesAreLinear(Data) ? FastGrayIdentity8 : FastEvaluateGrayCurves8);
412     else
413         *TransformFn = (AllCurvesAreLinear(Data) ? FastRGBIdentity8 : FastEvaluateRGBCurves8);
414 
415     return TRUE;
416 
417 }
418 
419