1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2023 Marti Maria Saguer
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:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
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.
24 //---------------------------------------------------------------------------------
27 #include "lcms2_internal.h"
29 // Alpha copy ------------------------------------------------------------------------------------------------------------------
31 // This macro return words stored as big endian
32 #define CHANGE_ENDIAN(w) (cmsUInt16Number) ((cmsUInt16Number) ((w)<<8)|((w)>>8))
35 // Floor to byte, taking care of saturation
36 cmsINLINE cmsUInt8Number
_cmsQuickSaturateByte(cmsFloat64Number d
)
40 if (d
>= 255.0) return 255;
42 return (cmsUInt8Number
) _cmsQuickFloorWord(d
);
46 // Return the size in bytes of a given formatter
48 cmsUInt32Number
trueBytesSize(cmsUInt32Number Format
)
50 cmsUInt32Number fmt_bytes
= T_BYTES(Format
);
52 // For double, the T_BYTES field returns zero
54 return sizeof(double);
56 // Otherwise, it is already correct for all formats
61 // Several format converters
63 typedef void(*cmsFormatterAlphaFn
)(void* dst
, const void* src
);
69 void copy8(void* dst
, const void* src
)
75 void from8to16(void* dst
, const void* src
)
77 cmsUInt8Number n
= *(cmsUInt8Number
*)src
;
78 *(cmsUInt16Number
*) dst
= (cmsUInt16Number
) FROM_8_TO_16(n
);
82 void from8to16SE(void* dst
, const void* src
)
84 cmsUInt8Number n
= *(cmsUInt8Number
*)src
;
85 *(cmsUInt16Number
*)dst
= CHANGE_ENDIAN(FROM_8_TO_16(n
));
89 void from8toFLT(void* dst
, const void* src
)
91 *(cmsFloat32Number
*)dst
= (cmsFloat32Number
) (*(cmsUInt8Number
*)src
) / 255.0f
;
95 void from8toDBL(void* dst
, const void* src
)
97 *(cmsFloat64Number
*)dst
= (cmsFloat64Number
) (*(cmsUInt8Number
*)src
) / 255.0;
101 void from8toHLF(void* dst
, const void* src
)
103 #ifndef CMS_NO_HALF_SUPPORT
104 cmsFloat32Number n
= (*(cmsUInt8Number
*)src
) / 255.0f
;
105 *(cmsUInt16Number
*)dst
= _cmsFloat2Half(n
);
107 cmsUNUSED_PARAMETER(dst
);
108 cmsUNUSED_PARAMETER(src
);
115 void from16to8(void* dst
, const void* src
)
117 cmsUInt16Number n
= *(cmsUInt16Number
*)src
;
118 *(cmsUInt8Number
*) dst
= FROM_16_TO_8(n
);
122 void from16SEto8(void* dst
, const void* src
)
124 cmsUInt16Number n
= *(cmsUInt16Number
*)src
;
125 *(cmsUInt8Number
*)dst
= FROM_16_TO_8(CHANGE_ENDIAN(n
));
129 void copy16(void* dst
, const void* src
)
131 memmove(dst
, src
, 2);
135 void from16to16(void* dst
, const void* src
)
137 cmsUInt16Number n
= *(cmsUInt16Number
*)src
;
138 *(cmsUInt16Number
*)dst
= CHANGE_ENDIAN(n
);
142 void from16toFLT(void* dst
, const void* src
)
144 *(cmsFloat32Number
*)dst
= (*(cmsUInt16Number
*)src
) / 65535.0f
;
148 void from16SEtoFLT(void* dst
, const void* src
)
150 *(cmsFloat32Number
*)dst
= (CHANGE_ENDIAN(*(cmsUInt16Number
*)src
)) / 65535.0f
;
154 void from16toDBL(void* dst
, const void* src
)
156 *(cmsFloat64Number
*)dst
= (cmsFloat64Number
) (*(cmsUInt16Number
*)src
) / 65535.0;
160 void from16SEtoDBL(void* dst
, const void* src
)
162 *(cmsFloat64Number
*)dst
= (cmsFloat64Number
) (CHANGE_ENDIAN(*(cmsUInt16Number
*)src
)) / 65535.0;
166 void from16toHLF(void* dst
, const void* src
)
168 #ifndef CMS_NO_HALF_SUPPORT
169 cmsFloat32Number n
= (*(cmsUInt16Number
*)src
) / 65535.0f
;
170 *(cmsUInt16Number
*)dst
= _cmsFloat2Half(n
);
172 cmsUNUSED_PARAMETER(dst
);
173 cmsUNUSED_PARAMETER(src
);
178 void from16SEtoHLF(void* dst
, const void* src
)
180 #ifndef CMS_NO_HALF_SUPPORT
181 cmsFloat32Number n
= (CHANGE_ENDIAN(*(cmsUInt16Number
*)src
)) / 65535.0f
;
182 *(cmsUInt16Number
*)dst
= _cmsFloat2Half(n
);
184 cmsUNUSED_PARAMETER(dst
);
185 cmsUNUSED_PARAMETER(src
);
191 void fromFLTto8(void* dst
, const void* src
)
193 cmsFloat32Number n
= *(cmsFloat32Number
*)src
;
194 *(cmsUInt8Number
*)dst
= _cmsQuickSaturateByte(n
* 255.0);
198 void fromFLTto16(void* dst
, const void* src
)
200 cmsFloat32Number n
= *(cmsFloat32Number
*)src
;
201 *(cmsUInt16Number
*)dst
= _cmsQuickSaturateWord(n
* 65535.0);
205 void fromFLTto16SE(void* dst
, const void* src
)
207 cmsFloat32Number n
= *(cmsFloat32Number
*)src
;
208 cmsUInt16Number i
= _cmsQuickSaturateWord(n
* 65535.0);
210 *(cmsUInt16Number
*)dst
= CHANGE_ENDIAN(i
);
214 void copy32(void* dst
, const void* src
)
216 memmove(dst
, src
, sizeof(cmsFloat32Number
));
220 void fromFLTtoDBL(void* dst
, const void* src
)
222 cmsFloat32Number n
= *(cmsFloat32Number
*)src
;
223 *(cmsFloat64Number
*)dst
= (cmsFloat64Number
)n
;
227 void fromFLTtoHLF(void* dst
, const void* src
)
229 #ifndef CMS_NO_HALF_SUPPORT
230 cmsFloat32Number n
= *(cmsFloat32Number
*)src
;
231 *(cmsUInt16Number
*)dst
= _cmsFloat2Half(n
);
233 cmsUNUSED_PARAMETER(dst
);
234 cmsUNUSED_PARAMETER(src
);
242 void fromHLFto8(void* dst
, const void* src
)
244 #ifndef CMS_NO_HALF_SUPPORT
245 cmsFloat32Number n
= _cmsHalf2Float(*(cmsUInt16Number
*)src
);
246 *(cmsUInt8Number
*)dst
= _cmsQuickSaturateByte(n
* 255.0);
248 cmsUNUSED_PARAMETER(dst
);
249 cmsUNUSED_PARAMETER(src
);
255 void fromHLFto16(void* dst
, const void* src
)
257 #ifndef CMS_NO_HALF_SUPPORT
258 cmsFloat32Number n
= _cmsHalf2Float(*(cmsUInt16Number
*)src
);
259 *(cmsUInt16Number
*)dst
= _cmsQuickSaturateWord(n
* 65535.0);
261 cmsUNUSED_PARAMETER(dst
);
262 cmsUNUSED_PARAMETER(src
);
267 void fromHLFto16SE(void* dst
, const void* src
)
269 #ifndef CMS_NO_HALF_SUPPORT
270 cmsFloat32Number n
= _cmsHalf2Float(*(cmsUInt16Number
*)src
);
271 cmsUInt16Number i
= _cmsQuickSaturateWord(n
* 65535.0);
272 *(cmsUInt16Number
*)dst
= CHANGE_ENDIAN(i
);
274 cmsUNUSED_PARAMETER(dst
);
275 cmsUNUSED_PARAMETER(src
);
280 void fromHLFtoFLT(void* dst
, const void* src
)
282 #ifndef CMS_NO_HALF_SUPPORT
283 *(cmsFloat32Number
*)dst
= _cmsHalf2Float(*(cmsUInt16Number
*)src
);
285 cmsUNUSED_PARAMETER(dst
);
286 cmsUNUSED_PARAMETER(src
);
291 void fromHLFtoDBL(void* dst
, const void* src
)
293 #ifndef CMS_NO_HALF_SUPPORT
294 *(cmsFloat64Number
*)dst
= (cmsFloat64Number
)_cmsHalf2Float(*(cmsUInt16Number
*)src
);
296 cmsUNUSED_PARAMETER(dst
);
297 cmsUNUSED_PARAMETER(src
);
303 void fromDBLto8(void* dst
, const void* src
)
305 cmsFloat64Number n
= *(cmsFloat64Number
*)src
;
306 *(cmsUInt8Number
*)dst
= _cmsQuickSaturateByte(n
* 255.0);
310 void fromDBLto16(void* dst
, const void* src
)
312 cmsFloat64Number n
= *(cmsFloat64Number
*)src
;
313 *(cmsUInt16Number
*)dst
= _cmsQuickSaturateWord(n
* 65535.0f
);
317 void fromDBLto16SE(void* dst
, const void* src
)
319 cmsFloat64Number n
= *(cmsFloat64Number
*)src
;
320 cmsUInt16Number i
= _cmsQuickSaturateWord(n
* 65535.0f
);
321 *(cmsUInt16Number
*)dst
= CHANGE_ENDIAN(i
);
325 void fromDBLtoFLT(void* dst
, const void* src
)
327 cmsFloat64Number n
= *(cmsFloat64Number
*)src
;
328 *(cmsFloat32Number
*)dst
= (cmsFloat32Number
) n
;
332 void fromDBLtoHLF(void* dst
, const void* src
)
334 #ifndef CMS_NO_HALF_SUPPORT
335 cmsFloat32Number n
= (cmsFloat32Number
) *(cmsFloat64Number
*)src
;
336 *(cmsUInt16Number
*)dst
= _cmsFloat2Half(n
);
338 cmsUNUSED_PARAMETER(dst
);
339 cmsUNUSED_PARAMETER(src
);
344 void copy64(void* dst
, const void* src
)
346 memmove(dst
, src
, sizeof(cmsFloat64Number
));
350 // Returns the position (x or y) of the formatter in the table of functions
352 int FormatterPos(cmsUInt32Number frm
)
354 cmsUInt32Number b
= T_BYTES(frm
);
356 if (b
== 0 && T_FLOAT(frm
))
358 #ifndef CMS_NO_HALF_SUPPORT
359 if (b
== 2 && T_FLOAT(frm
))
362 if (b
== 4 && T_FLOAT(frm
))
364 if (b
== 2 && !T_FLOAT(frm
))
371 if (b
== 1 && !T_FLOAT(frm
))
373 return -1; // not recognized
376 // Obtains an alpha-to-alpha function formatter
378 cmsFormatterAlphaFn
_cmsGetFormatterAlpha(cmsContext id
, cmsUInt32Number in
, cmsUInt32Number out
)
380 static cmsFormatterAlphaFn FormattersAlpha
[6][6] = {
382 /* from 8 */ { copy8
, from8to16
, from8to16SE
, from8toHLF
, from8toFLT
, from8toDBL
},
383 /* from 16*/ { from16to8
, copy16
, from16to16
, from16toHLF
, from16toFLT
, from16toDBL
},
384 /* from 16SE*/{ from16SEto8
, from16to16
, copy16
, from16SEtoHLF
,from16SEtoFLT
, from16SEtoDBL
},
385 /* from HLF*/ { fromHLFto8
, fromHLFto16
, fromHLFto16SE
, copy16
, fromHLFtoFLT
, fromHLFtoDBL
},
386 /* from FLT*/ { fromFLTto8
, fromFLTto16
, fromFLTto16SE
, fromFLTtoHLF
, copy32
, fromFLTtoDBL
},
387 /* from DBL*/ { fromDBLto8
, fromDBLto16
, fromDBLto16SE
, fromDBLtoHLF
, fromDBLtoFLT
, copy64
}};
389 int in_n
= FormatterPos(in
);
390 int out_n
= FormatterPos(out
);
392 if (in_n
< 0 || out_n
< 0 || in_n
> 5 || out_n
> 5) {
394 cmsSignalError(id
, cmsERROR_UNKNOWN_EXTENSION
, "Unrecognized alpha channel width");
398 return FormattersAlpha
[in_n
][out_n
];
403 // This function computes the distance from each component to the next one in bytes.
405 cmsBool
ComputeIncrementsForChunky(cmsUInt32Number Format
,
406 cmsUInt32Number ComponentStartingOrder
[],
407 cmsUInt32Number ComponentPointerIncrements
[])
409 cmsUInt32Number channels
[cmsMAXCHANNELS
];
410 cmsUInt32Number extra
= T_EXTRA(Format
);
411 cmsUInt32Number nchannels
= T_CHANNELS(Format
);
412 cmsUInt32Number total_chans
= nchannels
+ extra
;
414 cmsUInt32Number channelSize
= trueBytesSize(Format
);
415 cmsUInt32Number pixelSize
= channelSize
* total_chans
;
418 if (total_chans
<= 0 || total_chans
>= cmsMAXCHANNELS
)
421 memset(channels
, 0, sizeof(channels
));
423 // Separation is independent of starting point and only depends on channel size
424 for (i
= 0; i
< extra
; i
++)
425 ComponentPointerIncrements
[i
] = pixelSize
;
428 for (i
= 0; i
< total_chans
; i
++)
430 if (T_DOSWAP(Format
)) {
431 channels
[i
] = total_chans
- i
- 1;
438 // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012
439 if (T_SWAPFIRST(Format
) && total_chans
> 1) {
441 cmsUInt32Number tmp
= channels
[0];
442 for (i
= 0; i
< total_chans
-1; i
++)
443 channels
[i
] = channels
[i
+ 1];
445 channels
[total_chans
- 1] = tmp
;
450 for (i
= 0; i
< total_chans
; i
++) {
451 channels
[i
] *= channelSize
;
454 for (i
= 0; i
< extra
; i
++)
455 ComponentStartingOrder
[i
] = channels
[i
+ nchannels
];
462 // On planar configurations, the distance is the stride added to any non-negative
464 cmsBool
ComputeIncrementsForPlanar(cmsUInt32Number Format
,
465 cmsUInt32Number BytesPerPlane
,
466 cmsUInt32Number ComponentStartingOrder
[],
467 cmsUInt32Number ComponentPointerIncrements
[])
469 cmsUInt32Number channels
[cmsMAXCHANNELS
];
470 cmsUInt32Number extra
= T_EXTRA(Format
);
471 cmsUInt32Number nchannels
= T_CHANNELS(Format
);
472 cmsUInt32Number total_chans
= nchannels
+ extra
;
474 cmsUInt32Number channelSize
= trueBytesSize(Format
);
477 if (total_chans
<= 0 || total_chans
>= cmsMAXCHANNELS
)
480 memset(channels
, 0, sizeof(channels
));
482 // Separation is independent of starting point and only depends on channel size
483 for (i
= 0; i
< extra
; i
++)
484 ComponentPointerIncrements
[i
] = channelSize
;
487 for (i
= 0; i
< total_chans
; i
++)
489 if (T_DOSWAP(Format
)) {
490 channels
[i
] = total_chans
- i
- 1;
497 // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012
498 if (T_SWAPFIRST(Format
) && total_chans
> 0) {
500 cmsUInt32Number tmp
= channels
[0];
501 for (i
= 0; i
< total_chans
- 1; i
++)
502 channels
[i
] = channels
[i
+ 1];
504 channels
[total_chans
- 1] = tmp
;
508 for (i
= 0; i
< total_chans
; i
++) {
509 channels
[i
] *= BytesPerPlane
;
512 for (i
= 0; i
< extra
; i
++)
513 ComponentStartingOrder
[i
] = channels
[i
+ nchannels
];
520 // Dispatcher por chunky and planar RGB
522 cmsBool
ComputeComponentIncrements(cmsUInt32Number Format
,
523 cmsUInt32Number BytesPerPlane
,
524 cmsUInt32Number ComponentStartingOrder
[],
525 cmsUInt32Number ComponentPointerIncrements
[])
527 if (T_PLANAR(Format
)) {
529 return ComputeIncrementsForPlanar(Format
, BytesPerPlane
, ComponentStartingOrder
, ComponentPointerIncrements
);
532 return ComputeIncrementsForChunky(Format
, ComponentStartingOrder
, ComponentPointerIncrements
);
537 // Handles extra channels copying alpha if requested by the flags
538 void _cmsHandleExtraChannels(_cmsTRANSFORM
* p
, const void* in
,
540 cmsUInt32Number PixelsPerLine
,
541 cmsUInt32Number LineCount
,
542 const cmsStride
* Stride
)
544 cmsUInt32Number i
, j
, k
;
545 cmsUInt32Number nExtra
;
546 cmsUInt32Number SourceStartingOrder
[cmsMAXCHANNELS
];
547 cmsUInt32Number SourceIncrements
[cmsMAXCHANNELS
];
548 cmsUInt32Number DestStartingOrder
[cmsMAXCHANNELS
];
549 cmsUInt32Number DestIncrements
[cmsMAXCHANNELS
];
551 cmsFormatterAlphaFn copyValueFn
;
553 // Make sure we need some copy
554 if (!(p
->dwOriginalFlags
& cmsFLAGS_COPY_ALPHA
))
557 // Exit early if in-place color-management is occurring - no need to copy extra channels to themselves.
558 if (p
->InputFormat
== p
->OutputFormat
&& in
== out
)
561 // Make sure we have same number of alpha channels. If not, just return as this should be checked at transform creation time.
562 nExtra
= T_EXTRA(p
->InputFormat
);
563 if (nExtra
!= T_EXTRA(p
->OutputFormat
))
570 // Compute the increments
571 if (!ComputeComponentIncrements(p
->InputFormat
, Stride
->BytesPerPlaneIn
, SourceStartingOrder
, SourceIncrements
))
573 if (!ComputeComponentIncrements(p
->OutputFormat
, Stride
->BytesPerPlaneOut
, DestStartingOrder
, DestIncrements
))
576 // Check for conversions 8, 16, half, float, dbl
577 copyValueFn
= _cmsGetFormatterAlpha(p
->ContextID
, p
->InputFormat
, p
->OutputFormat
);
578 if (copyValueFn
== NULL
)
581 if (nExtra
== 1) { // Optimized routine for copying a single extra channel quickly
583 cmsUInt8Number
* SourcePtr
;
584 cmsUInt8Number
* DestPtr
;
586 cmsUInt32Number SourceStrideIncrement
= 0;
587 cmsUInt32Number DestStrideIncrement
= 0;
590 for (i
= 0; i
< LineCount
; i
++) {
592 // Prepare pointers for the loop
593 SourcePtr
= (cmsUInt8Number
*)in
+ SourceStartingOrder
[0] + SourceStrideIncrement
;
594 DestPtr
= (cmsUInt8Number
*)out
+ DestStartingOrder
[0] + DestStrideIncrement
;
596 for (j
= 0; j
< PixelsPerLine
; j
++) {
598 copyValueFn(DestPtr
, SourcePtr
);
600 SourcePtr
+= SourceIncrements
[0];
601 DestPtr
+= DestIncrements
[0];
604 SourceStrideIncrement
+= Stride
->BytesPerLineIn
;
605 DestStrideIncrement
+= Stride
->BytesPerLineOut
;
609 else { // General case with more than one extra channel
611 cmsUInt8Number
* SourcePtr
[cmsMAXCHANNELS
];
612 cmsUInt8Number
* DestPtr
[cmsMAXCHANNELS
];
614 cmsUInt32Number SourceStrideIncrements
[cmsMAXCHANNELS
];
615 cmsUInt32Number DestStrideIncrements
[cmsMAXCHANNELS
];
617 memset(SourceStrideIncrements
, 0, sizeof(SourceStrideIncrements
));
618 memset(DestStrideIncrements
, 0, sizeof(DestStrideIncrements
));
621 for (i
= 0; i
< LineCount
; i
++) {
623 // Prepare pointers for the loop
624 for (j
= 0; j
< nExtra
; j
++) {
626 SourcePtr
[j
] = (cmsUInt8Number
*)in
+ SourceStartingOrder
[j
] + SourceStrideIncrements
[j
];
627 DestPtr
[j
] = (cmsUInt8Number
*)out
+ DestStartingOrder
[j
] + DestStrideIncrements
[j
];
630 for (j
= 0; j
< PixelsPerLine
; j
++) {
632 for (k
= 0; k
< nExtra
; k
++) {
634 copyValueFn(DestPtr
[k
], SourcePtr
[k
]);
636 SourcePtr
[k
] += SourceIncrements
[k
];
637 DestPtr
[k
] += DestIncrements
[k
];
641 for (j
= 0; j
< nExtra
; j
++) {
643 SourceStrideIncrements
[j
] += Stride
->BytesPerLineIn
;
644 DestStrideIncrements
[j
] += Stride
->BytesPerLineOut
;