Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/lcms2mt/src/cmsalpha.c
Line
Count
Source (jump to first uncovered line)
1
//---------------------------------------------------------------------------------
2
//
3
//  Little Color Management System
4
//  Copyright (c) 1998-2020 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.0f);
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.0f);
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.0f);
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.0f);
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.0f);
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.0f);
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 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
void ComputeIncrementsForChunky(cmsUInt32Number Format,
406
                                cmsUInt32Number ComponentStartingOrder[],
407
                                cmsUInt32Number ComponentPointerIncrements[])
408
0
{
409
0
       cmsUInt32Number channels[cmsMAXEXTRACHANNELS];
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 >= cmsMAXEXTRACHANNELS)
419
0
       return;
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
0
}
457
458
459
460
//  On planar configurations, the distance is the stride added to any non-negative
461
static
462
void ComputeIncrementsForPlanar(cmsUInt32Number Format,
463
                                cmsUInt32Number BytesPerPlane,
464
                                cmsUInt32Number ComponentStartingOrder[],
465
                                cmsUInt32Number ComponentPointerIncrements[])
466
0
{
467
0
       cmsUInt32Number channels[cmsMAXEXTRACHANNELS];
468
0
       cmsUInt32Number extra = T_EXTRA(Format);
469
0
       cmsUInt32Number nchannels = T_CHANNELS(Format);
470
0
       cmsUInt32Number total_chans = nchannels + extra;
471
0
       cmsUInt32Number i;
472
0
       cmsUInt32Number channelSize = trueBytesSize(Format);
473
474
       // Sanity check
475
0
       if (total_chans <= 0 || total_chans >= cmsMAXEXTRACHANNELS)
476
0
           return;
477
478
0
       memset(channels, 0, sizeof(channels));
479
480
       // Separation is independent of starting point and only depends on channel size
481
0
       for (i = 0; i < extra; i++)
482
0
              ComponentPointerIncrements[i] = channelSize;
483
484
       // Handle do swap
485
0
       for (i = 0; i < total_chans; i++)
486
0
       {
487
0
              if (T_DOSWAP(Format)) {
488
0
                     channels[i] = total_chans - i - 1;
489
0
              }
490
0
              else {
491
0
                     channels[i] = i;
492
0
              }
493
0
       }
494
495
       // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012
496
0
       if (T_SWAPFIRST(Format) && total_chans > 0) {
497
498
0
              cmsUInt32Number tmp = channels[0];
499
0
              for (i = 0; i < total_chans - 1; i++)
500
0
                     channels[i] = channels[i + 1];
501
502
0
              channels[total_chans - 1] = tmp;
503
0
       }
504
505
       // Handle size
506
0
       for (i = 0; i < total_chans; i++) {
507
0
              channels[i] *= BytesPerPlane;
508
0
       }
509
510
0
       for (i = 0; i < extra; i++)
511
0
              ComponentStartingOrder[i] = channels[i + nchannels];
512
0
}
513
514
515
516
// Dispatcher por chunky and planar RGB
517
static
518
void  ComputeComponentIncrements(cmsUInt32Number Format,
519
                                 cmsUInt32Number BytesPerPlane,
520
                                 cmsUInt32Number ComponentStartingOrder[],
521
                                 cmsUInt32Number ComponentPointerIncrements[])
522
0
{
523
0
       if (T_PLANAR(Format)) {
524
525
0
              ComputeIncrementsForPlanar(Format,  BytesPerPlane, ComponentStartingOrder, ComponentPointerIncrements);
526
0
       }
527
0
       else {
528
0
              ComputeIncrementsForChunky(Format,  ComponentStartingOrder, ComponentPointerIncrements);
529
0
       }
530
531
0
}
532
533
534
535
// Handles extra channels copying alpha if requested by the flags
536
void _cmsHandleExtraChannels(cmsContext ContextID, _cmsTRANSFORM* p, const void* in,
537
                                               void* out,
538
                                               cmsUInt32Number PixelsPerLine,
539
                                               cmsUInt32Number LineCount,
540
                                               const cmsStride* Stride)
