LCOV - code coverage report
Current view: top level - alg - gdaldither.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 270 311 86.8 %
Date: 2026-02-07 18:19:04 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CIETMap Phase 2
       4             :  * Purpose:  Convert RGB (24bit) to a pseudo-colored approximation using
       5             :  *           Floyd-Steinberg dithering (error diffusion).
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2001, Frank Warmerdam
      10             :  * Copyright (c) 2007, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ******************************************************************************
      14             :  *
      15             :  * Notes:
      16             :  *
      17             :  * [1] Floyd-Steinberg dither:
      18             :  *  I should point out that the actual fractions we used were, assuming
      19             :  *  you are at X, moving left to right:
      20             :  *
      21             :  *                    X     7/16
      22             :  *             3/16   5/16  1/16
      23             :  *
      24             :  *  Note that the error goes to four neighbors, not three.  I think this
      25             :  *  will probably do better (at least for black and white) than the
      26             :  *  3/8-3/8-1/4 distribution, at the cost of greater processing.  I have
      27             :  *  seen the 3/8-3/8-1/4 distribution described as "our" algorithm before,
      28             :  *  but I have no idea who the credit really belongs to.
      29             :  *  --
      30             :  *                                          Lou Steinberg
      31             :  */
      32             : 
      33             : #include "cpl_port.h"
      34             : #include "gdal_alg.h"
      35             : #include "gdal_alg_priv.h"
      36             : 
      37             : #include <cmath>
      38             : #include <cstdlib>
      39             : #include <cstring>
      40             : #include <algorithm>
      41             : 
      42             : #include "cpl_conv.h"
      43             : #include "cpl_error.h"
      44             : #include "cpl_progress.h"
      45             : #include "cpl_vsi.h"
      46             : #include "gdal.h"
      47             : #include "gdal_priv.h"
      48             : 
      49             : #ifdef USE_NEON_OPTIMIZATIONS
      50             : #define USE_SSE2
      51             : #include "include_sse2neon.h"
      52             : #elif defined(__x86_64) || defined(_M_X64)
      53             : #define USE_SSE2
      54             : #include <emmintrin.h>
      55             : #endif
      56             : 
      57             : #ifdef USE_SSE2
      58             : 
      59             : #define CAST_PCT(x) reinterpret_cast<GByte *>(x)
      60             : #define ALIGN_INT_ARRAY_ON_16_BYTE(x)                                          \
      61             :     (((reinterpret_cast<GUIntptr_t>(x) % 16) != 0)                             \
      62             :          ? reinterpret_cast<int *>(reinterpret_cast<GByte *>(x) + 16 -         \
      63             :                                    (reinterpret_cast<GUIntptr_t>(x) % 16))     \
      64             :          : (x))
      65             : #else
      66             : #define CAST_PCT(x) x
      67             : #endif
      68             : 
      69             : #ifndef MAKE_COLOR_CODE_defined
      70             : #define MAKE_COLOR_CODE_defined
      71             : 
      72     1345150 : static int MAKE_COLOR_CODE(int r, int g, int b)
      73             : {
      74     1345150 :     return r | (g << 8) | (b << 16);
      75             : }
      76             : #endif
      77             : 
      78             : static void FindNearestColor(int nColors, const int *panPCT,
      79             :                              GByte *pabyColorMap, int nCLevels, int nDstNoData);
      80             : static int FindNearestColor(int nColors, const int *panPCT, int nRedValue,
      81             :                             int nGreenValue, int nBlueValue, int nDstNoData);
      82             : 
      83             : // Structure for a hashmap from a color code to a color index of the
      84             : // color table.
      85             : 
      86             : // NOTE: if changing the size of this structure, edit
      87             : // MEDIAN_CUT_AND_DITHER_BUFFER_SIZE_65536 in gdal_alg_priv.h and take
      88             : // into account HashHistogram in gdalmediancut.cpp.
      89             : typedef struct
      90             : {
      91             :     GUInt32 nColorCode;
      92             :     GUInt32 nColorCode2;
      93             :     GUInt32 nColorCode3;
      94             :     GByte nIndex;
      95             :     GByte nIndex2;
      96             :     GByte nIndex3;
      97             :     GByte nPadding;
      98             : } ColorIndex;
      99             : 
     100             : /************************************************************************/
     101             : /*                         GDALDitherRGB2PCT()                          */
     102             : /************************************************************************/
     103             : 
     104             : #ifndef IsColorCodeSet_defined
     105             : #define IsColorCodeSet_defined
     106             : 
     107      146386 : static inline bool IsColorCodeSet(GUInt32 nColorCode)
     108             : {
     109      146386 :     return (nColorCode >> 31) == 0;
     110             : }
     111             : #endif
     112             : 
     113             : /**
     114             :  * 24bit to 8bit conversion with dithering.
     115             :  *
     116             :  * This functions utilizes Floyd-Steinberg dithering in the process of
     117             :  * converting a 24bit RGB image into a pseudocolored 8bit image using a
     118             :  * provided color table.
     119             :  *
     120             :  * The red, green and blue input bands do not necessarily need to come
     121             :  * from the same file, but they must be the same width and height.  They will
     122             :  * be clipped to 8bit during reading, so non-eight bit bands are generally
     123             :  * inappropriate.  Likewise the hTarget band will be written with 8bit values
     124             :  * and must match the width and height of the source bands.
     125             :  *
     126             :  * The color table cannot have more than 256 entries.
     127             :  *
     128             :  * Since GDAL 3.13, source nodata values or mask band will be taken into account
     129             :  * to determine which pixels are valid, provided that a destination nodata value
     130             :  * is set to hTarget.
     131             :  *
     132             :  * @param hRed Red input band.
     133             :  * @param hGreen Green input band.
     134             :  * @param hBlue Blue input band.
     135             :  * @param hTarget Output band.
     136             :  * @param hColorTable the color table to use with the output band.
     137             :  * @param pfnProgress callback for reporting algorithm progress matching the
     138             :  * GDALProgressFunc() semantics.  May be NULL.
     139             :  * @param pProgressArg callback argument passed to pfnProgress.
     140             :  *
     141             :  * @return CE_None on success or CE_Failure if an error occurs.
     142             :  */
     143             : 
     144           7 : int CPL_STDCALL GDALDitherRGB2PCT(GDALRasterBandH hRed, GDALRasterBandH hGreen,
     145             :                                   GDALRasterBandH hBlue,
     146             :                                   GDALRasterBandH hTarget,
     147             :                                   GDALColorTableH hColorTable,
     148             :                                   GDALProgressFunc pfnProgress,
     149             :                                   void *pProgressArg)
     150             : 
     151             : {
     152           7 :     return GDALDitherRGB2PCTInternal(hRed, hGreen, hBlue, hTarget, hColorTable,
     153             :                                      5, nullptr, TRUE, pfnProgress,
     154           7 :                                      pProgressArg);
     155             : }
     156             : 
     157          32 : int GDALDitherRGB2PCTInternal(
     158             :     GDALRasterBandH hRed, GDALRasterBandH hGreen, GDALRasterBandH hBlue,
     159             :     GDALRasterBandH hTarget, GDALColorTableH hColorTable, int nBits,
     160             :     // NULL or at least 256 * 256 * 256 * sizeof(GInt16) bytes.
     161             :     GInt16 *pasDynamicColorMap, int bDither, GDALProgressFunc pfnProgress,
     162             :     void *pProgressArg)
     163             : {
     164          32 :     VALIDATE_POINTER1(hRed, "GDALDitherRGB2PCT", CE_Failure);
     165          32 :     VALIDATE_POINTER1(hGreen, "GDALDitherRGB2PCT", CE_Failure);
     166          32 :     VALIDATE_POINTER1(hBlue, "GDALDitherRGB2PCT", CE_Failure);
     167          32 :     VALIDATE_POINTER1(hTarget, "GDALDitherRGB2PCT", CE_Failure);
     168          32 :     VALIDATE_POINTER1(hColorTable, "GDALDitherRGB2PCT", CE_Failure);
     169             : 
     170             :     /* -------------------------------------------------------------------- */
     171             :     /*      Validate parameters.                                            */
     172             :     /* -------------------------------------------------------------------- */
     173          32 :     const int nXSize = GDALGetRasterBandXSize(hRed);
     174          32 :     const int nYSize = GDALGetRasterBandYSize(hRed);
     175             : 
     176          32 :     if (GDALGetRasterBandXSize(hGreen) != nXSize ||
     177          32 :         GDALGetRasterBandYSize(hGreen) != nYSize ||
     178          96 :         GDALGetRasterBandXSize(hBlue) != nXSize ||
     179          32 :         GDALGetRasterBandYSize(hBlue) != nYSize)
     180             :     {
     181           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     182             :                  "Green or blue band doesn't match size of red band.");
     183             : 
     184           0 :         return CE_Failure;
     185             :     }
     186             : 
     187          64 :     if (GDALGetRasterBandXSize(hTarget) != nXSize ||
     188          32 :         GDALGetRasterBandYSize(hTarget) != nYSize)
     189             :     {
     190           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     191             :                  "GDALDitherRGB2PCT(): "
     192             :                  "Target band doesn't match size of source bands.");
     193             : 
     194           0 :         return CE_Failure;
     195             :     }
     196             : 
     197          32 :     if (pfnProgress == nullptr)
     198          23 :         pfnProgress = GDALDummyProgress;
     199             : 
     200          32 :     int nSrcNoData = -1;
     201          32 :     GDALRasterBandH hMaskBand = nullptr;
     202          32 :     GByte byDstNoData = 0;
     203          32 :     int nDstNoData = -1;
     204          32 :     int bDstHasNoData = FALSE;
     205             :     const double dfDstNoData =
     206          32 :         GDALGetRasterNoDataValue(hTarget, &bDstHasNoData);
     207          32 :     if (bDstHasNoData && dfDstNoData >= 0 && dfDstNoData <= 255 &&
     208           3 :         std::round(dfDstNoData) == dfDstNoData)
     209             :     {
     210           3 :         byDstNoData = static_cast<GByte>(dfDstNoData);
     211           3 :         nDstNoData = byDstNoData;
     212             : 
     213           3 :         int bSrcHasNoDataR = FALSE;
     214             :         const double dfSrcNoDataR =
     215           3 :             GDALGetRasterNoDataValue(hRed, &bSrcHasNoDataR);
     216           3 :         int bSrcHasNoDataG = FALSE;
     217             :         const double dfSrcNoDataG =
     218           3 :             GDALGetRasterNoDataValue(hGreen, &bSrcHasNoDataG);
     219           3 :         int bSrcHasNoDataB = FALSE;
     220             :         const double dfSrcNoDataB =
     221           3 :             GDALGetRasterNoDataValue(hBlue, &bSrcHasNoDataB);
     222           3 :         if (bSrcHasNoDataR && bSrcHasNoDataG && bSrcHasNoDataB &&
     223           2 :             dfSrcNoDataR == dfSrcNoDataG && dfSrcNoDataR == dfSrcNoDataB &&
     224           2 :             dfSrcNoDataR >= 0 && dfSrcNoDataR <= 255 &&
     225           2 :             std::round(dfSrcNoDataR) == dfSrcNoDataR)
     226             :         {
     227           2 :             nSrcNoData = static_cast<int>(dfSrcNoDataR);
     228             :         }
     229             :         else
     230             :         {
     231           1 :             const int nMaskFlags = GDALGetMaskFlags(hRed);
     232           1 :             if ((nMaskFlags & GMF_PER_DATASET))
     233           1 :                 hMaskBand = GDALGetMaskBand(hRed);
     234             :         }
     235             :     }
     236             : 
     237             :     /* -------------------------------------------------------------------- */
     238             :     /*      Setup more direct colormap.                                     */
     239             :     /* -------------------------------------------------------------------- */
     240             :     int iColor;
     241             : #ifdef USE_SSE2
     242             :     int anPCTUnaligned[256 + 4];  // 4 for alignment on 16-byte boundary.
     243          32 :     int *anPCT = ALIGN_INT_ARRAY_ON_16_BYTE(anPCTUnaligned);
     244             : #else
     245             :     int anPCT[256 * 4] = {};
     246             : #endif
     247          32 :     const int nColors = GDALGetColorEntryCount(hColorTable);
     248             : 
     249          32 :     if (nColors == 0)
     250             :     {
     251           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     252             :                  "GDALDitherRGB2PCT(): "
     253             :                  "Color table must not be empty.");
     254             : 
     255           0 :         return CE_Failure;
     256             :     }
     257          32 :     else if (nColors > 256)
     258             :     {
     259           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     260             :                  "GDALDitherRGB2PCT(): "
     261             :                  "Color table cannot have more than 256 entries.");
     262             : 
     263           0 :         return CE_Failure;
     264             :     }
     265             : 
     266          32 :     iColor = 0;
     267        7417 :     do
     268             :     {
     269             :         GDALColorEntry sEntry;
     270             : 
     271        7449 :         GDALGetColorEntryAsRGB(hColorTable, iColor, &sEntry);
     272        7449 :         CAST_PCT(anPCT)[4 * iColor + 0] = static_cast<GByte>(sEntry.c1);
     273        7449 :         CAST_PCT(anPCT)[4 * iColor + 1] = static_cast<GByte>(sEntry.c2);
     274        7449 :         CAST_PCT(anPCT)[4 * iColor + 2] = static_cast<GByte>(sEntry.c3);
     275        7449 :         CAST_PCT(anPCT)[4 * iColor + 3] = 0;
     276             : 
     277        7449 :         iColor++;
     278        7449 :     } while (iColor < nColors);
     279             : 
     280             : #ifdef USE_SSE2
     281             :     // Pad to multiple of 8 colors.
     282          32 :     const int nColorsMod8 = nColors % 8;
     283          32 :     if (nColorsMod8)
     284             :     {
     285           1 :         int iDest = nColors;
     286           8 :         for (iColor = 0; iColor < 8 - nColorsMod8 && iDest < 256;
     287             :              iColor++, iDest++)
     288             :         {
     289           7 :             anPCT[iDest] = anPCT[nColors - 1];
     290             :         }
     291             :     }
     292             : #endif
     293             : 
     294             :     /* -------------------------------------------------------------------- */
     295             :     /*      Setup various variables.                                        */
     296             :     /* -------------------------------------------------------------------- */
     297          32 :     const int nCLevels = 1 << nBits;
     298          32 :     const int nCLevelsCube = nCLevels * nCLevels * nCLevels;
     299          32 :     ColorIndex *psColorIndexMap = nullptr;
     300             : 
     301          32 :     GByte *pabyRed = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
     302          32 :     GByte *pabyGreen = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
     303          32 :     GByte *pabyBlue = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
     304          32 :     std::unique_ptr<GByte, VSIFreeReleaser> pabyMask;
     305          32 :     if (hMaskBand)
     306           1 :         pabyMask.reset(static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize)));
     307             : 
     308          32 :     GByte *pabyIndex = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
     309             : 
     310             :     int *panError =
     311          32 :         static_cast<int *>(VSI_CALLOC_VERBOSE(sizeof(int), (nXSize + 2) * 3));
     312             : 
     313          32 :     if (pabyRed == nullptr || pabyGreen == nullptr || pabyBlue == nullptr ||
     314          64 :         pabyIndex == nullptr || panError == nullptr || (hMaskBand && !pabyMask))
     315             :     {
     316           0 :         CPLFree(pabyRed);
     317           0 :         CPLFree(pabyGreen);
     318           0 :         CPLFree(pabyBlue);
     319           0 :         CPLFree(pabyIndex);
     320           0 :         CPLFree(panError);
     321             : 
     322           0 :         return CE_Failure;
     323             :     }
     324             : 
     325          32 :     GByte *pabyColorMap = nullptr;
     326          32 :     if (pasDynamicColorMap == nullptr)
     327             :     {
     328             :         /* --------------------------------------------------------------------
     329             :          */
     330             :         /*      Build a 24bit to 8 bit color mapping. */
     331             :         /* --------------------------------------------------------------------
     332             :          */
     333             : 
     334             :         pabyColorMap = static_cast<GByte *>(
     335          22 :             VSI_MALLOC_VERBOSE(nCLevelsCube * sizeof(GByte)));
     336          22 :         if (pabyColorMap == nullptr)
     337             :         {
     338           0 :             CPLFree(pabyRed);
     339           0 :             CPLFree(pabyGreen);
     340           0 :             CPLFree(pabyBlue);
     341           0 :             CPLFree(pabyIndex);
     342           0 :             CPLFree(panError);
     343           0 :             CPLFree(pabyColorMap);
     344             : 
     345           0 :             return CE_Failure;
     346             :         }
     347             : 
     348          22 :         FindNearestColor(nColors, anPCT, pabyColorMap, nCLevels, nDstNoData);
     349             :     }
     350             :     else
     351             :     {
     352          10 :         pabyColorMap = nullptr;
     353          10 :         if (nBits == 8 && static_cast<GIntBig>(nXSize) * nYSize <= 65536)
     354             :         {
     355             :             // If the image is small enough, then the number of colors
     356             :             // will be limited and using a hashmap, rather than a full table
     357             :             // will be more efficient.
     358           9 :             psColorIndexMap =
     359             :                 reinterpret_cast<ColorIndex *>(pasDynamicColorMap);
     360           9 :             memset(psColorIndexMap, 0xFF, sizeof(ColorIndex) * PRIME_FOR_65536);
     361             :         }
     362             :         else
     363             :         {
     364           1 :             memset(pasDynamicColorMap, 0xFF, 256 * 256 * 256 * sizeof(GInt16));
     365             :         }
     366             :     }
     367             : 
     368             :     /* ==================================================================== */
     369             :     /*      Loop over all scanlines of data to process.                     */
     370             :     /* ==================================================================== */
     371          32 :     CPLErr err = CE_None;
     372             : 
     373        5206 :     for (int iScanline = 0; iScanline < nYSize; iScanline++)
     374             :     {
     375             :         /* --------------------------------------------------------------------
     376             :          */
     377             :         /*      Report progress */
     378             :         /* --------------------------------------------------------------------
     379             :          */
     380        5174 :         if (!pfnProgress(iScanline / static_cast<double>(nYSize), nullptr,
     381             :                          pProgressArg))
     382             :         {
     383           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User Terminated");
     384           0 :             CPLFree(pabyRed);
     385           0 :             CPLFree(pabyGreen);
     386           0 :             CPLFree(pabyBlue);
     387           0 :             CPLFree(pabyIndex);
     388           0 :             CPLFree(panError);
     389           0 :             CPLFree(pabyColorMap);
     390             : 
     391           0 :             return CE_Failure;
     392             :         }
     393             : 
     394             :         /* --------------------------------------------------------------------
     395             :          */
     396             :         /*      Read source data. */
     397             :         /* --------------------------------------------------------------------
     398             :          */
     399        5174 :         CPLErr err1 = GDALRasterIO(hRed, GF_Read, 0, iScanline, nXSize, 1,
     400             :                                    pabyRed, nXSize, 1, GDT_UInt8, 0, 0);
     401        5174 :         if (err1 == CE_None)
     402        5174 :             err1 = GDALRasterIO(hGreen, GF_Read, 0, iScanline, nXSize, 1,
     403             :                                 pabyGreen, nXSize, 1, GDT_UInt8, 0, 0);
     404        5174 :         if (err1 == CE_None)
     405        5174 :             err1 = GDALRasterIO(hBlue, GF_Read, 0, iScanline, nXSize, 1,
     406             :                                 pabyBlue, nXSize, 1, GDT_UInt8, 0, 0);
     407        5174 :         if (err1 == CE_None && hMaskBand)
     408         150 :             err1 = GDALRasterIO(hMaskBand, GF_Read, 0, iScanline, nXSize, 1,
     409         150 :                                 pabyMask.get(), nXSize, 1, GDT_UInt8, 0, 0);
     410        5174 :         if (err1 != CE_None)
     411             :         {
     412           0 :             CPLFree(pabyRed);
     413           0 :             CPLFree(pabyGreen);
     414           0 :             CPLFree(pabyBlue);
     415           0 :             CPLFree(pabyIndex);
     416           0 :             CPLFree(panError);
     417           0 :             CPLFree(pabyColorMap);
     418             : 
     419           0 :             return err1;
     420             :         }
     421             : 
     422             :         /* --------------------------------------------------------------------
     423             :          */
     424             :         /*      Apply the error from the previous line to this one. */
     425             :         /* --------------------------------------------------------------------
     426             :          */
     427        5174 :         if (bDither)
     428             :         {
     429     1236570 :             for (int i = 0; i < nXSize; i++)
     430             :             {
     431     1305440 :                 if (nDstNoData >= 0 &&
     432       72900 :                     ((pabyRed[i] == nSrcNoData && pabyGreen[i] == nSrcNoData &&
     433       72900 :                       pabyBlue[i] == nSrcNoData) ||
     434       63892 :                      (pabyMask && (pabyMask.get())[i] == 0)))
     435             :                 {
     436       45088 :                     continue;
     437             :                 }
     438             : 
     439     1187460 :                 pabyRed[i] = static_cast<GByte>(std::max(
     440     1187460 :                     0, std::min(255, (pabyRed[i] + panError[i * 3 + 0 + 3]))));
     441     1187460 :                 pabyGreen[i] = static_cast<GByte>(std::max(
     442     2374910 :                     0,
     443     1187460 :                     std::min(255, (pabyGreen[i] + panError[i * 3 + 1 + 3]))));
     444     1187460 :                 pabyBlue[i] = static_cast<GByte>(std::max(
     445     1187460 :                     0, std::min(255, (pabyBlue[i] + panError[i * 3 + 2 + 3]))));
     446             :             }
     447             : 
     448        4024 :             memset(panError, 0, sizeof(int) * (nXSize + 2) * 3);
     449             :         }
     450             : 
     451             :         /* --------------------------------------------------------------------
     452             :          */
     453             :         /*      Figure out the nearest color to the RGB value. */
     454             :         /* --------------------------------------------------------------------
     455             :          */
     456        5174 :         int nLastRedError = 0;
     457        5174 :         int nLastGreenError = 0;
     458        5174 :         int nLastBlueError = 0;
     459             : 
     460     1525220 :         for (int i = 0; i < nXSize; i++)
     461             :         {
     462     1592940 :             if (nDstNoData >= 0 &&
     463       72900 :                 ((pabyRed[i] == nSrcNoData && pabyGreen[i] == nSrcNoData &&
     464       72902 :                   pabyBlue[i] == nSrcNoData) ||
     465       63892 :                  (pabyMask && (pabyMask.get())[i] == 0)))
     466             :             {
     467       45088 :                 nLastRedError = 0;
     468       45088 :                 nLastGreenError = 0;
     469       45088 :                 nLastBlueError = 0;
     470       45088 :                 pabyIndex[i] = byDstNoData;
     471       45088 :                 continue;
     472             :             }
     473             : 
     474             :             const int nRedValue =
     475     1474960 :                 std::max(0, std::min(255, pabyRed[i] + nLastRedError));
     476             :             const int nGreenValue =
     477     1474960 :                 std::max(0, std::min(255, pabyGreen[i] + nLastGreenError));
     478             :             const int nBlueValue =
     479     1474960 :                 std::max(0, std::min(255, pabyBlue[i] + nLastBlueError));
     480             : 
     481     1474960 :             int iIndex = 0;
     482     1474960 :             int nError = 0;
     483     1474960 :             int nSixth = 0;
     484     1474960 :             if (psColorIndexMap)
     485             :             {
     486             :                 const GUInt32 nColorCode =
     487      389644 :                     MAKE_COLOR_CODE(nRedValue, nGreenValue, nBlueValue);
     488      389644 :                 GUInt32 nIdx = nColorCode % PRIME_FOR_65536;
     489             :                 while (true)
     490             :                 {
     491      389651 :                     if (psColorIndexMap[nIdx].nColorCode == nColorCode)
     492             :                     {
     493      252924 :                         iIndex = psColorIndexMap[nIdx].nIndex;
     494      252924 :                         break;
     495             :                     }
     496      136727 :                     if (!IsColorCodeSet(psColorIndexMap[nIdx].nColorCode))
     497             :                     {
     498      125973 :                         psColorIndexMap[nIdx].nColorCode = nColorCode;
     499      125973 :                         iIndex = FindNearestColor(nColors, anPCT, nRedValue,
     500             :                                                   nGreenValue, nBlueValue,
     501             :                                                   nDstNoData);
     502      125973 :                         psColorIndexMap[nIdx].nIndex =
     503             :                             static_cast<GByte>(iIndex);
     504      125973 :                         break;
     505             :                     }
     506       10754 :                     if (psColorIndexMap[nIdx].nColorCode2 == nColorCode)
     507             :                     {
     508        1476 :                         iIndex = psColorIndexMap[nIdx].nIndex2;
     509        1476 :                         break;
     510             :                     }
     511        9278 :                     if (!IsColorCodeSet(psColorIndexMap[nIdx].nColorCode2))
     512             :                     {
     513        8876 :                         psColorIndexMap[nIdx].nColorCode2 = nColorCode;
     514        8876 :                         iIndex = FindNearestColor(nColors, anPCT, nRedValue,
     515             :                                                   nGreenValue, nBlueValue,
     516             :                                                   nDstNoData);
     517        8876 :                         psColorIndexMap[nIdx].nIndex2 =
     518             :                             static_cast<GByte>(iIndex);
     519        8876 :                         break;
     520             :                     }
     521         402 :                     if (psColorIndexMap[nIdx].nColorCode3 == nColorCode)
     522             :                     {
     523          31 :                         iIndex = psColorIndexMap[nIdx].nIndex3;
     524          31 :                         break;
     525             :                     }
     526         371 :                     if (!IsColorCodeSet(psColorIndexMap[nIdx].nColorCode3))
     527             :                     {
     528         364 :                         psColorIndexMap[nIdx].nColorCode3 = nColorCode;
     529         364 :                         iIndex = FindNearestColor(nColors, anPCT, nRedValue,
     530             :                                                   nGreenValue, nBlueValue,
     531             :                                                   nDstNoData);
     532         364 :                         psColorIndexMap[nIdx].nIndex3 =
     533             :                             static_cast<GByte>(iIndex);
     534         364 :                         break;
     535             :                     }
     536             : 
     537           0 :                     do
     538             :                     {
     539           7 :                         nIdx += 257;
     540           7 :                         if (nIdx >= PRIME_FOR_65536)
     541           0 :                             nIdx -= PRIME_FOR_65536;
     542             :                     } while (
     543           7 :                         IsColorCodeSet(psColorIndexMap[nIdx].nColorCode) &&
     544           6 :                         psColorIndexMap[nIdx].nColorCode != nColorCode &&
     545           3 :                         IsColorCodeSet(psColorIndexMap[nIdx].nColorCode2) &&
     546           0 :                         psColorIndexMap[nIdx].nColorCode2 != nColorCode &&
     547          10 :                         IsColorCodeSet(psColorIndexMap[nIdx].nColorCode3) &&
     548           0 :                         psColorIndexMap[nIdx].nColorCode3 != nColorCode);
     549             :                 }
     550             :             }
     551     1085310 :             else if (pasDynamicColorMap == nullptr)
     552             :             {
     553     1005310 :                 const int iRed = nRedValue * nCLevels / 256;
     554     1005310 :                 const int iGreen = nGreenValue * nCLevels / 256;
     555     1005310 :                 const int iBlue = nBlueValue * nCLevels / 256;
     556             : 
     557     1005310 :                 iIndex = pabyColorMap[iRed + iGreen * nCLevels +
     558     1005310 :                                       iBlue * nCLevels * nCLevels];
     559             :             }
     560             :             else
     561             :             {
     562             :                 const GUInt32 nColorCode =
     563       80000 :                     MAKE_COLOR_CODE(nRedValue, nGreenValue, nBlueValue);
     564       80000 :                 GInt16 *psIndex = &pasDynamicColorMap[nColorCode];
     565       80000 :                 if (*psIndex < 0)
     566             :                 {
     567       19399 :                     *psIndex = static_cast<GInt16>(
     568       19399 :                         FindNearestColor(nColors, anPCT, nRedValue, nGreenValue,
     569             :                                          nBlueValue, nDstNoData));
     570       19399 :                     iIndex = *psIndex;
     571             :                 }
     572             :                 else
     573             :                 {
     574       60601 :                     iIndex = *psIndex;
     575             :                 }
     576             :             }
     577             : 
     578     1474960 :             pabyIndex[i] = static_cast<GByte>(iIndex);
     579     1474960 :             if (!bDither)
     580      287500 :                 continue;
     581             : 
     582             :             /* --------------------------------------------------------------------
     583             :              */
     584             :             /*      Compute Red error, and carry it on to the next error line.
     585             :              */
     586             :             /* --------------------------------------------------------------------
     587             :              */
     588     1187460 :             nError = nRedValue - CAST_PCT(anPCT)[4 * iIndex + 0];
     589     1187460 :             nSixth = nError / 6;
     590             : 
     591     1187460 :             panError[i * 3] += nSixth;
     592     1187460 :             panError[i * 3 + 6] = nSixth;
     593     1187460 :             panError[i * 3 + 3] += nError - 5 * nSixth;
     594             : 
     595     1187460 :             nLastRedError = 2 * nSixth;
     596             : 
     597             :             /* --------------------------------------------------------------------
     598             :              */
     599             :             /*      Compute Green error, and carry it on to the next error line.
     600             :              */
     601             :             /* --------------------------------------------------------------------
     602             :              */
     603     1187460 :             nError = nGreenValue - CAST_PCT(anPCT)[4 * iIndex + 1];
     604     1187460 :             nSixth = nError / 6;
     605             : 
     606     1187460 :             panError[i * 3 + 1] += nSixth;
     607     1187460 :             panError[i * 3 + 6 + 1] = nSixth;
     608     1187460 :             panError[i * 3 + 3 + 1] += nError - 5 * nSixth;
     609             : 
     610     1187460 :             nLastGreenError = 2 * nSixth;
     611             : 
     612             :             /* --------------------------------------------------------------------
     613             :              */
     614             :             /*      Compute Blue error, and carry it on to the next error line.
     615             :              */
     616             :             /* --------------------------------------------------------------------
     617             :              */
     618     1187460 :             nError = nBlueValue - CAST_PCT(anPCT)[4 * iIndex + 2];
     619     1187460 :             nSixth = nError / 6;
     620             : 
     621     1187460 :             panError[i * 3 + 2] += nSixth;
     622     1187460 :             panError[i * 3 + 6 + 2] = nSixth;
     623     1187460 :             panError[i * 3 + 3 + 2] += nError - 5 * nSixth;
     624             : 
     625     1187460 :             nLastBlueError = 2 * nSixth;
     626             :         }
     627             : 
     628             :         /* --------------------------------------------------------------------
     629             :          */
     630             :         /*      Write results. */
     631             :         /* --------------------------------------------------------------------
     632             :          */
     633        5174 :         err = GDALRasterIO(hTarget, GF_Write, 0, iScanline, nXSize, 1,
     634             :                            pabyIndex, nXSize, 1, GDT_UInt8, 0, 0);
     635        5174 :         if (err != CE_None)
     636           0 :             break;
     637             :     }
     638             : 
     639          32 :     pfnProgress(1.0, nullptr, pProgressArg);
     640             : 
     641             :     /* -------------------------------------------------------------------- */
     642             :     /*      Cleanup                                                         */
     643             :     /* -------------------------------------------------------------------- */
     644          32 :     CPLFree(pabyRed);
     645          32 :     CPLFree(pabyGreen);
     646          32 :     CPLFree(pabyBlue);
     647          32 :     CPLFree(pabyIndex);
     648          32 :     CPLFree(panError);
     649          32 :     CPLFree(pabyColorMap);
     650             : 
     651          32 :     return err;
     652             : }
     653             : 
     654      875508 : static int FindNearestColor(int nColors, const int *panPCT, int nRedValue,
     655             :                             int nGreenValue, int nBlueValue, int nDstNoData)
     656             : 
     657             : {
     658             : #ifdef USE_SSE2
     659      875508 :     int nBestDist = 768;
     660      875508 :     int nBestIndex = 0;
     661             : 
     662      875508 :     int anDistanceUnaligned[16 + 4] =
     663             :         {};  // 4 for alignment on 16-byte boundary.
     664      875508 :     int *anDistance = ALIGN_INT_ARRAY_ON_16_BYTE(anDistanceUnaligned);
     665             : 
     666      875508 :     const __m128i ff = _mm_set1_epi32(0xFFFFFFFF);
     667      875508 :     const __m128i mask_low = _mm_srli_epi64(ff, 32);
     668      875508 :     const __m128i mask_high = _mm_slli_epi64(ff, 32);
     669             : 
     670             :     const unsigned int nColorVal =
     671      875508 :         MAKE_COLOR_CODE(nRedValue, nGreenValue, nBlueValue);
     672     1751020 :     const __m128i thisColor = _mm_set1_epi32(nColorVal);
     673      875508 :     const __m128i thisColor_low = _mm_srli_epi64(thisColor, 32);
     674      875508 :     const __m128i thisColor_high = _mm_slli_epi64(thisColor, 32);
     675             : 
     676    26892900 :     for (int iColor = 0; iColor < nColors; iColor += 8)
     677             :     {
     678             :         const __m128i pctColor =
     679    26017400 :             _mm_load_si128(reinterpret_cast<const __m128i *>(&panPCT[iColor]));
     680    26017400 :         const __m128i pctColor2 = _mm_load_si128(
     681    26017400 :             reinterpret_cast<const __m128i *>(&panPCT[iColor + 4]));
     682             : 
     683    52034800 :         _mm_store_si128(
     684             :             reinterpret_cast<__m128i *>(anDistance),
     685             :             _mm_sad_epu8(_mm_and_si128(pctColor, mask_low), thisColor_low));
     686    26017400 :         _mm_store_si128(
     687    26017400 :             reinterpret_cast<__m128i *>(anDistance + 4),
     688             :             _mm_sad_epu8(_mm_and_si128(pctColor, mask_high), thisColor_high));
     689    26017400 :         _mm_store_si128(
     690    26017400 :             reinterpret_cast<__m128i *>(anDistance + 8),
     691             :             _mm_sad_epu8(_mm_and_si128(pctColor2, mask_low), thisColor_low));
     692    26017400 :         _mm_store_si128(
     693    26017400 :             reinterpret_cast<__m128i *>(anDistance + 12),
     694             :             _mm_sad_epu8(_mm_and_si128(pctColor2, mask_high), thisColor_high));
     695             : 
     696    26017400 :         if (anDistance[0] < nBestDist && iColor != nDstNoData)
     697             :         {
     698     1119370 :             nBestIndex = iColor;
     699     1119370 :             nBestDist = anDistance[0];
     700             :         }
     701    26017400 :         if (anDistance[4] < nBestDist && iColor + 1 != nDstNoData)
     702             :         {
     703      800944 :             nBestIndex = iColor + 1;
     704      800944 :             nBestDist = anDistance[4];
     705             :         }
     706    26017400 :         if (anDistance[2] < nBestDist && iColor + 2 != nDstNoData)
     707             :         {
     708      517890 :             nBestIndex = iColor + 2;
     709      517890 :             nBestDist = anDistance[2];
     710             :         }
     711    26017400 :         if (anDistance[6] < nBestDist && iColor + 3 != nDstNoData)
     712             :         {
     713      494934 :             nBestIndex = iColor + 3;
     714      494934 :             nBestDist = anDistance[6];
     715             :         }
     716    26017400 :         if (anDistance[8 + 0] < nBestDist && iColor + 4 != nDstNoData)
     717             :         {
     718      363062 :             nBestIndex = iColor + 4;
     719      363062 :             nBestDist = anDistance[8 + 0];
     720             :         }
     721    26017400 :         if (anDistance[8 + 4] < nBestDist && iColor + 5 != nDstNoData)
     722             :         {
     723      318223 :             nBestIndex = iColor + 4 + 1;
     724      318223 :             nBestDist = anDistance[8 + 4];
     725             :         }
     726    26017400 :         if (anDistance[8 + 2] < nBestDist && iColor + 6 != nDstNoData)
     727             :         {
     728      312654 :             nBestIndex = iColor + 4 + 2;
     729      312654 :             nBestDist = anDistance[8 + 2];
     730             :         }
     731    26017400 :         if (anDistance[8 + 6] < nBestDist && iColor + 7 != nDstNoData)
     732             :         {
     733      393373 :             nBestIndex = iColor + 4 + 3;
     734      393373 :             nBestDist = anDistance[8 + 6];
     735             :         }
     736             :     }
     737      875508 :     return nBestIndex;
     738             : #else
     739             :     int nBestDist = 768;
     740             :     int nBestIndex = 0;
     741             : 
     742             :     for (int iColor = 0; iColor < nColors; iColor++)
     743             :     {
     744             :         if (iColor != nDstNoData)
     745             :         {
     746             :             const int nThisDist =
     747             :                 std::abs(nRedValue - panPCT[4 * iColor + 0]) +
     748             :                 std::abs(nGreenValue - panPCT[4 * iColor + 1]) +
     749             :                 std::abs(nBlueValue - panPCT[4 * iColor + 2]);
     750             : 
     751             :             if (nThisDist < nBestDist)
     752             :             {
     753             :                 nBestIndex = iColor;
     754             :                 nBestDist = nThisDist;
     755             :             }
     756             :         }
     757             :     }
     758             :     return nBestIndex;
     759             : #endif
     760             : }
     761             : 
     762             : /************************************************************************/
     763             : /*                          FindNearestColor()                          */
     764             : /*                                                                      */
     765             : /*      Finear near PCT color for any RGB color.                        */
     766             : /************************************************************************/
     767             : 
     768          22 : static void FindNearestColor(int nColors, const int *panPCT,
     769             :                              GByte *pabyColorMap, int nCLevels, int nDstNoData)
     770             : 
     771             : {
     772             :     /* -------------------------------------------------------------------- */
     773             :     /*  Loop over all the cells in the high density cube.                   */
     774             :     /* -------------------------------------------------------------------- */
     775         726 :     for (int iBlue = 0; iBlue < nCLevels; iBlue++)
     776             :     {
     777       23232 :         for (int iGreen = 0; iGreen < nCLevels; iGreen++)
     778             :         {
     779      743424 :             for (int iRed = 0; iRed < nCLevels; iRed++)
     780             :             {
     781      720896 :                 const int nRedValue = (iRed * 255) / (nCLevels - 1);
     782      720896 :                 const int nGreenValue = (iGreen * 255) / (nCLevels - 1);
     783      720896 :                 const int nBlueValue = (iBlue * 255) / (nCLevels - 1);
     784             : 
     785             :                 const int nBestIndex =
     786      720896 :                     FindNearestColor(nColors, panPCT, nRedValue, nGreenValue,
     787             :                                      nBlueValue, nDstNoData);
     788      720896 :                 pabyColorMap[iRed + iGreen * nCLevels +
     789      720896 :                              iBlue * nCLevels * nCLevels] =
     790             :                     static_cast<GByte>(nBestIndex);
     791             :             }
     792             :         }
     793             :     }
     794          22 : }

Generated by: LCOV version 1.14