1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2017 Marti Maria Saguer
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 //---------------------------------------------------------------------------------
25 //
26
27 #include "lcms2_internal.h"
28
29 // Alpha copy ------------------------------------------------------------------------------------------------------------------
30
31 // Floor to byte, taking care of saturation
_cmsQuickSaturateByte(cmsFloat64Number d)32 cmsINLINE cmsUInt8Number _cmsQuickSaturateByte(cmsFloat64Number d)
33 {
34 d += 0.5;
35 if (d <= 0) return 0;
36 if (d >= 255.0) return 255;
37
38 return (cmsUInt8Number) _cmsQuickFloorWord(d);
39 }
40
41
42 // Return the size in bytes of a given formatter
43 static
trueBytesSize(cmsUInt32Number Format)44 cmsUInt32Number trueBytesSize(cmsUInt32Number Format)
45 {
46 cmsUInt32Number fmt_bytes = T_BYTES(Format);
47
48 // For double, the T_BYTES field returns zero
49 if (fmt_bytes == 0)
50 return sizeof(double);
51
52 // Otherwise, it is already correct for all formats
53 return fmt_bytes;
54 }
55
56
57 // Several format converters
58
59 typedef void(*cmsFormatterAlphaFn)(void* dst, const void* src);
60
61
62 // From 8
63
64 static
copy8(void * dst,const void * src)65 void copy8(void* dst, const void* src)
66 {
67 memmove(dst, src, 1);
68 }
69
70 static
from8to16(void * dst,const void * src)71 void from8to16(void* dst, const void* src)
72 {
73 cmsUInt8Number n = *(cmsUInt8Number*)src;
74 *(cmsUInt16Number*) dst = FROM_8_TO_16(n);
75 }
76
77 static
from8toFLT(void * dst,const void * src)78 void from8toFLT(void* dst, const void* src)
79 {
80 *(cmsFloat32Number*)dst = (*(cmsUInt8Number*)src) / 255.0f;
81 }
82
83 static
from8toDBL(void * dst,const void * src)84 void from8toDBL(void* dst, const void* src)
85 {
86 *(cmsFloat64Number*)dst = (*(cmsUInt8Number*)src) / 255.0;
87 }
88
89 static
from8toHLF(void * dst,const void * src)90 void from8toHLF(void* dst, const void* src)
91 {
92 #ifndef CMS_NO_HALF_SUPPORT
93 cmsFloat32Number n = (*(cmsUInt8Number*)src) / 255.0f;
94 *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
95 #else
96 cmsUNUSED_PARAMETER(dst);
97 cmsUNUSED_PARAMETER(src);
98 #endif
99 }
100
101 // From 16
102
103 static
from16to8(void * dst,const void * src)104 void from16to8(void* dst, const void* src)
105 {
106 cmsUInt16Number n = *(cmsUInt16Number*)src;
107 *(cmsUInt8Number*) dst = FROM_16_TO_8(n);
108 }
109
110 static
copy16(void * dst,const void * src)111 void copy16(void* dst, const void* src)
112 {
113 memmove(dst, src, 2);
114 }
115
from16toFLT(void * dst,const void * src)116 void from16toFLT(void* dst, const void* src)
117 {
118 *(cmsFloat32Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f;
119 }
120
from16toDBL(void * dst,const void * src)121 void from16toDBL(void* dst, const void* src)
122 {
123 *(cmsFloat64Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f;
124 }
125
126 static
from16toHLF(void * dst,const void * src)127 void from16toHLF(void* dst, const void* src)
128 {
129 #ifndef CMS_NO_HALF_SUPPORT
130 cmsFloat32Number n = (*(cmsUInt16Number*)src) / 65535.0f;
131 *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
132 #else
133 cmsUNUSED_PARAMETER(dst);
134 cmsUNUSED_PARAMETER(src);
135 #endif
136 }
137
138 // From Float
139
140 static
fromFLTto8(void * dst,const void * src)141 void fromFLTto8(void* dst, const void* src)
142 {
143 cmsFloat32Number n = *(cmsFloat32Number*)src;
144 *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f);
145 }
146
147 static
fromFLTto16(void * dst,const void * src)148 void fromFLTto16(void* dst, const void* src)
149 {
150 cmsFloat32Number n = *(cmsFloat32Number*)src;
151 *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
152 }
153
154 static
copy32(void * dst,const void * src)155 void copy32(void* dst, const void* src)
156 {
157 memmove(dst, src, sizeof(cmsFloat32Number));
158 }
159
160 static
fromFLTtoDBL(void * dst,const void * src)161 void fromFLTtoDBL(void* dst, const void* src)
162 {
163 cmsFloat32Number n = *(cmsFloat32Number*)src;
164 *(cmsFloat64Number*)dst = (cmsFloat64Number)n;
165 }
166
167 static
fromFLTtoHLF(void * dst,const void * src)168 void fromFLTtoHLF(void* dst, const void* src)
169 {
170 #ifndef CMS_NO_HALF_SUPPORT
171 cmsFloat32Number n = *(cmsFloat32Number*)src;
172 *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
173 #else
174 cmsUNUSED_PARAMETER(dst);
175 cmsUNUSED_PARAMETER(src);
176 #endif
177 }
178
179
180 // From HALF
181
182 static
fromHLFto8(void * dst,const void * src)183 void fromHLFto8(void* dst, const void* src)
184 {
185 #ifndef CMS_NO_HALF_SUPPORT
186 cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
187 *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f);
188 #else
189 cmsUNUSED_PARAMETER(dst);
190 cmsUNUSED_PARAMETER(src);
191 #endif
192
193 }
194
195 static
fromHLFto16(void * dst,const void * src)196 void fromHLFto16(void* dst, const void* src)
197 {
198 #ifndef CMS_NO_HALF_SUPPORT
199 cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
200 *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
201 #else
202 cmsUNUSED_PARAMETER(dst);
203 cmsUNUSED_PARAMETER(src);
204 #endif
205 }
206
207 static
fromHLFtoFLT(void * dst,const void * src)208 void fromHLFtoFLT(void* dst, const void* src)
209 {
210 #ifndef CMS_NO_HALF_SUPPORT
211 *(cmsFloat32Number*)dst = _cmsHalf2Float(*(cmsUInt16Number*)src);
212 #else
213 cmsUNUSED_PARAMETER(dst);
214 cmsUNUSED_PARAMETER(src);
215 #endif
216 }
217
218 static
fromHLFtoDBL(void * dst,const void * src)219 void fromHLFtoDBL(void* dst, const void* src)
220 {
221 #ifndef CMS_NO_HALF_SUPPORT
222 *(cmsFloat64Number*)dst = (cmsFloat64Number)_cmsHalf2Float(*(cmsUInt16Number*)src);
223 #else
224 cmsUNUSED_PARAMETER(dst);
225 cmsUNUSED_PARAMETER(src);
226 #endif
227 }
228
229 // From double
230 static
fromDBLto8(void * dst,const void * src)231 void fromDBLto8(void* dst, const void* src)
232 {
233 cmsFloat64Number n = *(cmsFloat64Number*)src;
234 *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0);
235 }
236
237 static
fromDBLto16(void * dst,const void * src)238 void fromDBLto16(void* dst, const void* src)
239 {
240 cmsFloat64Number n = *(cmsFloat64Number*)src;
241 *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
242 }
243
244 static
fromDBLtoFLT(void * dst,const void * src)245 void fromDBLtoFLT(void* dst, const void* src)
246 {
247 cmsFloat64Number n = *(cmsFloat64Number*)src;
248 *(cmsFloat32Number*)dst = (cmsFloat32Number) n;
249 }
250
251 static
fromDBLtoHLF(void * dst,const void * src)252 void fromDBLtoHLF(void* dst, const void* src)
253 {
254 #ifndef CMS_NO_HALF_SUPPORT
255 cmsFloat32Number n = (cmsFloat32Number) *(cmsFloat64Number*)src;
256 *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
257 #else
258 cmsUNUSED_PARAMETER(dst);
259 cmsUNUSED_PARAMETER(src);
260 #endif
261 }
262
263 static
copy64(void * dst,const void * src)264 void copy64(void* dst, const void* src)
265 {
266 memmove(dst, src, sizeof(cmsFloat64Number));
267 }
268
269
270 // Returns the position (x or y) of the formatter in the table of functions
271 static
FormatterPos(cmsUInt32Number frm)272 int FormatterPos(cmsUInt32Number frm)
273 {
274 cmsUInt32Number b = T_BYTES(frm);
275
276 if (b == 0 && T_FLOAT(frm))
277 return 4; // DBL
278 #ifndef CMS_NO_HALF_SUPPORT
279 if (b == 2 && T_FLOAT(frm))
280 return 2; // HLF
281 #endif
282 if (b == 4 && T_FLOAT(frm))
283 return 3; // FLT
284 if (b == 2 && !T_FLOAT(frm))
285 return 1; // 16
286 if (b == 1 && !T_FLOAT(frm))
287 return 0; // 8
288
289 return -1; // not recognized
290 }
291
292 // Obtains a alpha-to-alpha funmction formatter
293 static
_cmsGetFormatterAlpha(cmsContext id,cmsUInt32Number in,cmsUInt32Number out)294 cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out)
295 {
296 static const cmsFormatterAlphaFn FormattersAlpha[5][5] = {
297
298 /* from 8 */ { copy8, from8to16, from8toHLF, from8toFLT, from8toDBL },
299 /* from 16*/ { from16to8, copy16, from16toHLF, from16toFLT, from16toDBL },
300 /* from HLF*/ { fromHLFto8, fromHLFto16, copy16, fromHLFtoFLT, fromHLFtoDBL },
301 /* from FLT*/ { fromFLTto8, fromFLTto16, fromFLTtoHLF, copy32, fromFLTtoDBL },
302 /* from DBL*/ { fromDBLto8, fromDBLto16, fromDBLtoHLF, fromDBLtoFLT, copy64 }};
303
304 int in_n = FormatterPos(in);
305 int out_n = FormatterPos(out);
306
307 if (in_n < 0 || out_n < 0 || in_n > 4 || out_n > 4) {
308
309 cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized alpha channel width");
310 return NULL;
311 }
312
313 return FormattersAlpha[in_n][out_n];
314 }
315
316
317
318 // This function computes the distance from each component to the next one in bytes.
319 static
ComputeIncrementsForChunky(cmsUInt32Number Format,cmsUInt32Number ComponentStartingOrder[],cmsUInt32Number ComponentPointerIncrements[])320 void ComputeIncrementsForChunky(cmsUInt32Number Format,
321 cmsUInt32Number ComponentStartingOrder[],
322 cmsUInt32Number ComponentPointerIncrements[])
323 {
324 cmsUInt32Number channels[cmsMAXCHANNELS];
325 cmsUInt32Number extra = T_EXTRA(Format);
326 cmsUInt32Number nchannels = T_CHANNELS(Format);
327 cmsUInt32Number total_chans = nchannels + extra;
328 cmsUInt32Number i;
329 cmsUInt32Number channelSize = trueBytesSize(Format);
330 cmsUInt32Number pixelSize = channelSize * total_chans;
331
332 // Sanity check
333 if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS)
334 return;
335
336 memset(channels, 0, sizeof(channels));
337
338 // Separation is independent of starting point and only depends on channel size
339 for (i = 0; i < extra; i++)
340 ComponentPointerIncrements[i] = pixelSize;
341
342 // Handle do swap
343 for (i = 0; i < total_chans; i++)
344 {
345 if (T_DOSWAP(Format)) {
346 channels[i] = total_chans - i - 1;
347 }
348 else {
349 channels[i] = i;
350 }
351 }
352
353 // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012
354 if (T_SWAPFIRST(Format) && total_chans > 1) {
355
356 cmsUInt32Number tmp = channels[0];
357 for (i = 0; i < total_chans-1; i++)
358 channels[i] = channels[i + 1];
359
360 channels[total_chans - 1] = tmp;
361 }
362
363 // Handle size
364 if (channelSize > 1)
365 for (i = 0; i < total_chans; i++) {
366 channels[i] *= channelSize;
367 }
368
369 for (i = 0; i < extra; i++)
370 ComponentStartingOrder[i] = channels[i + nchannels];
371 }
372
373
374
375 // On planar configurations, the distance is the stride added to any non-negative
376 static
ComputeIncrementsForPlanar(cmsUInt32Number Format,cmsUInt32Number BytesPerPlane,cmsUInt32Number ComponentStartingOrder[],cmsUInt32Number ComponentPointerIncrements[])377 void ComputeIncrementsForPlanar(cmsUInt32Number Format,
378 cmsUInt32Number BytesPerPlane,
379 cmsUInt32Number ComponentStartingOrder[],
380 cmsUInt32Number ComponentPointerIncrements[])
381 {
382 cmsUInt32Number channels[cmsMAXCHANNELS];
383 cmsUInt32Number extra = T_EXTRA(Format);
384 cmsUInt32Number nchannels = T_CHANNELS(Format);
385 cmsUInt32Number total_chans = nchannels + extra;
386 cmsUInt32Number i;
387 cmsUInt32Number channelSize = trueBytesSize(Format);
388
389 // Sanity check
390 if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS)
391 return;
392
393 memset(channels, 0, sizeof(channels));
394
395 // Separation is independent of starting point and only depends on channel size
396 for (i = 0; i < extra; i++)
397 ComponentPointerIncrements[i] = channelSize;
398
399 // Handle do swap
400 for (i = 0; i < total_chans; i++)
401 {
402 if (T_DOSWAP(Format)) {
403 channels[i] = total_chans - i - 1;
404 }
405 else {
406 channels[i] = i;
407 }
408 }
409
410 // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012
411 if (T_SWAPFIRST(Format) && total_chans > 0) {
412
413 cmsUInt32Number tmp = channels[0];
414 for (i = 0; i < total_chans - 1; i++)
415 channels[i] = channels[i + 1];
416
417 channels[total_chans - 1] = tmp;
418 }
419
420 // Handle size
421 for (i = 0; i < total_chans; i++) {
422 channels[i] *= BytesPerPlane;
423 }
424
425 for (i = 0; i < extra; i++)
426 ComponentStartingOrder[i] = channels[i + nchannels];
427 }
428
429
430
431 // Dispatcher por chunky and planar RGB
432 static
ComputeComponentIncrements(cmsUInt32Number Format,cmsUInt32Number BytesPerPlane,cmsUInt32Number ComponentStartingOrder[],cmsUInt32Number ComponentPointerIncrements[])433 void ComputeComponentIncrements(cmsUInt32Number Format,
434 cmsUInt32Number BytesPerPlane,
435 cmsUInt32Number ComponentStartingOrder[],
436 cmsUInt32Number ComponentPointerIncrements[])
437 {
438 if (T_PLANAR(Format)) {
439
440 ComputeIncrementsForPlanar(Format, BytesPerPlane, ComponentStartingOrder, ComponentPointerIncrements);
441 }
442 else {
443 ComputeIncrementsForChunky(Format, ComponentStartingOrder, ComponentPointerIncrements);
444 }
445
446 }
447
448
449
450 // Handles extra channels copying alpha if requested by the flags
_cmsHandleExtraChannels(_cmsTRANSFORM * p,const void * in,void * out,cmsUInt32Number PixelsPerLine,cmsUInt32Number LineCount,const cmsStride * Stride)451 void _cmsHandleExtraChannels(_cmsTRANSFORM* p, const void* in,
452 void* out,
453 cmsUInt32Number PixelsPerLine,
454 cmsUInt32Number LineCount,
455 const cmsStride* Stride)
456 {
457 cmsUInt32Number i, j, k;
458 cmsUInt32Number nExtra;
459 cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS];
460 cmsUInt32Number SourceIncrements[cmsMAXCHANNELS];
461 cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS];
462 cmsUInt32Number DestIncrements[cmsMAXCHANNELS];
463
464 cmsFormatterAlphaFn copyValueFn;
465
466 // Make sure we need some copy
467 if (!(p->dwOriginalFlags & cmsFLAGS_COPY_ALPHA))
468 return;
469
470 // Exit early if in-place color-management is occurring - no need to copy extra channels to themselves.
471 if (p->InputFormat == p->OutputFormat && in == out)
472 return;
473
474 // Make sure we have same number of alpha channels. If not, just return as this should be checked at transform creation time.
475 nExtra = T_EXTRA(p->InputFormat);
476 if (nExtra != T_EXTRA(p->OutputFormat))
477 return;
478
479 // Anything to do?
480 if (nExtra == 0)
481 return;
482
483 // Compute the increments
484 ComputeComponentIncrements(p->InputFormat, Stride->BytesPerPlaneIn, SourceStartingOrder, SourceIncrements);
485 ComputeComponentIncrements(p->OutputFormat, Stride->BytesPerPlaneOut, DestStartingOrder, DestIncrements);
486
487 // Check for conversions 8, 16, half, float, dbl
488 copyValueFn = _cmsGetFormatterAlpha(p->ContextID, p->InputFormat, p->OutputFormat);
489
490 if (nExtra == 1) { // Optimized routine for copying a single extra channel quickly
491
492 cmsUInt8Number* SourcePtr;
493 cmsUInt8Number* DestPtr;
494
495 cmsUInt32Number SourceStrideIncrement = 0;
496 cmsUInt32Number DestStrideIncrement = 0;
497
498 // The loop itself
499 for (i = 0; i < LineCount; i++) {
500
501 // Prepare pointers for the loop
502 SourcePtr = (cmsUInt8Number*)in + SourceStartingOrder[0] + SourceStrideIncrement;
503 DestPtr = (cmsUInt8Number*)out + DestStartingOrder[0] + DestStrideIncrement;
504
505 for (j = 0; j < PixelsPerLine; j++) {
506
507 copyValueFn(DestPtr, SourcePtr);
508
509 SourcePtr += SourceIncrements[0];
510 DestPtr += DestIncrements[0];
511 }
512
513 SourceStrideIncrement += Stride->BytesPerLineIn;
514 DestStrideIncrement += Stride->BytesPerLineOut;
515 }
516
517 }
518 else { // General case with more than one extra channel
519
520 cmsUInt8Number* SourcePtr[cmsMAXCHANNELS];
521 cmsUInt8Number* DestPtr[cmsMAXCHANNELS];
522
523 cmsUInt32Number SourceStrideIncrements[cmsMAXCHANNELS];
524 cmsUInt32Number DestStrideIncrements[cmsMAXCHANNELS];
525
526 memset(SourceStrideIncrements, 0, sizeof(SourceStrideIncrements));
527 memset(DestStrideIncrements, 0, sizeof(DestStrideIncrements));
528
529 // The loop itself
530 for (i = 0; i < LineCount; i++) {
531
532 // Prepare pointers for the loop
533 for (j = 0; j < nExtra; j++) {
534
535 SourcePtr[j] = (cmsUInt8Number*)in + SourceStartingOrder[j] + SourceStrideIncrements[j];
536 DestPtr[j] = (cmsUInt8Number*)out + DestStartingOrder[j] + DestStrideIncrements[j];
537 }
538
539 for (j = 0; j < PixelsPerLine; j++) {
540
541 for (k = 0; k < nExtra; k++) {
542
543 copyValueFn(DestPtr[k], SourcePtr[k]);
544
545 SourcePtr[k] += SourceIncrements[k];
546 DestPtr[k] += DestIncrements[k];
547 }
548 }
549
550 for (j = 0; j < nExtra; j++) {
551
552 SourceStrideIncrements[j] += Stride->BytesPerLineIn;
553 DestStrideIncrements[j] += Stride->BytesPerLineOut;
554 }
555 }
556 }
557 }
558
559
560