//--------------------------------------------------------------------------------- // // Little Color Management System // Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software // is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // //--------------------------------------------------------------------------------- // #include "lcms2_internal.h" // Alpha copy ------------------------------------------------------------------------------------------------------------------ // Floor to byte, taking care of saturation cmsINLINE cmsUInt8Number _cmsQuickSaturateByte(cmsFloat64Number d) { d += 0.5; if (d <= 0) return 0; if (d >= 255.0) return 255; return (cmsUInt8Number) _cmsQuickFloorWord(d); } // Return the size in bytes of a given formatter static cmsUInt32Number trueBytesSize(cmsUInt32Number Format) { cmsUInt32Number fmt_bytes = T_BYTES(Format); // For double, the T_BYTES field returns zero if (fmt_bytes == 0) return sizeof(double); // Otherwise, it is already correct for all formats return fmt_bytes; } // Several format converters typedef void(*cmsFormatterAlphaFn)(void* dst, const void* src); // From 8 static void copy8(void* dst, const void* src) { memmove(dst, src, 1); } static void from8to16(void* dst, const void* src) { cmsUInt8Number n = *(cmsUInt8Number*)src; *(cmsUInt16Number*) dst = FROM_8_TO_16(n); } static void from8toFLT(void* dst, const void* src) { *(cmsFloat32Number*)dst = (*(cmsUInt8Number*)src) / 255.0f; } static void from8toDBL(void* dst, const void* src) { *(cmsFloat64Number*)dst = (*(cmsUInt8Number*)src) / 255.0; } static void from8toHLF(void* dst, const void* src) { #ifndef CMS_NO_HALF_SUPPORT cmsFloat32Number n = (*(cmsUInt8Number*)src) / 255.0f; *(cmsUInt16Number*)dst = _cmsFloat2Half(n); #else cmsUNUSED_PARAMETER(dst); cmsUNUSED_PARAMETER(src); #endif } // From 16 static void from16to8(void* dst, const void* src) { cmsUInt16Number n = *(cmsUInt16Number*)src; *(cmsUInt8Number*) dst = FROM_16_TO_8(n); } static void copy16(void* dst, const void* src) { memmove(dst, src, 2); } void from16toFLT(void* dst, const void* src) { *(cmsFloat32Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f; } void from16toDBL(void* dst, const void* src) { *(cmsFloat64Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f; } static void from16toHLF(void* dst, const void* src) { #ifndef CMS_NO_HALF_SUPPORT cmsFloat32Number n = (*(cmsUInt16Number*)src) / 65535.0f; *(cmsUInt16Number*)dst = _cmsFloat2Half(n); #else cmsUNUSED_PARAMETER(dst); cmsUNUSED_PARAMETER(src); #endif } // From Float static void fromFLTto8(void* dst, const void* src) { cmsFloat32Number n = *(cmsFloat32Number*)src; *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f); } static void fromFLTto16(void* dst, const void* src) { cmsFloat32Number n = *(cmsFloat32Number*)src; *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); } static void copy32(void* dst, const void* src) { memmove(dst, src, sizeof(cmsFloat32Number)); } static void fromFLTtoDBL(void* dst, const void* src) { cmsFloat32Number n = *(cmsFloat32Number*)src; *(cmsFloat64Number*)dst = (cmsFloat64Number)n; } static void fromFLTtoHLF(void* dst, const void* src) { #ifndef CMS_NO_HALF_SUPPORT cmsFloat32Number n = *(cmsFloat32Number*)src; *(cmsUInt16Number*)dst = _cmsFloat2Half(n); #else cmsUNUSED_PARAMETER(dst); cmsUNUSED_PARAMETER(src); #endif } // From HALF static void fromHLFto8(void* dst, const void* src) { #ifndef CMS_NO_HALF_SUPPORT cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f); #else cmsUNUSED_PARAMETER(dst); cmsUNUSED_PARAMETER(src); #endif } static void fromHLFto16(void* dst, const void* src) { #ifndef CMS_NO_HALF_SUPPORT cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); #else cmsUNUSED_PARAMETER(dst); cmsUNUSED_PARAMETER(src); #endif } static void fromHLFtoFLT(void* dst, const void* src) { #ifndef CMS_NO_HALF_SUPPORT *(cmsFloat32Number*)dst = _cmsHalf2Float(*(cmsUInt16Number*)src); #else cmsUNUSED_PARAMETER(dst); cmsUNUSED_PARAMETER(src); #endif } static void fromHLFtoDBL(void* dst, const void* src) { #ifndef CMS_NO_HALF_SUPPORT *(cmsFloat64Number*)dst = (cmsFloat64Number)_cmsHalf2Float(*(cmsUInt16Number*)src); #else cmsUNUSED_PARAMETER(dst); cmsUNUSED_PARAMETER(src); #endif } // From double static void fromDBLto8(void* dst, const void* src) { cmsFloat64Number n = *(cmsFloat64Number*)src; *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0); } static void fromDBLto16(void* dst, const void* src) { cmsFloat64Number n = *(cmsFloat64Number*)src; *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); } static void fromDBLtoFLT(void* dst, const void* src) { cmsFloat64Number n = *(cmsFloat64Number*)src; *(cmsFloat32Number*)dst = (cmsFloat32Number) n; } static void fromDBLtoHLF(void* dst, const void* src) { #ifndef CMS_NO_HALF_SUPPORT cmsFloat32Number n = (cmsFloat32Number) *(cmsFloat64Number*)src; *(cmsUInt16Number*)dst = _cmsFloat2Half(n); #else cmsUNUSED_PARAMETER(dst); cmsUNUSED_PARAMETER(src); #endif } static void copy64(void* dst, const void* src) { memmove(dst, src, sizeof(cmsFloat64Number)); } // Returns the position (x or y) of the formatter in the table of functions static int FormatterPos(cmsUInt32Number frm) { cmsUInt32Number b = T_BYTES(frm); if (b == 0 && T_FLOAT(frm)) return 4; // DBL #ifndef CMS_NO_HALF_SUPPORT if (b == 2 && T_FLOAT(frm)) return 2; // HLF #endif if (b == 4 && T_FLOAT(frm)) return 3; // FLT if (b == 2 && !T_FLOAT(frm)) return 1; // 16 if (b == 1 && !T_FLOAT(frm)) return 0; // 8 return -1; // not recognized } // Obtains a alpha-to-alpha funmction formatter static cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out) { static const cmsFormatterAlphaFn FormattersAlpha[5][5] = { /* from 8 */ { copy8, from8to16, from8toHLF, from8toFLT, from8toDBL }, /* from 16*/ { from16to8, copy16, from16toHLF, from16toFLT, from16toDBL }, /* from HLF*/ { fromHLFto8, fromHLFto16, copy16, fromHLFtoFLT, fromHLFtoDBL }, /* from FLT*/ { fromFLTto8, fromFLTto16, fromFLTtoHLF, copy32, fromFLTtoDBL }, /* from DBL*/ { fromDBLto8, fromDBLto16, fromDBLtoHLF, fromDBLtoFLT, copy64 }}; int in_n = FormatterPos(in); int out_n = FormatterPos(out); if (in_n < 0 || out_n < 0 || in_n > 4 || out_n > 4) { cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized alpha channel width"); return NULL; } return FormattersAlpha[in_n][out_n]; } // This function computes the distance from each component to the next one in bytes. static void ComputeIncrementsForChunky(cmsUInt32Number Format, cmsUInt32Number ComponentStartingOrder[], cmsUInt32Number ComponentPointerIncrements[]) { cmsUInt32Number channels[cmsMAXCHANNELS]; cmsUInt32Number extra = T_EXTRA(Format); cmsUInt32Number nchannels = T_CHANNELS(Format); cmsUInt32Number total_chans = nchannels + extra; cmsUInt32Number i; cmsUInt32Number channelSize = trueBytesSize(Format); cmsUInt32Number pixelSize = channelSize * total_chans; // Sanity check if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS) return; memset(channels, 0, sizeof(channels)); // Separation is independent of starting point and only depends on channel size for (i = 0; i < extra; i++) ComponentPointerIncrements[i] = pixelSize; // Handle do swap for (i = 0; i < total_chans; i++) { if (T_DOSWAP(Format)) { channels[i] = total_chans - i - 1; } else { channels[i] = i; } } // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012 if (T_SWAPFIRST(Format) && total_chans > 1) { cmsUInt32Number tmp = channels[0]; for (i = 0; i < total_chans-1; i++) channels[i] = channels[i + 1]; channels[total_chans - 1] = tmp; } // Handle size if (channelSize > 1) for (i = 0; i < total_chans; i++) { channels[i] *= channelSize; } for (i = 0; i < extra; i++) ComponentStartingOrder[i] = channels[i + nchannels]; } // On planar configurations, the distance is the stride added to any non-negative static void ComputeIncrementsForPlanar(cmsUInt32Number Format, cmsUInt32Number BytesPerPlane, cmsUInt32Number ComponentStartingOrder[], cmsUInt32Number ComponentPointerIncrements[]) { cmsUInt32Number channels[cmsMAXCHANNELS]; cmsUInt32Number extra = T_EXTRA(Format); cmsUInt32Number nchannels = T_CHANNELS(Format); cmsUInt32Number total_chans = nchannels + extra; cmsUInt32Number i; cmsUInt32Number channelSize = trueBytesSize(Format); // Sanity check if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS) return; memset(channels, 0, sizeof(channels)); // Separation is independent of starting point and only depends on channel size for (i = 0; i < extra; i++) ComponentPointerIncrements[i] = channelSize; // Handle do swap for (i = 0; i < total_chans; i++) { if (T_DOSWAP(Format)) { channels[i] = total_chans - i - 1; } else { channels[i] = i; } } // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012 if (T_SWAPFIRST(Format) && total_chans > 0) { cmsUInt32Number tmp = channels[0]; for (i = 0; i < total_chans - 1; i++) channels[i] = channels[i + 1]; channels[total_chans - 1] = tmp; } // Handle size for (i = 0; i < total_chans; i++) { channels[i] *= BytesPerPlane; } for (i = 0; i < extra; i++) ComponentStartingOrder[i] = channels[i + nchannels]; } // Dispatcher por chunky and planar RGB static void ComputeComponentIncrements(cmsUInt32Number Format, cmsUInt32Number BytesPerPlane, cmsUInt32Number ComponentStartingOrder[], cmsUInt32Number ComponentPointerIncrements[]) { if (T_PLANAR(Format)) { ComputeIncrementsForPlanar(Format, BytesPerPlane, ComponentStartingOrder, ComponentPointerIncrements); } else { ComputeIncrementsForChunky(Format, ComponentStartingOrder, ComponentPointerIncrements); } } // Handles extra channels copying alpha if requested by the flags void _cmsHandleExtraChannels(_cmsTRANSFORM* p, const void* in, void* out, cmsUInt32Number PixelsPerLine, cmsUInt32Number LineCount, const cmsStride* Stride) { cmsUInt32Number i, j, k; cmsUInt32Number nExtra; cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS]; cmsUInt32Number SourceIncrements[cmsMAXCHANNELS]; cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS]; cmsUInt32Number DestIncrements[cmsMAXCHANNELS]; cmsFormatterAlphaFn copyValueFn; // Make sure we need some copy if (!(p->dwOriginalFlags & cmsFLAGS_COPY_ALPHA)) return; // Exit early if in-place color-management is occurring - no need to copy extra channels to themselves. if (p->InputFormat == p->OutputFormat && in == out) return; // Make sure we have same number of alpha channels. If not, just return as this should be checked at transform creation time. nExtra = T_EXTRA(p->InputFormat); if (nExtra != T_EXTRA(p->OutputFormat)) return; // Anything to do? if (nExtra == 0) return; // Compute the increments ComputeComponentIncrements(p->InputFormat, Stride->BytesPerPlaneIn, SourceStartingOrder, SourceIncrements); ComputeComponentIncrements(p->OutputFormat, Stride->BytesPerPlaneOut, DestStartingOrder, DestIncrements); // Check for conversions 8, 16, half, float, dbl copyValueFn = _cmsGetFormatterAlpha(p->ContextID, p->InputFormat, p->OutputFormat); if (nExtra == 1) { // Optimized routine for copying a single extra channel quickly cmsUInt8Number* SourcePtr; cmsUInt8Number* DestPtr; cmsUInt32Number SourceStrideIncrement = 0; cmsUInt32Number DestStrideIncrement = 0; // The loop itself for (i = 0; i < LineCount; i++) { // Prepare pointers for the loop SourcePtr = (cmsUInt8Number*)in + SourceStartingOrder[0] + SourceStrideIncrement; DestPtr = (cmsUInt8Number*)out + DestStartingOrder[0] + DestStrideIncrement; for (j = 0; j < PixelsPerLine; j++) { copyValueFn(DestPtr, SourcePtr); SourcePtr += SourceIncrements[0]; DestPtr += DestIncrements[0]; } SourceStrideIncrement += Stride->BytesPerLineIn; DestStrideIncrement += Stride->BytesPerLineOut; } } else { // General case with more than one extra channel cmsUInt8Number* SourcePtr[cmsMAXCHANNELS]; cmsUInt8Number* DestPtr[cmsMAXCHANNELS]; cmsUInt32Number SourceStrideIncrements[cmsMAXCHANNELS]; cmsUInt32Number DestStrideIncrements[cmsMAXCHANNELS]; memset(SourceStrideIncrements, 0, sizeof(SourceStrideIncrements)); memset(DestStrideIncrements, 0, sizeof(DestStrideIncrements)); // The loop itself for (i = 0; i < LineCount; i++) { // Prepare pointers for the loop for (j = 0; j < nExtra; j++) { SourcePtr[j] = (cmsUInt8Number*)in + SourceStartingOrder[j] + SourceStrideIncrements[j]; DestPtr[j] = (cmsUInt8Number*)out + DestStartingOrder[j] + DestStrideIncrements[j]; } for (j = 0; j < PixelsPerLine; j++) { for (k = 0; k < nExtra; k++) { copyValueFn(DestPtr[k], SourcePtr[k]); SourcePtr[k] += SourceIncrements[k]; DestPtr[k] += DestIncrements[k]; } } for (j = 0; j < nExtra; j++) { SourceStrideIncrements[j] += Stride->BytesPerLineIn; DestStrideIncrements[j] += Stride->BytesPerLineOut; } } } }