1 /*
  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  3  *
  4  * This code is free software; you can redistribute it and/or modify it
  5  * under the terms of the GNU General Public License version 2 only, as
  6  * published by the Free Software Foundation.  Oracle designates this
  7  * particular file as subject to the "Classpath" exception as provided
  8  * by Oracle in the LICENSE file that accompanied this code.
  9  *
 10  * This code is distributed in the hope that it will be useful, but WITHOUT
 11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 13  * version 2 for more details (a copy is included in the LICENSE file that
 14  * accompanied this code).
 15  *
 16  * You should have received a copy of the GNU General Public License version
 17  * 2 along with this work; if not, write to the Free Software Foundation,
 18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 19  *
 20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 21  * or visit www.oracle.com if you need additional information or have any
 22  * questions.
 23  */
 24 
 25 // This file is available under and governed by the GNU General Public
 26 // License version 2 only, as published by the Free Software Foundation.
 27 // However, the following notice accompanied the original version of this
 28 // file:
 29 //
 30 //---------------------------------------------------------------------------------
 31 //
 32 //  Little Color Management System
 33 //  Copyright (c) 1998-2023 Marti Maria Saguer
 34 //
 35 // Permission is hereby granted, free of charge, to any person obtaining
 36 // a copy of this software and associated documentation files (the "Software"),
 37 // to deal in the Software without restriction, including without limitation
 38 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
 39 // and/or sell copies of the Software, and to permit persons to whom the Software
 40 // is furnished to do so, subject to the following conditions:
 41 //
 42 // The above copyright notice and this permission notice shall be included in
 43 // all copies or substantial portions of the Software.
 44 //
 45 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 46 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 47 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 48 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 49 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 50 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 51 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 52 //
 53 //---------------------------------------------------------------------------------
 54 //
 55 
 56 #include "lcms2_internal.h"
 57 
 58 // Alpha copy ------------------------------------------------------------------------------------------------------------------
 59 
 60 // This macro return words stored as big endian
 61 #define CHANGE_ENDIAN(w)    (cmsUInt16Number) ((cmsUInt16Number) ((w)<<8)|((w)>>8))
 62 
 63 
 64 // Floor to byte, taking care of saturation
 65 cmsINLINE cmsUInt8Number _cmsQuickSaturateByte(cmsFloat64Number d)
 66 {
 67        d += 0.5;
 68        if (d <= 0) return 0;
 69        if (d >= 255.0) return 255;
 70 
 71        return (cmsUInt8Number) _cmsQuickFloorWord(d);
 72 }
 73 
 74 
 75 // Return the size in bytes of a given formatter
 76 static
 77 cmsUInt32Number trueBytesSize(cmsUInt32Number Format)
 78 {
 79     cmsUInt32Number fmt_bytes = T_BYTES(Format);
 80 
 81     // For double, the T_BYTES field returns zero
 82     if (fmt_bytes == 0)
 83         return sizeof(double);
 84 
 85     // Otherwise, it is already correct for all formats
 86     return fmt_bytes;
 87 }
 88 
 89 
 90 // Several format converters
 91 
 92 typedef void(*cmsFormatterAlphaFn)(void* dst, const void* src);
 93 
 94 
 95 // From 8
 96 
 97 static
 98 void copy8(void* dst, const void* src)
 99 {
100        memmove(dst, src, 1);
101 }
102 
103 static
104 void from8to16(void* dst, const void* src)
105 {
106        cmsUInt8Number n = *(cmsUInt8Number*)src;
107        *(cmsUInt16Number*) dst = (cmsUInt16Number) FROM_8_TO_16(n);
108 }
109 
110 static
111 void from8to16SE(void* dst, const void* src)
112 {
113     cmsUInt8Number n = *(cmsUInt8Number*)src;
114     *(cmsUInt16Number*)dst = CHANGE_ENDIAN(FROM_8_TO_16(n));
115 }
116 
117 static
118 void from8toFLT(void* dst, const void* src)
119 {
120        *(cmsFloat32Number*)dst = (cmsFloat32Number) (*(cmsUInt8Number*)src) / 255.0f;
121 }
122 
123 static
124 void from8toDBL(void* dst, const void* src)
125 {
126        *(cmsFloat64Number*)dst = (cmsFloat64Number) (*(cmsUInt8Number*)src) / 255.0;
127 }
128 
129 static
130 void from8toHLF(void* dst, const void* src)
131 {
132 #ifndef CMS_NO_HALF_SUPPORT
133        cmsFloat32Number n = (*(cmsUInt8Number*)src) / 255.0f;
134        *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
135 #else
136     cmsUNUSED_PARAMETER(dst);
137     cmsUNUSED_PARAMETER(src);
138 #endif
139 }
140 
141 // From 16
142 
143 static
144 void from16to8(void* dst, const void* src)
145 {
146        cmsUInt16Number n = *(cmsUInt16Number*)src;
147        *(cmsUInt8Number*) dst = FROM_16_TO_8(n);
148 }
149 
150 static
151 void from16SEto8(void* dst, const void* src)
152 {
153     cmsUInt16Number n = *(cmsUInt16Number*)src;
154     *(cmsUInt8Number*)dst = FROM_16_TO_8(CHANGE_ENDIAN(n));
155 }
156 
157 static
158 void copy16(void* dst, const void* src)
159 {
160        memmove(dst, src, 2);
161 }
162 
163 static
164 void from16to16(void* dst, const void* src)
165 {
166     cmsUInt16Number n = *(cmsUInt16Number*)src;
167     *(cmsUInt16Number*)dst = CHANGE_ENDIAN(n);
168 }
169 
170 static
171 void from16toFLT(void* dst, const void* src)
172 {
173        *(cmsFloat32Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f;
174 }
175 
176 static
177 void from16SEtoFLT(void* dst, const void* src)
178 {
179     *(cmsFloat32Number*)dst = (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0f;
180 }
181 
182 static
183 void from16toDBL(void* dst, const void* src)
184 {
185        *(cmsFloat64Number*)dst = (cmsFloat64Number) (*(cmsUInt16Number*)src) / 65535.0;
186 }
187 
188 static
189 void from16SEtoDBL(void* dst, const void* src)
190 {
191     *(cmsFloat64Number*)dst = (cmsFloat64Number) (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0;
192 }
193 
194 static
195 void from16toHLF(void* dst, const void* src)
196 {
197 #ifndef CMS_NO_HALF_SUPPORT
198        cmsFloat32Number n = (*(cmsUInt16Number*)src) / 65535.0f;
199        *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
200 #else
201     cmsUNUSED_PARAMETER(dst);
202     cmsUNUSED_PARAMETER(src);
203 #endif
204 }
205 
206 static
207 void from16SEtoHLF(void* dst, const void* src)
208 {
209 #ifndef CMS_NO_HALF_SUPPORT
210     cmsFloat32Number n = (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0f;
211     *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
212 #else
213     cmsUNUSED_PARAMETER(dst);
214     cmsUNUSED_PARAMETER(src);
215 #endif
216 }
217 // From Float
218 
219 static
220 void fromFLTto8(void* dst, const void* src)
221 {
222     cmsFloat32Number n = *(cmsFloat32Number*)src;
223     *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0);
224 }
225 
226 static
227 void fromFLTto16(void* dst, const void* src)
228 {
229     cmsFloat32Number n = *(cmsFloat32Number*)src;
230     *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0);
231 }
232 
233 static
234 void fromFLTto16SE(void* dst, const void* src)
235 {
236     cmsFloat32Number n = *(cmsFloat32Number*)src;
237     cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0);
238 
239     *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i);
240 }
241 
242 static
243 void copy32(void* dst, const void* src)
244 {
245     memmove(dst, src, sizeof(cmsFloat32Number));
246 }
247 
248 static
249 void fromFLTtoDBL(void* dst, const void* src)
250 {
251     cmsFloat32Number n = *(cmsFloat32Number*)src;
252     *(cmsFloat64Number*)dst = (cmsFloat64Number)n;
253 }
254 
255 static
256 void fromFLTtoHLF(void* dst, const void* src)
257 {
258 #ifndef CMS_NO_HALF_SUPPORT
259        cmsFloat32Number n = *(cmsFloat32Number*)src;
260        *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
261 #else
262     cmsUNUSED_PARAMETER(dst);
263     cmsUNUSED_PARAMETER(src);
264 #endif
265 }
266 
267 
268 // From HALF
269 
270 static
271 void fromHLFto8(void* dst, const void* src)
272 {
273 #ifndef CMS_NO_HALF_SUPPORT
274        cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
275        *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0);
276 #else
277     cmsUNUSED_PARAMETER(dst);
278     cmsUNUSED_PARAMETER(src);
279 #endif
280 
281 }
282 
283 static
284 void fromHLFto16(void* dst, const void* src)
285 {
286 #ifndef CMS_NO_HALF_SUPPORT
287        cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
288        *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0);
289 #else
290     cmsUNUSED_PARAMETER(dst);
291     cmsUNUSED_PARAMETER(src);
292 #endif
293 }
294 
295 static
296 void fromHLFto16SE(void* dst, const void* src)
297 {
298 #ifndef CMS_NO_HALF_SUPPORT
299     cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
300     cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0);
301     *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i);
302 #else
303     cmsUNUSED_PARAMETER(dst);
304     cmsUNUSED_PARAMETER(src);
305 #endif
306 }
307 
308 static
309 void fromHLFtoFLT(void* dst, const void* src)
310 {
311 #ifndef CMS_NO_HALF_SUPPORT
312        *(cmsFloat32Number*)dst = _cmsHalf2Float(*(cmsUInt16Number*)src);
313 #else
314     cmsUNUSED_PARAMETER(dst);
315     cmsUNUSED_PARAMETER(src);
316 #endif
317 }
318 
319 static
320 void fromHLFtoDBL(void* dst, const void* src)
321 {
322 #ifndef CMS_NO_HALF_SUPPORT
323        *(cmsFloat64Number*)dst = (cmsFloat64Number)_cmsHalf2Float(*(cmsUInt16Number*)src);
324 #else
325     cmsUNUSED_PARAMETER(dst);
326     cmsUNUSED_PARAMETER(src);
327 #endif
328 }
329 
330 // From double
331 static
332 void fromDBLto8(void* dst, const void* src)
333 {
334        cmsFloat64Number n = *(cmsFloat64Number*)src;
335        *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0);
336 }
337 
338 static
339 void fromDBLto16(void* dst, const void* src)
340 {
341        cmsFloat64Number n = *(cmsFloat64Number*)src;
342        *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
343 }
344 
345 static
346 void fromDBLto16SE(void* dst, const void* src)
347 {
348     cmsFloat64Number n = *(cmsFloat64Number*)src;
349     cmsUInt16Number  i = _cmsQuickSaturateWord(n * 65535.0f);
350     *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i);
351 }
352 
353 static
354 void fromDBLtoFLT(void* dst, const void* src)
355 {
356        cmsFloat64Number n = *(cmsFloat64Number*)src;
357        *(cmsFloat32Number*)dst = (cmsFloat32Number) n;
358 }
359 
360 static
361 void fromDBLtoHLF(void* dst, const void* src)
362 {
363 #ifndef CMS_NO_HALF_SUPPORT
364        cmsFloat32Number n = (cmsFloat32Number) *(cmsFloat64Number*)src;
365        *(cmsUInt16Number*)dst = _cmsFloat2Half(n);
366 #else
367     cmsUNUSED_PARAMETER(dst);
368     cmsUNUSED_PARAMETER(src);
369 #endif
370 }
371 
372 static
373 void copy64(void* dst, const void* src)
374 {
375        memmove(dst, src, sizeof(cmsFloat64Number));
376 }
377 
378 
379 // Returns the position (x or y) of the formatter in the table of functions
380 static
381 int FormatterPos(cmsUInt32Number frm)
382 {
383     cmsUInt32Number  b = T_BYTES(frm);
384 
385     if (b == 0 && T_FLOAT(frm))
386         return 5; // DBL
387 #ifndef CMS_NO_HALF_SUPPORT
388     if (b == 2 && T_FLOAT(frm))
389         return 3; // HLF
390 #endif
391     if (b == 4 && T_FLOAT(frm))
392         return 4; // FLT
393     if (b == 2 && !T_FLOAT(frm))
394     {
395         if (T_ENDIAN16(frm))
396             return 2; // 16SE
397         else
398             return 1; // 16
399     }
400     if (b == 1 && !T_FLOAT(frm))
401         return 0; // 8
402     return -1; // not recognized
403 }
404 
405 // Obtains an alpha-to-alpha function formatter
406 static
407 cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out)
408 {
409 static cmsFormatterAlphaFn FormattersAlpha[6][6] = {
410 
411        /* from 8 */  { copy8,       from8to16,   from8to16SE,   from8toHLF,   from8toFLT,    from8toDBL    },
412        /* from 16*/  { from16to8,   copy16,      from16to16,    from16toHLF,  from16toFLT,   from16toDBL   },
413        /* from 16SE*/{ from16SEto8, from16to16,  copy16,        from16SEtoHLF,from16SEtoFLT, from16SEtoDBL },
414        /* from HLF*/ { fromHLFto8,  fromHLFto16, fromHLFto16SE, copy16,       fromHLFtoFLT,  fromHLFtoDBL  },
415        /* from FLT*/ { fromFLTto8,  fromFLTto16, fromFLTto16SE, fromFLTtoHLF, copy32,        fromFLTtoDBL  },
416        /* from DBL*/ { fromDBLto8,  fromDBLto16, fromDBLto16SE, fromDBLtoHLF, fromDBLtoFLT,  copy64 }};
417 
418         int in_n  = FormatterPos(in);
419         int out_n = FormatterPos(out);
420 
421         if (in_n < 0 || out_n < 0 || in_n > 5 || out_n > 5) {
422 
423                cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized alpha channel width");
424                return NULL;
425         }
426 
427         return FormattersAlpha[in_n][out_n];
428 }
429 
430 
431 
432 // This function computes the distance from each component to the next one in bytes.
433 static
434 void ComputeIncrementsForChunky(cmsUInt32Number Format,
435                                 cmsUInt32Number ComponentStartingOrder[],
436                                 cmsUInt32Number ComponentPointerIncrements[])
437 {
438        cmsUInt32Number channels[cmsMAXCHANNELS];
439        cmsUInt32Number extra = T_EXTRA(Format);
440        cmsUInt32Number nchannels = T_CHANNELS(Format);
441        cmsUInt32Number total_chans = nchannels + extra;
442        cmsUInt32Number i;
443        cmsUInt32Number channelSize = trueBytesSize(Format);
444        cmsUInt32Number pixelSize = channelSize * total_chans;
445 
446        // Sanity check
447        if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS)
448            return;
449 
450         memset(channels, 0, sizeof(channels));
451 
452        // Separation is independent of starting point and only depends on channel size
453        for (i = 0; i < extra; i++)
454               ComponentPointerIncrements[i] = pixelSize;
455 
456        // Handle do swap
457        for (i = 0; i < total_chans; i++)
458        {
459               if (T_DOSWAP(Format)) {
460                      channels[i] = total_chans - i - 1;
461               }
462               else {
463                      channels[i] = i;
464               }
465        }
466 
467        // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012
468        if (T_SWAPFIRST(Format) && total_chans > 1) {
469 
470               cmsUInt32Number tmp = channels[0];
471               for (i = 0; i < total_chans-1; i++)
472                      channels[i] = channels[i + 1];
473 
474               channels[total_chans - 1] = tmp;
475        }
476 
477        // Handle size
478        if (channelSize > 1)
479               for (i = 0; i < total_chans; i++) {
480                      channels[i] *= channelSize;
481               }
482 
483        for (i = 0; i < extra; i++)
484               ComponentStartingOrder[i] = channels[i + nchannels];
485 }
486 
487 
488 
489 //  On planar configurations, the distance is the stride added to any non-negative
490 static
491 void ComputeIncrementsForPlanar(cmsUInt32Number Format,
492                                 cmsUInt32Number BytesPerPlane,
493                                 cmsUInt32Number ComponentStartingOrder[],
494                                 cmsUInt32Number ComponentPointerIncrements[])
495 {
496        cmsUInt32Number channels[cmsMAXCHANNELS];
497        cmsUInt32Number extra = T_EXTRA(Format);
498        cmsUInt32Number nchannels = T_CHANNELS(Format);
499        cmsUInt32Number total_chans = nchannels + extra;
500        cmsUInt32Number i;
501        cmsUInt32Number channelSize = trueBytesSize(Format);
502 
503        // Sanity check
504        if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS)
505            return;
506 
507        memset(channels, 0, sizeof(channels));
508 
509        // Separation is independent of starting point and only depends on channel size
510        for (i = 0; i < extra; i++)
511               ComponentPointerIncrements[i] = channelSize;
512 
513        // Handle do swap
514        for (i = 0; i < total_chans; i++)
515        {
516               if (T_DOSWAP(Format)) {
517                      channels[i] = total_chans - i - 1;
518               }
519               else {
520                      channels[i] = i;
521               }
522        }
523 
524        // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012
525        if (T_SWAPFIRST(Format) && total_chans > 0) {
526 
527               cmsUInt32Number tmp = channels[0];
528               for (i = 0; i < total_chans - 1; i++)
529                      channels[i] = channels[i + 1];
530 
531               channels[total_chans - 1] = tmp;
532        }
533 
534        // Handle size
535        for (i = 0; i < total_chans; i++) {
536               channels[i] *= BytesPerPlane;
537        }
538 
539        for (i = 0; i < extra; i++)
540               ComponentStartingOrder[i] = channels[i + nchannels];
541 }
542 
543 
544 
545 // Dispatcher por chunky and planar RGB
546 static
547 void  ComputeComponentIncrements(cmsUInt32Number Format,
548                                  cmsUInt32Number BytesPerPlane,
549                                  cmsUInt32Number ComponentStartingOrder[],
550                                  cmsUInt32Number ComponentPointerIncrements[])
551 {
552        if (T_PLANAR(Format)) {
553 
554               ComputeIncrementsForPlanar(Format,  BytesPerPlane, ComponentStartingOrder, ComponentPointerIncrements);
555        }
556        else {
557               ComputeIncrementsForChunky(Format,  ComponentStartingOrder, ComponentPointerIncrements);
558        }
559 
560 }
561 
562 
563 
564 // Handles extra channels copying alpha if requested by the flags
565 void _cmsHandleExtraChannels(_cmsTRANSFORM* p, const void* in,
566                                                void* out,
567                                                cmsUInt32Number PixelsPerLine,
568                                                cmsUInt32Number LineCount,
569                                                const cmsStride* Stride)
570 {
571     cmsUInt32Number i, j, k;
572     cmsUInt32Number nExtra;
573     cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS];
574     cmsUInt32Number SourceIncrements[cmsMAXCHANNELS];
575     cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS];
576     cmsUInt32Number DestIncrements[cmsMAXCHANNELS];
577 
578     cmsFormatterAlphaFn copyValueFn;
579 
580     // Make sure we need some copy
581     if (!(p->dwOriginalFlags & cmsFLAGS_COPY_ALPHA))
582         return;
583 
584     // Exit early if in-place color-management is occurring - no need to copy extra channels to themselves.
585     if (p->InputFormat == p->OutputFormat && in == out)
586         return;
587 
588     // Make sure we have same number of alpha channels. If not, just return as this should be checked at transform creation time.
589     nExtra = T_EXTRA(p->InputFormat);
590     if (nExtra != T_EXTRA(p->OutputFormat))
591         return;
592 
593     // Anything to do?
594     if (nExtra == 0)
595         return;
596 
597     // Compute the increments
598     ComputeComponentIncrements(p->InputFormat, Stride->BytesPerPlaneIn, SourceStartingOrder, SourceIncrements);
599     ComputeComponentIncrements(p->OutputFormat, Stride->BytesPerPlaneOut, DestStartingOrder, DestIncrements);
600 
601     // Check for conversions 8, 16, half, float, dbl
602     copyValueFn = _cmsGetFormatterAlpha(p->ContextID, p->InputFormat, p->OutputFormat);
603     if (copyValueFn == NULL)
604         return;
605 
606     if (nExtra == 1) { // Optimized routine for copying a single extra channel quickly
607 
608         cmsUInt8Number* SourcePtr;
609         cmsUInt8Number* DestPtr;
610 
611         cmsUInt32Number SourceStrideIncrement = 0;
612         cmsUInt32Number DestStrideIncrement = 0;
613 
614         // The loop itself
615         for (i = 0; i < LineCount; i++) {
616 
617             // Prepare pointers for the loop
618             SourcePtr = (cmsUInt8Number*)in + SourceStartingOrder[0] + SourceStrideIncrement;
619             DestPtr = (cmsUInt8Number*)out + DestStartingOrder[0] + DestStrideIncrement;
620 
621             for (j = 0; j < PixelsPerLine; j++) {
622 
623                 copyValueFn(DestPtr, SourcePtr);
624 
625                 SourcePtr += SourceIncrements[0];
626                 DestPtr += DestIncrements[0];
627             }
628 
629             SourceStrideIncrement += Stride->BytesPerLineIn;
630             DestStrideIncrement += Stride->BytesPerLineOut;
631         }
632 
633     }
634     else { // General case with more than one extra channel
635 
636         cmsUInt8Number* SourcePtr[cmsMAXCHANNELS];
637         cmsUInt8Number* DestPtr[cmsMAXCHANNELS];
638 
639         cmsUInt32Number SourceStrideIncrements[cmsMAXCHANNELS];
640         cmsUInt32Number DestStrideIncrements[cmsMAXCHANNELS];
641 
642         memset(SourceStrideIncrements, 0, sizeof(SourceStrideIncrements));
643         memset(DestStrideIncrements, 0, sizeof(DestStrideIncrements));
644 
645         // The loop itself
646         for (i = 0; i < LineCount; i++) {
647 
648             // Prepare pointers for the loop
649             for (j = 0; j < nExtra; j++) {
650 
651                 SourcePtr[j] = (cmsUInt8Number*)in + SourceStartingOrder[j] + SourceStrideIncrements[j];
652                 DestPtr[j] = (cmsUInt8Number*)out + DestStartingOrder[j] + DestStrideIncrements[j];
653             }
654 
655             for (j = 0; j < PixelsPerLine; j++) {
656 
657                 for (k = 0; k < nExtra; k++) {
658 
659                     copyValueFn(DestPtr[k], SourcePtr[k]);
660 
661                     SourcePtr[k] += SourceIncrements[k];
662                     DestPtr[k] += DestIncrements[k];
663                 }
664             }
665 
666             for (j = 0; j < nExtra; j++) {
667 
668                 SourceStrideIncrements[j] += Stride->BytesPerLineIn;
669                 DestStrideIncrements[j] += Stride->BytesPerLineOut;
670             }
671         }
672     }
673 }
674 
675