Coverage Report

Created: 2025-07-23 08:18

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