2016-04-30 17:58:04 +02:00
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Little Color Management System
|
|
|
|
// Copyright (c) 1998-2016 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
|
|
|
|
int trueBytesSize(cmsUInt32Number Format)
|
|
|
|
{
|
|
|
|
int 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)
|
|
|
|
{
|
|
|
|
cmsFloat32Number n = (*(cmsUInt8Number*)src) / 255.0f;
|
|
|
|
*(cmsUInt16Number*)dst = _cmsFloat2Half(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
cmsFloat32Number n = (*(cmsUInt16Number*)src) / 65535.0f;
|
|
|
|
*(cmsUInt16Number*)dst = _cmsFloat2Half(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
cmsFloat32Number n = *(cmsFloat32Number*)src;
|
|
|
|
*(cmsUInt16Number*)dst = _cmsFloat2Half(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// From HALF
|
|
|
|
|
|
|
|
static
|
|
|
|
void fromHLFto8(void* dst, const void* src)
|
|
|
|
{
|
|
|
|
cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
|
|
|
|
*(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
void fromHLFto16(void* dst, const void* src)
|
|
|
|
{
|
|
|
|
cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
|
|
|
|
*(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
void fromHLFtoFLT(void* dst, const void* src)
|
|
|
|
{
|
|
|
|
*(cmsFloat32Number*)dst = _cmsHalf2Float(*(cmsUInt16Number*)src);
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
void fromHLFtoDBL(void* dst, const void* src)
|
|
|
|
{
|
|
|
|
*(cmsFloat64Number*)dst = (cmsFloat64Number)_cmsHalf2Float(*(cmsUInt16Number*)src);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
cmsFloat32Number n = (cmsFloat32Number) *(cmsFloat64Number*)src;
|
|
|
|
*(cmsUInt16Number*)dst = _cmsFloat2Half(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
int b = T_BYTES(frm);
|
|
|
|
|
|
|
|
if (b == 0 && T_FLOAT(frm))
|
|
|
|
return 4; // DBL
|
|
|
|
if (b == 2 && T_FLOAT(frm))
|
|
|
|
return 2; // HLF
|
|
|
|
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 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];
|
|
|
|
int extra = T_EXTRA(Format);
|
|
|
|
int nchannels = T_CHANNELS(Format);
|
|
|
|
int total_chans = nchannels + extra;
|
|
|
|
int i;
|
|
|
|
int channelSize = trueBytesSize(Format);
|
|
|
|
int 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];
|
|
|
|
int extra = T_EXTRA(Format);
|
|
|
|
int nchannels = T_CHANNELS(Format);
|
|
|
|
int total_chans = nchannels + extra;
|
|
|
|
int i;
|
|
|
|
int 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,
|
2016-08-06 13:04:56 +02:00
|
|
|
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];
|
2016-04-30 17:58:04 +02:00
|
|
|
|
2016-08-06 13:04:56 +02:00
|
|
|
cmsFormatterAlphaFn copyValueFn;
|
2016-04-30 17:58:04 +02:00
|
|
|
|
2016-08-06 13:04:56 +02:00
|
|
|
// Make sure we need some copy
|
|
|
|
if (!(p->dwOriginalFlags & cmsFLAGS_COPY_ALPHA))
|
|
|
|
return;
|
2016-04-30 17:58:04 +02:00
|
|
|
|
2016-08-06 13:04:56 +02:00
|
|
|
// 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;
|
2016-04-30 17:58:04 +02:00
|
|
|
|
2016-08-06 13:04:56 +02:00
|
|
|
// 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;
|
2016-04-30 17:58:04 +02:00
|
|
|
|
2016-08-06 13:04:56 +02:00
|
|
|
// Anything to do?
|
|
|
|
if (nExtra == 0)
|
|
|
|
return;
|
2016-04-30 17:58:04 +02:00
|
|
|
|
2016-08-06 13:04:56 +02:00
|
|
|
// Compute the increments
|
|
|
|
ComputeComponentIncrements(p->InputFormat, Stride->BytesPerPlaneIn, SourceStartingOrder, SourceIncrements);
|
|
|
|
ComputeComponentIncrements(p->OutputFormat, Stride->BytesPerPlaneOut, DestStartingOrder, DestIncrements);
|
2016-04-30 17:58:04 +02:00
|
|
|
|
2016-08-06 13:04:56 +02:00
|
|
|
// Check for conversions 8, 16, half, float, dbl
|
|
|
|
copyValueFn = _cmsGetFormatterAlpha(p->ContextID, p->InputFormat, p->OutputFormat);
|
2016-04-30 17:58:04 +02:00
|
|
|
|
2016-08-06 13:04:56 +02:00
|
|
|
if (nExtra == 1) { // Optimized routine for copying a single extra channel quickly
|
2016-04-30 17:58:04 +02:00
|
|
|
|
2016-08-06 13:04:56 +02:00
|
|
|
cmsUInt8Number* SourcePtr;
|
|
|
|
cmsUInt8Number* DestPtr;
|
2016-04-30 17:58:04 +02:00
|
|
|
|
2016-08-06 13:04:56 +02:00
|
|
|
cmsUInt32Number SourceStrideIncrement = 0;
|
|
|
|
cmsUInt32Number DestStrideIncrement = 0;
|
2016-04-30 17:58:04 +02:00
|
|
|
|
2016-08-06 13:04:56 +02:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-04-30 17:58:04 +02:00
|
|
|
}
|
|
|
|
|
2016-08-06 13:04:56 +02:00
|
|
|
|