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// This macro return words stored as big endian
32#define CHANGE_ENDIAN(w) (cmsUInt16Number) ((cmsUInt16Number) ((w)<<8)|((w)>>8))
33
34
35// Floor to byte, taking care of saturation
36cmsINLINE cmsUInt8Number _cmsQuickSaturateByte(cmsFloat64Number d)
37{
38 d += 0.5;
39 if (d <= 0) return 0;
40 if (d >= 255.0) return 255;
41
42 return (cmsUInt8Number) _cmsQuickFloorWord(d);
43}
44
45
46// Return the size in bytes of a given formatter
47static
48cmsUInt32Number trueBytesSize(cmsUInt32Number Format)
49{
50 cmsUInt32Number fmt_bytes = T_BYTES(Format);
51
52 // For double, the T_BYTES field returns zero
53 if (fmt_bytes == 0)
54 return sizeof(double);
55
56 // Otherwise, it is already correct for all formats
57 return fmt_bytes;
58}
59
60
61// Several format converters
62
63typedef void(*cmsFormatterAlphaFn)(void* dst, const void* src);
64
65
66// From 8
67
68static
69void copy8(void* dst, const void* src)
70{
71 memmove(dst, src, 1);
72}
73
74static
75void from8to16(void* dst, const void* src)
76{
77 cmsUInt8Number n = *(cmsUInt8Number*)src;
78 *(cmsUInt16Number*) dst = FROM_8_TO_16(n);
79}
80
81static
82void from8to16SE(void* dst, const void* src)
83{
84 cmsUInt8Number n = *(cmsUInt8Number*)src;
85 *(cmsUInt16Number*)dst = CHANGE_ENDIAN(FROM_8_TO_16(n));
86}
87
88static
89void from8toFLT(void* dst, const void* src)
90{
91 *(cmsFloat32Number*)dst = (*(cmsUInt8Number*)src) / 255.0f;
92}
93
94static
95void from8toDBL(void* dst, const void* src)
96{
97 *(cmsFloat64Number*)dst = (*(cmsUInt8Number*)src) / 255.0;
98}
99
100static
101void from8toHLF(void* dst, const void* src)
102{
103#ifndef CMS_NO_HALF_SUPPORT
104 cmsFloat32Number n = (*(cmsUInt8Number*)src) / 255.0f;
105 *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
106#else
107 cmsUNUSED_PARAMETER(dst);
108 cmsUNUSED_PARAMETER(src);
109#endif
110}
111
112// From 16
113
114static
115void from16to8(void* dst, const void* src)
116{
117 cmsUInt16Number n = *(cmsUInt16Number*)src;
118 *(cmsUInt8Number*) dst = FROM_16_TO_8(n);
119}
120
121static
122void from16SEto8(void* dst, const void* src)
123{
124 cmsUInt16Number n = *(cmsUInt16Number*)src;
125 *(cmsUInt8Number*)dst = FROM_16_TO_8(CHANGE_ENDIAN(n));
126}
127
128static
129void copy16(void* dst, const void* src)
130{
131 memmove(dst, src, 2);
132}
133
134static
135void from16to16(void* dst, const void* src)
136{
137 cmsUInt16Number n = *(cmsUInt16Number*)src;
138 *(cmsUInt16Number*)dst = CHANGE_ENDIAN(n);
139}
140
141static
142void from16toFLT(void* dst, const void* src)
143{
144 *(cmsFloat32Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f;
145}
146
147static
148void from16SEtoFLT(void* dst, const void* src)
149{
150 *(cmsFloat32Number*)dst = (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0f;
151}
152
153static
154void from16toDBL(void* dst, const void* src)
155{
156 *(cmsFloat64Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f;
157}
158
159static
160void from16SEtoDBL(void* dst, const void* src)
161{
162 *(cmsFloat64Number*)dst = (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0f;
163}
164
165static
166void from16toHLF(void* dst, const void* src)
167{
168#ifndef CMS_NO_HALF_SUPPORT
169 cmsFloat32Number n = (*(cmsUInt16Number*)src) / 65535.0f;
170 *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
171#else
172 cmsUNUSED_PARAMETER(dst);
173 cmsUNUSED_PARAMETER(src);
174#endif
175}
176
177static
178void from16SEtoHLF(void* dst, const void* src)
179{
180#ifndef CMS_NO_HALF_SUPPORT
181 cmsFloat32Number n = (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0f;
182 *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
183#else
184 cmsUNUSED_PARAMETER(dst);
185 cmsUNUSED_PARAMETER(src);
186#endif
187}
188// From Float
189
190static
191void fromFLTto8(void* dst, const void* src)
192{
193 cmsFloat32Number n = *(cmsFloat32Number*)src;
194 *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f);
195}
196
197static
198void fromFLTto16(void* dst, const void* src)
199{
200 cmsFloat32Number n = *(cmsFloat32Number*)src;
201 *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
202}
203
204static
205void fromFLTto16SE(void* dst, const void* src)
206{
207 cmsFloat32Number n = *(cmsFloat32Number*)src;
208 cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f);
209
210 *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i);
211}
212
213static
214void copy32(void* dst, const void* src)
215{
216 memmove(dst, src, sizeof(cmsFloat32Number));
217}
218
219static
220void fromFLTtoDBL(void* dst, const void* src)
221{
222 cmsFloat32Number n = *(cmsFloat32Number*)src;
223 *(cmsFloat64Number*)dst = (cmsFloat64Number)n;
224}
225
226static
227void fromFLTtoHLF(void* dst, const void* src)
228{
229#ifndef CMS_NO_HALF_SUPPORT
230 cmsFloat32Number n = *(cmsFloat32Number*)src;
231 *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
232#else
233 cmsUNUSED_PARAMETER(dst);
234 cmsUNUSED_PARAMETER(src);
235#endif
236}
237
238
239// From HALF
240
241static
242void fromHLFto8(void* dst, const void* src)
243{
244#ifndef CMS_NO_HALF_SUPPORT
245 cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
246 *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f);
247#else
248 cmsUNUSED_PARAMETER(dst);
249 cmsUNUSED_PARAMETER(src);
250#endif
251
252}
253
254static
255void fromHLFto16(void* dst, const void* src)
256{
257#ifndef CMS_NO_HALF_SUPPORT
258 cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
259 *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
260#else
261 cmsUNUSED_PARAMETER(dst);
262 cmsUNUSED_PARAMETER(src);
263#endif
264}
265
266static
267void fromHLFto16SE(void* dst, const void* src)
268{
269#ifndef CMS_NO_HALF_SUPPORT
270 cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
271 cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f);
272 *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i);
273#else
274 cmsUNUSED_PARAMETER(dst);
275 cmsUNUSED_PARAMETER(src);
276#endif
277}
278static
279void fromHLFtoFLT(void* dst, const void* src)
280{
281#ifndef CMS_NO_HALF_SUPPORT
282 *(cmsFloat32Number*)dst = _cmsHalf2Float(*(cmsUInt16Number*)src);
283#else
284 cmsUNUSED_PARAMETER(dst);
285 cmsUNUSED_PARAMETER(src);
286#endif
287}
288
289static
290void fromHLFtoDBL(void* dst, const void* src)
291{
292#ifndef CMS_NO_HALF_SUPPORT
293 *(cmsFloat64Number*)dst = (cmsFloat64Number)_cmsHalf2Float(*(cmsUInt16Number*)src);
294#else
295 cmsUNUSED_PARAMETER(dst);
296 cmsUNUSED_PARAMETER(src);
297#endif
298}
299
300// From double
301static
302void fromDBLto8(void* dst, const void* src)
303{
304 cmsFloat64Number n = *(cmsFloat64Number*)src;
305 *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0);
306}
307
308static
309void fromDBLto16(void* dst, const void* src)
310{
311 cmsFloat64Number n = *(cmsFloat64Number*)src;
312 *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
313}
314
315static
316void fromDBLto16SE(void* dst, const void* src)
317{
318 cmsFloat64Number n = *(cmsFloat64Number*)src;
319 cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f);
320 *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i);
321}
322static
323void fromDBLtoFLT(void* dst, const void* src)
324{
325 cmsFloat64Number n = *(cmsFloat64Number*)src;
326 *(cmsFloat32Number*)dst = (cmsFloat32Number) n;
327}
328
329static
330void fromDBLtoHLF(void* dst, const void* src)
331{
332#ifndef CMS_NO_HALF_SUPPORT
333 cmsFloat32Number n = (cmsFloat32Number) *(cmsFloat64Number*)src;
334 *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
335#else
336 cmsUNUSED_PARAMETER(dst);
337 cmsUNUSED_PARAMETER(src);
338#endif
339}
340
341static
342void copy64(void* dst, const void* src)
343{
344 memmove(dst, src, sizeof(cmsFloat64Number));
345}
346
347
348// Returns the position (x or y) of the formatter in the table of functions
349static
350int FormatterPos(cmsUInt32Number frm)
351{
352 cmsUInt32Number b = T_BYTES(frm);
353
354 if (b == 0 && T_FLOAT(frm))
355 return 5; // DBL
356#ifndef CMS_NO_HALF_SUPPORT
357 if (b == 2 && T_FLOAT(frm))
358 return 3; // HLF
359#endif
360 if (b == 4 && T_FLOAT(frm))
361 return 4; // FLT
362 if (b == 2 && !T_FLOAT(frm))
363 return 1; // 16
364 if (b == 1 && !T_FLOAT(frm))
365 return 0; // 8
366 if (b == 2 && T_ENDIAN16(frm))
367 return 3;
368 return -1; // not recognized
369}
370
371// Obtains a alpha-to-alpha funmction formatter
372static
373cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out)
374{
375static cmsFormatterAlphaFn FormattersAlpha[6][6] = {
376
377 /* from 8 */ { copy8, from8to16, from8to16SE, from8toHLF, from8toFLT, from8toDBL },
378 /* from 16*/ { from16to8, copy16, from16to16, from16toHLF, from16toFLT, from16toDBL },
379 /* from 16SE*/{ from16SEto8, from16to16, copy16, from16SEtoHLF,from16SEtoFLT, from16SEtoDBL },
380 /* from HLF*/ { fromHLFto8, fromHLFto16, fromHLFto16SE, copy16, fromHLFtoFLT, fromHLFtoDBL },
381 /* from FLT*/ { fromFLTto8, fromFLTto16, fromFLTto16SE, fromFLTtoHLF, copy32, fromFLTtoDBL },
382 /* from DBL*/ { fromDBLto8, fromDBLto16, fromDBLto16SE, fromDBLtoHLF, fromDBLtoFLT, copy64 }};
383
384 int in_n = FormatterPos(in);
385 int out_n = FormatterPos(out);
386
387 if (in_n < 0 || out_n < 0 || in_n > 4 || out_n > 4) {
388
389 cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized alpha channel width");
390 return NULL;
391 }
392
393 return FormattersAlpha[in_n][out_n];
394}
395
396
397
398// This function computes the distance from each component to the next one in bytes.
399static
400void ComputeIncrementsForChunky(cmsUInt32Number Format,
401 cmsUInt32Number ComponentStartingOrder[],
402 cmsUInt32Number ComponentPointerIncrements[])
403{
404 cmsUInt32Number channels[cmsMAXEXTRACHANNELS];
405 cmsUInt32Number extra = T_EXTRA(Format);
406 cmsUInt32Number nchannels = T_CHANNELS(Format);
407 cmsUInt32Number total_chans = nchannels + extra;
408 cmsUInt32Number i;
409 cmsUInt32Number channelSize = trueBytesSize(Format);
410 cmsUInt32Number pixelSize = channelSize * total_chans;
411
412 // Sanity check
413 if (total_chans <= 0 || total_chans >= cmsMAXEXTRACHANNELS)
414 return;
415
416 memset(channels, 0, sizeof(channels));
417
418 // Separation is independent of starting point and only depends on channel size
419 for (i = 0; i < extra; i++)
420 ComponentPointerIncrements[i] = pixelSize;
421
422 // Handle do swap
423 for (i = 0; i < total_chans; i++)
424 {
425 if (T_DOSWAP(Format)) {
426 channels[i] = total_chans - i - 1;
427 }
428 else {
429 channels[i] = i;
430 }
431 }
432
433 // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012
434 if (T_SWAPFIRST(Format) && total_chans > 1) {
435
436 cmsUInt32Number tmp = channels[0];
437 for (i = 0; i < total_chans-1; i++)
438 channels[i] = channels[i + 1];
439
440 channels[total_chans - 1] = tmp;
441 }
442
443 // Handle size
444 if (channelSize > 1)
445 for (i = 0; i < total_chans; i++) {
446 channels[i] *= channelSize;
447 }
448
449 for (i = 0; i < extra; i++)
450 ComponentStartingOrder[i] = channels[i + nchannels];
451}
452
453
454
455// On planar configurations, the distance is the stride added to any non-negative
456static
457void ComputeIncrementsForPlanar(cmsUInt32Number Format,
458 cmsUInt32Number BytesPerPlane,
459 cmsUInt32Number ComponentStartingOrder[],
460 cmsUInt32Number ComponentPointerIncrements[])
461{
462 cmsUInt32Number channels[cmsMAXEXTRACHANNELS];
463 cmsUInt32Number extra = T_EXTRA(Format);
464 cmsUInt32Number nchannels = T_CHANNELS(Format);
465 cmsUInt32Number total_chans = nchannels + extra;
466 cmsUInt32Number i;
467 cmsUInt32Number channelSize = trueBytesSize(Format);
468
469 // Sanity check
470 if (total_chans <= 0 || total_chans >= cmsMAXEXTRACHANNELS)
471 return;
472
473 memset(channels, 0, sizeof(channels));
474
475 // Separation is independent of starting point and only depends on channel size
476 for (i = 0; i < extra; i++)
477 ComponentPointerIncrements[i] = channelSize;
478
479 // Handle do swap
480 for (i = 0; i < total_chans; i++)
481 {
482 if (T_DOSWAP(Format)) {
483 channels[i] = total_chans - i - 1;
484 }
485 else {
486 channels[i] = i;
487 }
488 }
489
490 // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012
491 if (T_SWAPFIRST(Format) && total_chans > 0) {
492
493 cmsUInt32Number tmp = channels[0];
494 for (i = 0; i < total_chans - 1; i++)
495 channels[i] = channels[i + 1];
496
497 channels[total_chans - 1] = tmp;
498 }
499
500 // Handle size
501 for (i = 0; i < total_chans; i++) {
502 channels[i] *= BytesPerPlane;
503 }
504
505 for (i = 0; i < extra; i++)
506 ComponentStartingOrder[i] = channels[i + nchannels];
507}
508
509
510
511// Dispatcher por chunky and planar RGB
512static
513void ComputeComponentIncrements(cmsUInt32Number Format,
514 cmsUInt32Number BytesPerPlane,
515 cmsUInt32Number ComponentStartingOrder[],
516 cmsUInt32Number ComponentPointerIncrements[])
517{
518 if (T_PLANAR(Format)) {
519
520 ComputeIncrementsForPlanar(Format, BytesPerPlane, ComponentStartingOrder, ComponentPointerIncrements);
521 }
522 else {
523 ComputeIncrementsForChunky(Format, ComponentStartingOrder, ComponentPointerIncrements);
524 }
525
526}
527
528
529
530// Handles extra channels copying alpha if requested by the flags
531void _cmsHandleExtraChannels(cmsContext ContextID, _cmsTRANSFORM* p, const void* in,
532 void* out,
533 cmsUInt32Number PixelsPerLine,
534 cmsUInt32Number LineCount,
535 const cmsStride* Stride)
536{
537 cmsUInt32Number i, j, k;
538 cmsUInt32Number nExtra;
539 cmsUInt32Number SourceStartingOrder[cmsMAXEXTRACHANNELS];
540 cmsUInt32Number SourceIncrements[cmsMAXEXTRACHANNELS];
541 cmsUInt32Number DestStartingOrder[cmsMAXEXTRACHANNELS];
542 cmsUInt32Number DestIncrements[cmsMAXEXTRACHANNELS];
543
544 cmsFormatterAlphaFn copyValueFn;
545
546 // Make sure we need some copy
547 if (!(p->core->dwOriginalFlags & cmsFLAGS_COPY_ALPHA))
548 return;
549
550 // Exit early if in-place color-management is occurring - no need to copy extra channels to themselves.
551 if (p->InputFormat == p->OutputFormat && in == out)
552 return;
553
554 // Make sure we have same number of alpha channels. If not, just return as this should be checked at transform creation time.
555 nExtra = T_EXTRA(p->InputFormat);
556 if (nExtra != T_EXTRA(p->OutputFormat))
557 return;
558
559 // Anything to do?
560 if (nExtra == 0)
561 return;
562
563 // Compute the increments
564 ComputeComponentIncrements(p->InputFormat, Stride->BytesPerPlaneIn, SourceStartingOrder, SourceIncrements);
565 ComputeComponentIncrements(p->OutputFormat, Stride->BytesPerPlaneOut, DestStartingOrder, DestIncrements);
566
567 // Check for conversions 8, 16, half, float, dbl
568 copyValueFn = _cmsGetFormatterAlpha(ContextID, p->InputFormat, p->OutputFormat);
569
570 if (nExtra == 1) { // Optimized routine for copying a single extra channel quickly
571
572 cmsUInt8Number* SourcePtr;
573 cmsUInt8Number* DestPtr;
574
575 cmsUInt32Number SourceStrideIncrement = 0;
576 cmsUInt32Number DestStrideIncrement = 0;
577
578 // The loop itself
579 for (i = 0; i < LineCount; i++) {
580
581 // Prepare pointers for the loop
582 SourcePtr = (cmsUInt8Number*)in + SourceStartingOrder[0] + SourceStrideIncrement;
583 DestPtr = (cmsUInt8Number*)out + DestStartingOrder[0] + DestStrideIncrement;
584
585 for (j = 0; j < PixelsPerLine; j++) {
586
587 copyValueFn(DestPtr, SourcePtr);
588
589 SourcePtr += SourceIncrements[0];
590 DestPtr += DestIncrements[0];
591 }
592
593 SourceStrideIncrement += Stride->BytesPerLineIn;
594 DestStrideIncrement += Stride->BytesPerLineOut;
595 }
596
597 }
598 else { // General case with more than one extra channel
599
600 cmsUInt8Number* SourcePtr[cmsMAXEXTRACHANNELS];
601 cmsUInt8Number* DestPtr[cmsMAXEXTRACHANNELS];
602
603 cmsUInt32Number SourceStrideIncrements[cmsMAXEXTRACHANNELS];
604 cmsUInt32Number DestStrideIncrements[cmsMAXEXTRACHANNELS];
605
606 memset(SourceStrideIncrements, 0, sizeof(SourceStrideIncrements));
607 memset(DestStrideIncrements, 0, sizeof(DestStrideIncrements));
608
609 // The loop itself
610 for (i = 0; i < LineCount; i++) {
611
612 // Prepare pointers for the loop
613 for (j = 0; j < nExtra; j++) {
614
615 SourcePtr[j] = (cmsUInt8Number*)in + SourceStartingOrder[j] + SourceStrideIncrements[j];
616 DestPtr[j] = (cmsUInt8Number*)out + DestStartingOrder[j] + DestStrideIncrements[j];
617 }
618
619 for (j = 0; j < PixelsPerLine; j++) {
620
621 for (k = 0; k < nExtra; k++) {
622
623 copyValueFn(DestPtr[k], SourcePtr[k]);
624
625 SourcePtr[k] += SourceIncrements[k];
626 DestPtr[k] += DestIncrements[k];
627 }
628 }
629
630 for (j = 0; j < nExtra; j++) {
631
632 SourceStrideIncrements[j] += Stride->BytesPerLineIn;
633 DestStrideIncrements[j] += Stride->BytesPerLineOut;
634 }
635 }
636 }
637}
638