541
9.87k
{
542
9.87k
    cmsUInt32Number i, j, k;
543
9.87k
    cmsUInt32Number nExtra;
544
9.87k
    cmsUInt32Number SourceStartingOrder[cmsMAXEXTRACHANNELS];
545
9.87k
    cmsUInt32Number SourceIncrements[cmsMAXEXTRACHANNELS];
546
9.87k
    cmsUInt32Number DestStartingOrder[cmsMAXEXTRACHANNELS];
547
9.87k
    cmsUInt32Number DestIncrements[cmsMAXEXTRACHANNELS];
548
549
9.87k
    cmsFormatterAlphaFn copyValueFn;
550
551
    // Make sure we need some copy
552
9.87k
    if (!(p->core->dwOriginalFlags & cmsFLAGS_COPY_ALPHA))
553
9.87k
        return;
554
555
    // Exit early if in-place color-management is occurring - no need to copy extra channels to themselves.
556
0
    if (p->InputFormat == p->OutputFormat && in == out)
557
0
        return;
558
559
    // Make sure we have same number of alpha channels. If not, just return as this should be checked at transform creation time.
560
0
    nExtra = T_EXTRA(p->InputFormat);
561
0
    if (nExtra != T_EXTRA(p->OutputFormat))
562
0
        return;
563
564
    // Anything to do?
565
0
    if (nExtra == 0)
566
0
        return;
567
568
    // Compute the increments
569
0
    ComputeComponentIncrements(p->InputFormat, Stride->BytesPerPlaneIn, SourceStartingOrder, SourceIncrements);
570
0
    ComputeComponentIncrements(p->OutputFormat, Stride->BytesPerPlaneOut, DestStartingOrder, DestIncrements);
571
572
    // Check for conversions 8, 16, half, float, dbl
573
0
    copyValueFn = _cmsGetFormatterAlpha(ContextID, p->InputFormat, p->OutputFormat);
574
0
    if (copyValueFn == NULL)
575
0
        return;
576
577
0
    if (nExtra == 1) { // Optimized routine for copying a single extra channel quickly
578
579
0
        cmsUInt8Number* SourcePtr;
580
0
        cmsUInt8Number* DestPtr;
581
582
0
        cmsUInt32Number SourceStrideIncrement = 0;
583
0
        cmsUInt32Number DestStrideIncrement = 0;
584
585
        // The loop itself
586
0
        for (i = 0; i < LineCount; i++) {
587
588
            // Prepare pointers for the loop
589
0
            SourcePtr = (cmsUInt8Number*)in + SourceStartingOrder[0] + SourceStrideIncrement;
590
0
            DestPtr = (cmsUInt8Number*)out + DestStartingOrder[0] + DestStrideIncrement;
591
592
0
            for (j = 0; j < PixelsPerLine; j++) {
593
594
0
                copyValueFn(DestPtr, SourcePtr);
595
596
0
                SourcePtr += SourceIncrements[0];
597
0
                DestPtr += DestIncrements[0];
598
0
            }
599
600
0
            SourceStrideIncrement += Stride->BytesPerLineIn;
601
0
            DestStrideIncrement += Stride->BytesPerLineOut;
602
0
        }
603
604
0
    }
605
0
    else { // General case with more than one extra channel
606
607
0
        cmsUInt8Number* SourcePtr[cmsMAXEXTRACHANNELS];
608
0
        cmsUInt8Number* DestPtr[cmsMAXEXTRACHANNELS];
609
610
0
        cmsUInt32Number SourceStrideIncrements[cmsMAXEXTRACHANNELS];
611
0
        cmsUInt32Number DestStrideIncrements[cmsMAXEXTRACHANNELS];
612
613
0
        memset(SourceStrideIncrements, 0, sizeof(SourceStrideIncrements));
614
0
        memset(DestStrideIncrements, 0, sizeof(DestStrideIncrements));
615
616
        // The loop itself
617
0
        for (i = 0; i < LineCount; i++) {
618
619
            // Prepare pointers for the loop
620
0
            for (j = 0; j < nExtra; j++) {
621
622
0
                SourcePtr[j] = (cmsUInt8Number*)in + SourceStartingOrder[j] + SourceStrideIncrements[j];
623
0
                DestPtr[j] = (cmsUInt8Number*)out + DestStartingOrder[j] + DestStrideIncrements[j];
624
0
            }
625
626
0
            for (j = 0; j < PixelsPerLine; j++) {
627
628
0
                for (k = 0; k < nExtra; k++) {
629
630
0
                    copyValueFn(DestPtr[k], SourcePtr[k]);
631
632
0
                    SourcePtr[k] += SourceIncrements[k];
633
0
                    DestPtr[k] += DestIncrements[k];
634
0
                }
635
0
            }
636
637
0
            for (j = 0; j < nExtra; j++) {
638
639
0
                SourceStrideIncrements[j] += Stride->BytesPerLineIn;
640
0
                DestStrideIncrements[j] += Stride->BytesPerLineOut;
641
0
            }
642
0
        }
643
0
    }
644
0
}