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