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-2020 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 = 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 = (*(cmsUInt8Number*)src) / 255.0f;
 121 }
 122 
 123 static
 124 void from8toDBL(void* dst, const void* src)
 125 {
 126        *(cmsFloat64Number*)dst = (*(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 = (*(cmsUInt16Number*)src) / 65535.0f;
 186 }
 187 
 188 static
 189 void from16SEtoDBL(void* dst, const void* src)
 190 {
 191     *(cmsFloat64Number*)dst = (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0f;
 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.0f);
 224 }
 225 
 226 static
 227 void fromFLTto16(void* dst, const void* src)
 228 {
 229     cmsFloat32Number n = *(cmsFloat32Number*)src;
 230     *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
 231 }
 232 
 233 static
 234 void fromFLTto16SE(void* dst, const void* src)
 235 {
 236     cmsFloat32Number n = *(cmsFloat32Number*)src;
 237     cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f);
 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.0f);
 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.0f);
 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.0f);
 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