LCOV - code coverage report
Current view: top level - alg - gdaldither.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 224 265 84.5 %
Date: 2025-01-18 12:42:00 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 <cstdlib>
      38             : #include <cstring>
      39             : #include <algorithm>
      40             : 
      41             : #include "cpl_conv.h"
      42             : #include "cpl_error.h"
      43             : #include "cpl_progress.h"
      44             : #include "cpl_vsi.h"
      45             : #include "gdal.h"
      46             : #include "gdal_priv.h"
      47             : 
      48             : #ifdef USE_NEON_OPTIMIZATIONS
      49             : #define USE_SSE2
      50             : #include "include_sse2neon.h"
      51             : #elif defined(__x86_64) || defined(_M_X64)
      52             : #define USE_SSE2
      53             : #include <emmintrin.h>
      54             : #endif
      55             : 
      56             : #ifdef USE_SSE2
      57             : 
      58             : #define CAST_PCT(x) reinterpret_cast<GByte *>(x)
      59             : #define ALIGN_INT_ARRAY_ON_16_BYTE(x)                                          \
      60             :     (((reinterpret_cast<GUIntptr_t>(x) % 16) != 0)                             \
      61             :          ? reinterpret_cast<int *>(reinterpret_cast<GByte *>(x) + 16 -         \
      62             :                                    (reinterpret_cast<GUIntptr_t>(x) % 16))     \
      63             :          : (x))
      64             : #else
      65             : #define CAST_PCT(x) x
      66             : #endif
      67             : 
      68             : #ifndef MAKE_COLOR_CODE_defined
      69             : #define MAKE_COLOR_CODE_defined
      70             : 
      71      820864 : static int MAKE_COLOR_CODE(int r, int g, int b)
      72             : {
      73      820864 :     return r | (g << 8) | (b << 16);
      74             : }
      75             : #endif
      76             : 
      77             : static void FindNearestColor(int nColors, int *panPCT, GByte *pabyColorMap,
      78             :                              int nCLevels);
      79             : static int FindNearestColor(int nColors, int *panPCT, int nRedValue,
      80             :                             int nGreenValue, int nBlueValue);
      81             : 
      82             : // Structure for a hashmap from a color code to a color index of the
      83             : // color table.
      84             : 
      85             : // NOTE: if changing the size of this structure, edit
      86             : // MEDIAN_CUT_AND_DITHER_BUFFER_SIZE_65536 in gdal_alg_priv.h and take
      87             : // into account HashHistogram in gdalmediancut.cpp.
      88             : typedef struct
      89             : {
      90             :     GUInt32 nColorCode;
      91             :     GUInt32 nColorCode2;
      92             :     GUInt32 nColorCode3;
      93             :     GByte nIndex;
      94             :     GByte nIndex2;
      95             :     GByte nIndex3;
      96             :     GByte nPadding;
      97             : } ColorIndex;
      98             : 
      99             : /************************************************************************/
     100             : /*                         GDALDitherRGB2PCT()                          */
     101             : /************************************************************************/
     102             : 
     103             : #ifndef IsColorCodeSet_defined
     104             : #define IsColorCodeSet_defined
     105             : 
     106      146386 : static inline bool IsColorCodeSet(GUInt32 nColorCode)
     107             : {
     108      146386 :     return (nColorCode >> 31) == 0;
     109             : }
     110             : #endif
     111             : 
     112             : /**
     113             :  * 24bit to 8bit conversion with dithering.
     114             :  *
     115             :  * This functions utilizes Floyd-Steinberg dithering in the process of
     116             :  * converting a 24bit RGB image into a pseudocolored 8bit image using a
     117             :  * provided color table.
     118             :  *
     119             :  * The red, green and blue input bands do not necessarily need to come
     120             :  * from the same file, but they must be the same width and height.  They will
     121             :  * be clipped to 8bit during reading, so non-eight bit bands are generally
     122             :  * inappropriate.  Likewise the hTarget band will be written with 8bit values
     123             :  * and must match the width and height of the source bands.
     124             :  *
     125             :  * The color table cannot have more than 256 entries.
     126             :  *
     127             :  * @param hRed Red input band.
     128             :  * @param hGreen Green input band.
     129             :  * @param hBlue Blue input band.
     130             :  * @param hTarget Output band.
     131             :  * @param hColorTable the color table to use with the output band.
     132             :  * @param pfnProgress callback for reporting algorithm progress matching the
     133             :  * GDALProgressFunc() semantics.  May be NULL.
     134             :  * @param pProgressArg callback argument passed to pfnProgress.
     135             :  *
     136             :  * @return CE_None on success or CE_Failure if an error occurs.
     137             :  */
     138             : 
     139           6 : int CPL_STDCALL GDALDitherRGB2PCT(GDALRasterBandH hRed, GDALRasterBandH hGreen,
     140             :                                   GDALRasterBandH hBlue,
     141             :                                   GDALRasterBandH hTarget,
     142             :                                   GDALColorTableH hColorTable,
     143             :                                   GDALProgressFunc pfnProgress,
     144             :                                   void *pProgressArg)
     145             : 
     146             : {
     147           6 :     return GDALDitherRGB2PCTInternal(hRed, hGreen, hBlue, hTarget, hColorTable,
     148             :                                      5, nullptr, TRUE, pfnProgress,
     149           6 :                                      pProgressArg);
     150             : }
     151             : 
     152          16 : int GDALDitherRGB2PCTInternal(
     153             :     GDALRasterBandH hRed, GDALRasterBandH hGreen, GDALRasterBandH hBlue,
     154             :     GDALRasterBandH hTarget, GDALColorTableH hColorTable, int nBits,
     155             :     // NULL or at least 256 * 256 * 256 * sizeof(GInt16) bytes.
     156             :     GInt16 *pasDynamicColorMap, int bDither, GDALProgressFunc pfnProgress,
     157             :     void *pProgressArg)
     158             : {
     159          16 :     VALIDATE_POINTER1(hRed, "GDALDitherRGB2PCT", CE_Failure);
     160          16 :     VALIDATE_POINTER1(hGreen, "GDALDitherRGB2PCT", CE_Failure);
     161          16 :     VALIDATE_POINTER1(hBlue, "GDALDitherRGB2PCT", CE_Failure);
     162          16 :     VALIDATE_POINTER1(hTarget, "GDALDitherRGB2PCT", CE_Failure);
     163          16 :     VALIDATE_POINTER1(hColorTable, "GDALDitherRGB2PCT", CE_Failure);
     164             : 
     165             :     /* -------------------------------------------------------------------- */
     166             :     /*      Validate parameters.                                            */
     167             :     /* -------------------------------------------------------------------- */
     168          16 :     const int nXSize = GDALGetRasterBandXSize(hRed);
     169          16 :     const int nYSize = GDALGetRasterBandYSize(hRed);
     170             : 
     171          16 :     if (GDALGetRasterBandXSize(hGreen) != nXSize ||
     172          16 :         GDALGetRasterBandYSize(hGreen) != nYSize ||
     173          48 :         GDALGetRasterBandXSize(hBlue) != nXSize ||
     174          16 :         GDALGetRasterBandYSize(hBlue) != nYSize)
     175             :     {
     176           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     177             :                  "Green or blue band doesn't match size of red band.");
     178             : 
     179           0 :         return CE_Failure;
     180             :     }
     181             : 
     182          32 :     if (GDALGetRasterBandXSize(hTarget) != nXSize ||
     183          16 :         GDALGetRasterBandYSize(hTarget) != nYSize)
     184             :     {
     185           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     186             :                  "GDALDitherRGB2PCT(): "
     187             :                  "Target band doesn't match size of source bands.");
     188             : 
     189           0 :         return CE_Failure;
     190             :     }
     191             : 
     192          16 :     if (pfnProgress == nullptr)
     193          11 :         pfnProgress = GDALDummyProgress;
     194             : 
     195             :     /* -------------------------------------------------------------------- */
     196             :     /*      Setup more direct colormap.                                     */
     197             :     /* -------------------------------------------------------------------- */
     198             :     int iColor;
     199             : #ifdef USE_SSE2
     200             :     int anPCTUnaligned[256 + 4];  // 4 for alignment on 16-byte boundary.
     201          16 :     int *anPCT = ALIGN_INT_ARRAY_ON_16_BYTE(anPCTUnaligned);
     202             : #else
     203             :     int anPCT[256 * 4] = {};
     204             : #endif
     205          16 :     const int nColors = GDALGetColorEntryCount(hColorTable);
     206             : 
     207          16 :     if (nColors == 0)
     208             :     {
     209           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     210             :                  "GDALDitherRGB2PCT(): "
     211             :                  "Color table must not be empty.");
     212             : 
     213           0 :         return CE_Failure;
     214             :     }
     215          16 :     else if (nColors > 256)
     216             :     {
     217           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     218             :                  "GDALDitherRGB2PCT(): "
     219             :                  "Color table cannot have more than 256 entries.");
     220             : 
     221           0 :         return CE_Failure;
     222             :     }
     223             : 
     224          16 :     iColor = 0;
     225        3337 :     do
     226             :     {
     227             :         GDALColorEntry sEntry;
     228             : 
     229        3353 :         GDALGetColorEntryAsRGB(hColorTable, iColor, &sEntry);
     230        3353 :         CAST_PCT(anPCT)[4 * iColor + 0] = static_cast<GByte>(sEntry.c1);
     231        3353 :         CAST_PCT(anPCT)[4 * iColor + 1] = static_cast<GByte>(sEntry.c2);
     232        3353 :         CAST_PCT(anPCT)[4 * iColor + 2] = static_cast<GByte>(sEntry.c3);
     233        3353 :         CAST_PCT(anPCT)[4 * iColor + 3] = 0;
     234             : 
     235        3353 :         iColor++;
     236        3353 :     } while (iColor < nColors);
     237             : 
     238             : #ifdef USE_SSE2
     239             :     // Pad to multiple of 8 colors.
     240          16 :     const int nColorsMod8 = nColors % 8;
     241          16 :     if (nColorsMod8)
     242             :     {
     243           1 :         int iDest = nColors;
     244           8 :         for (iColor = 0; iColor < 8 - nColorsMod8 && iDest < 256;
     245             :              iColor++, iDest++)
     246             :         {
     247           7 :             anPCT[iDest] = anPCT[nColors - 1];
     248             :         }
     249             :     }
     250             : #endif
     251             : 
     252             :     /* -------------------------------------------------------------------- */
     253             :     /*      Setup various variables.                                        */
     254             :     /* -------------------------------------------------------------------- */
     255          16 :     const int nCLevels = 1 << nBits;
     256          16 :     const int nCLevelsCube = nCLevels * nCLevels * nCLevels;
     257          16 :     ColorIndex *psColorIndexMap = nullptr;
     258             : 
     259          16 :     GByte *pabyRed = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
     260          16 :     GByte *pabyGreen = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
     261          16 :     GByte *pabyBlue = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
     262             : 
     263          16 :     GByte *pabyIndex = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
     264             : 
     265             :     int *panError =
     266          16 :         static_cast<int *>(VSI_CALLOC_VERBOSE(sizeof(int), (nXSize + 2) * 3));
     267             : 
     268          16 :     if (pabyRed == nullptr || pabyGreen == nullptr || pabyBlue == nullptr ||
     269          16 :         pabyIndex == nullptr || panError == nullptr)
     270             :     {
     271           0 :         CPLFree(pabyRed);
     272           0 :         CPLFree(pabyGreen);
     273           0 :         CPLFree(pabyBlue);
     274           0 :         CPLFree(pabyIndex);
     275           0 :         CPLFree(panError);
     276             : 
     277           0 :         return CE_Failure;
     278             :     }
     279             : 
     280          16 :     GByte *pabyColorMap = nullptr;
     281          16 :     if (pasDynamicColorMap == nullptr)
     282             :     {
     283             :         /* --------------------------------------------------------------------
     284             :          */
     285             :         /*      Build a 24bit to 8 bit color mapping. */
     286             :         /* --------------------------------------------------------------------
     287             :          */
     288             : 
     289             :         pabyColorMap = static_cast<GByte *>(
     290           6 :             VSI_MALLOC_VERBOSE(nCLevelsCube * sizeof(GByte)));
     291           6 :         if (pabyColorMap == nullptr)
     292             :         {
     293           0 :             CPLFree(pabyRed);
     294           0 :             CPLFree(pabyGreen);
     295           0 :             CPLFree(pabyBlue);
     296           0 :             CPLFree(pabyIndex);
     297           0 :             CPLFree(panError);
     298           0 :             CPLFree(pabyColorMap);
     299             : 
     300           0 :             return CE_Failure;
     301             :         }
     302             : 
     303           6 :         FindNearestColor(nColors, anPCT, pabyColorMap, nCLevels);
     304             :     }
     305             :     else
     306             :     {
     307          10 :         pabyColorMap = nullptr;
     308          10 :         if (nBits == 8 && static_cast<GIntBig>(nXSize) * nYSize <= 65536)
     309             :         {
     310             :             // If the image is small enough, then the number of colors
     311             :             // will be limited and using a hashmap, rather than a full table
     312             :             // will be more efficient.
     313           9 :             psColorIndexMap =
     314             :                 reinterpret_cast<ColorIndex *>(pasDynamicColorMap);
     315           9 :             memset(psColorIndexMap, 0xFF, sizeof(ColorIndex) * PRIME_FOR_65536);
     316             :         }
     317             :         else
     318             :         {
     319           1 :             memset(pasDynamicColorMap, 0xFF, 256 * 256 * 256 * sizeof(GInt16));
     320             :         }
     321             :     }
     322             : 
     323             :     /* ==================================================================== */
     324             :     /*      Loop over all scanlines of data to process.                     */
     325             :     /* ==================================================================== */
     326          16 :     CPLErr err = CE_None;
     327             : 
     328        2290 :     for (int iScanline = 0; iScanline < nYSize; iScanline++)
     329             :     {
     330             :         /* --------------------------------------------------------------------
     331             :          */
     332             :         /*      Report progress */
     333             :         /* --------------------------------------------------------------------
     334             :          */
     335        2274 :         if (!pfnProgress(iScanline / static_cast<double>(nYSize), nullptr,
     336             :                          pProgressArg))
     337             :         {
     338           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User Terminated");
     339           0 :             CPLFree(pabyRed);
     340           0 :             CPLFree(pabyGreen);
     341           0 :             CPLFree(pabyBlue);
     342           0 :             CPLFree(pabyIndex);
     343           0 :             CPLFree(panError);
     344           0 :             CPLFree(pabyColorMap);
     345             : 
     346           0 :             return CE_Failure;
     347             :         }
     348             : 
     349             :         /* --------------------------------------------------------------------
     350             :          */
     351             :         /*      Read source data. */
     352             :         /* --------------------------------------------------------------------
     353             :          */
     354        2274 :         CPLErr err1 = GDALRasterIO(hRed, GF_Read, 0, iScanline, nXSize, 1,
     355             :                                    pabyRed, nXSize, 1, GDT_Byte, 0, 0);
     356        2274 :         if (err1 == CE_None)
     357        2274 :             err1 = GDALRasterIO(hGreen, GF_Read, 0, iScanline, nXSize, 1,
     358             :                                 pabyGreen, nXSize, 1, GDT_Byte, 0, 0);
     359        2274 :         if (err1 == CE_None)
     360        2274 :             err1 = GDALRasterIO(hBlue, GF_Read, 0, iScanline, nXSize, 1,
     361             :                                 pabyBlue, nXSize, 1, GDT_Byte, 0, 0);
     362        2274 :         if (err1 != CE_None)
     363             :         {
     364           0 :             CPLFree(pabyRed);
     365           0 :             CPLFree(pabyGreen);
     366           0 :             CPLFree(pabyBlue);
     367           0 :             CPLFree(pabyIndex);
     368           0 :             CPLFree(panError);
     369           0 :             CPLFree(pabyColorMap);
     370             : 
     371           0 :             return err1;
     372             :         }
     373             : 
     374             :         /* --------------------------------------------------------------------
     375             :          */
     376             :         /*      Apply the error from the previous line to this one. */
     377             :         /* --------------------------------------------------------------------
     378             :          */
     379        2274 :         if (bDither)
     380             :         {
     381      278468 :             for (int i = 0; i < nXSize; i++)
     382             :             {
     383      277144 :                 pabyRed[i] = static_cast<GByte>(std::max(
     384      277144 :                     0, std::min(255, (pabyRed[i] + panError[i * 3 + 0 + 3]))));
     385      277144 :                 pabyGreen[i] = static_cast<GByte>(std::max(
     386      554288 :                     0,
     387      277144 :                     std::min(255, (pabyGreen[i] + panError[i * 3 + 1 + 3]))));
     388      277144 :                 pabyBlue[i] = static_cast<GByte>(std::max(
     389      277144 :                     0, std::min(255, (pabyBlue[i] + panError[i * 3 + 2 + 3]))));
     390             :             }
     391             : 
     392        1324 :             memset(panError, 0, sizeof(int) * (nXSize + 2) * 3);
     393             :         }
     394             : 
     395             :         /* --------------------------------------------------------------------
     396             :          */
     397             :         /*      Figure out the nearest color to the RGB value. */
     398             :         /* --------------------------------------------------------------------
     399             :          */
     400        2274 :         int nLastRedError = 0;
     401        2274 :         int nLastGreenError = 0;
     402        2274 :         int nLastBlueError = 0;
     403             : 
     404      486918 :         for (int i = 0; i < nXSize; i++)
     405             :         {
     406             :             const int nRedValue =
     407      484644 :                 std::max(0, std::min(255, pabyRed[i] + nLastRedError));
     408             :             const int nGreenValue =
     409      484644 :                 std::max(0, std::min(255, pabyGreen[i] + nLastGreenError));
     410             :             const int nBlueValue =
     411      484644 :                 std::max(0, std::min(255, pabyBlue[i] + nLastBlueError));
     412             : 
     413      484644 :             int iIndex = 0;
     414      484644 :             int nError = 0;
     415      484644 :             int nSixth = 0;
     416      484644 :             if (psColorIndexMap)
     417             :             {
     418             :                 const GUInt32 nColorCode =
     419      389644 :                     MAKE_COLOR_CODE(nRedValue, nGreenValue, nBlueValue);
     420      389644 :                 GUInt32 nIdx = nColorCode % PRIME_FOR_65536;
     421             :                 while (true)
     422             :                 {
     423      389651 :                     if (psColorIndexMap[nIdx].nColorCode == nColorCode)
     424             :                     {
     425      252924 :                         iIndex = psColorIndexMap[nIdx].nIndex;
     426      252924 :                         break;
     427             :                     }
     428      136727 :                     if (!IsColorCodeSet(psColorIndexMap[nIdx].nColorCode))
     429             :                     {
     430      125973 :                         psColorIndexMap[nIdx].nColorCode = nColorCode;
     431      125973 :                         iIndex = FindNearestColor(nColors, anPCT, nRedValue,
     432             :                                                   nGreenValue, nBlueValue);
     433      125973 :                         psColorIndexMap[nIdx].nIndex =
     434             :                             static_cast<GByte>(iIndex);
     435      125973 :                         break;
     436             :                     }
     437       10754 :                     if (psColorIndexMap[nIdx].nColorCode2 == nColorCode)
     438             :                     {
     439        1476 :                         iIndex = psColorIndexMap[nIdx].nIndex2;
     440        1476 :                         break;
     441             :                     }
     442        9278 :                     if (!IsColorCodeSet(psColorIndexMap[nIdx].nColorCode2))
     443             :                     {
     444        8876 :                         psColorIndexMap[nIdx].nColorCode2 = nColorCode;
     445        8876 :                         iIndex = FindNearestColor(nColors, anPCT, nRedValue,
     446             :                                                   nGreenValue, nBlueValue);
     447        8876 :                         psColorIndexMap[nIdx].nIndex2 =
     448             :                             static_cast<GByte>(iIndex);
     449        8876 :                         break;
     450             :                     }
     451         402 :                     if (psColorIndexMap[nIdx].nColorCode3 == nColorCode)
     452             :                     {
     453          31 :                         iIndex = psColorIndexMap[nIdx].nIndex3;
     454          31 :                         break;
     455             :                     }
     456         371 :                     if (!IsColorCodeSet(psColorIndexMap[nIdx].nColorCode3))
     457             :                     {
     458         364 :                         psColorIndexMap[nIdx].nColorCode3 = nColorCode;
     459         364 :                         iIndex = FindNearestColor(nColors, anPCT, nRedValue,
     460             :                                                   nGreenValue, nBlueValue);
     461         364 :                         psColorIndexMap[nIdx].nIndex3 =
     462             :                             static_cast<GByte>(iIndex);
     463         364 :                         break;
     464             :                     }
     465             : 
     466           0 :                     do
     467             :                     {
     468           7 :                         nIdx += 257;
     469           7 :                         if (nIdx >= PRIME_FOR_65536)
     470           0 :                             nIdx -= PRIME_FOR_65536;
     471             :                     } while (
     472           7 :                         IsColorCodeSet(psColorIndexMap[nIdx].nColorCode) &&
     473           6 :                         psColorIndexMap[nIdx].nColorCode != nColorCode &&
     474           3 :                         IsColorCodeSet(psColorIndexMap[nIdx].nColorCode2) &&
     475           0 :                         psColorIndexMap[nIdx].nColorCode2 != nColorCode &&
     476          10 :                         IsColorCodeSet(psColorIndexMap[nIdx].nColorCode3) &&
     477           0 :                         psColorIndexMap[nIdx].nColorCode3 != nColorCode);
     478             :                 }
     479             :             }
     480       95000 :             else if (pasDynamicColorMap == nullptr)
     481             :             {
     482       15000 :                 const int iRed = nRedValue * nCLevels / 256;
     483       15000 :                 const int iGreen = nGreenValue * nCLevels / 256;
     484       15000 :                 const int iBlue = nBlueValue * nCLevels / 256;
     485             : 
     486       15000 :                 iIndex = pabyColorMap[iRed + iGreen * nCLevels +
     487       15000 :                                       iBlue * nCLevels * nCLevels];
     488             :             }
     489             :             else
     490             :             {
     491             :                 const GUInt32 nColorCode =
     492       80000 :                     MAKE_COLOR_CODE(nRedValue, nGreenValue, nBlueValue);
     493       80000 :                 GInt16 *psIndex = &pasDynamicColorMap[nColorCode];
     494       80000 :                 if (*psIndex < 0)
     495             :                 {
     496       19399 :                     *psIndex = static_cast<GInt16>(FindNearestColor(
     497             :                         nColors, anPCT, nRedValue, nGreenValue, nBlueValue));
     498       19399 :                     iIndex = *psIndex;
     499             :                 }
     500             :                 else
     501             :                 {
     502       60601 :                     iIndex = *psIndex;
     503             :                 }
     504             :             }
     505             : 
     506      484644 :             pabyIndex[i] = static_cast<GByte>(iIndex);
     507      484644 :             if (!bDither)
     508      207500 :                 continue;
     509             : 
     510             :             /* --------------------------------------------------------------------
     511             :              */
     512             :             /*      Compute Red error, and carry it on to the next error line.
     513             :              */
     514             :             /* --------------------------------------------------------------------
     515             :              */
     516      277144 :             nError = nRedValue - CAST_PCT(anPCT)[4 * iIndex + 0];
     517      277144 :             nSixth = nError / 6;
     518             : 
     519      277144 :             panError[i * 3] += nSixth;
     520      277144 :             panError[i * 3 + 6] = nSixth;
     521      277144 :             panError[i * 3 + 3] += nError - 5 * nSixth;
     522             : 
     523      277144 :             nLastRedError = 2 * nSixth;
     524             : 
     525             :             /* --------------------------------------------------------------------
     526             :              */
     527             :             /*      Compute Green error, and carry it on to the next error line.
     528             :              */
     529             :             /* --------------------------------------------------------------------
     530             :              */
     531      277144 :             nError = nGreenValue - CAST_PCT(anPCT)[4 * iIndex + 1];
     532      277144 :             nSixth = nError / 6;
     533             : 
     534      277144 :             panError[i * 3 + 1] += nSixth;
     535      277144 :             panError[i * 3 + 6 + 1] = nSixth;
     536      277144 :             panError[i * 3 + 3 + 1] += nError - 5 * nSixth;
     537             : 
     538      277144 :             nLastGreenError = 2 * nSixth;
     539             : 
     540             :             /* --------------------------------------------------------------------
     541             :              */
     542             :             /*      Compute Blue error, and carry it on to the next error line.
     543             :              */
     544             :             /* --------------------------------------------------------------------
     545             :              */
     546      277144 :             nError = nBlueValue - CAST_PCT(anPCT)[4 * iIndex + 2];
     547      277144 :             nSixth = nError / 6;
     548             : 
     549      277144 :             panError[i * 3 + 2] += nSixth;
     550      277144 :             panError[i * 3 + 6 + 2] = nSixth;
     551      277144 :             panError[i * 3 + 3 + 2] += nError - 5 * nSixth;
     552             : 
     553      277144 :             nLastBlueError = 2 * nSixth;
     554             :         }
     555             : 
     556             :         /* --------------------------------------------------------------------
     557             :          */
     558             :         /*      Write results. */
     559             :         /* --------------------------------------------------------------------
     560             :          */
     561        2274 :         err = GDALRasterIO(hTarget, GF_Write, 0, iScanline, nXSize, 1,
     562             :                            pabyIndex, nXSize, 1, GDT_Byte, 0, 0);
     563        2274 :         if (err != CE_None)
     564           0 :             break;
     565             :     }
     566             : 
     567          16 :     pfnProgress(1.0, nullptr, pProgressArg);
     568             : 
     569             :     /* -------------------------------------------------------------------- */
     570             :     /*      Cleanup                                                         */
     571             :     /* -------------------------------------------------------------------- */
     572          16 :     CPLFree(pabyRed);
     573          16 :     CPLFree(pabyGreen);
     574          16 :     CPLFree(pabyBlue);
     575          16 :     CPLFree(pabyIndex);
     576          16 :     CPLFree(panError);
     577          16 :     CPLFree(pabyColorMap);
     578             : 
     579          16 :     return err;
     580             : }
     581             : 
     582      351220 : static int FindNearestColor(int nColors, int *panPCT, int nRedValue,
     583             :                             int nGreenValue, int nBlueValue)
     584             : 
     585             : {
     586             : #ifdef USE_SSE2
     587      351220 :     int nBestDist = 768;
     588      351220 :     int nBestIndex = 0;
     589             : 
     590      351220 :     int anDistanceUnaligned[16 + 4] =
     591             :         {};  // 4 for alignment on 16-byte boundary.
     592      351220 :     int *anDistance = ALIGN_INT_ARRAY_ON_16_BYTE(anDistanceUnaligned);
     593             : 
     594      351220 :     const __m128i ff = _mm_set1_epi32(0xFFFFFFFF);
     595      351220 :     const __m128i mask_low = _mm_srli_epi64(ff, 32);
     596      351220 :     const __m128i mask_high = _mm_slli_epi64(ff, 32);
     597             : 
     598             :     const unsigned int nColorVal =
     599      351220 :         MAKE_COLOR_CODE(nRedValue, nGreenValue, nBlueValue);
     600      702440 :     const __m128i thisColor = _mm_set1_epi32(nColorVal);
     601      351220 :     const __m128i thisColor_low = _mm_srli_epi64(thisColor, 32);
     602      351220 :     const __m128i thisColor_high = _mm_slli_epi64(thisColor, 32);
     603             : 
     604     9591380 :     for (int iColor = 0; iColor < nColors; iColor += 8)
     605             :     {
     606             :         const __m128i pctColor =
     607     9240160 :             _mm_load_si128(reinterpret_cast<__m128i *>(&panPCT[iColor]));
     608             :         const __m128i pctColor2 =
     609    18480300 :             _mm_load_si128(reinterpret_cast<__m128i *>(&panPCT[iColor + 4]));
     610             : 
     611    18480300 :         _mm_store_si128(
     612             :             reinterpret_cast<__m128i *>(anDistance),
     613             :             _mm_sad_epu8(_mm_and_si128(pctColor, mask_low), thisColor_low));
     614     9240160 :         _mm_store_si128(
     615     9240160 :             reinterpret_cast<__m128i *>(anDistance + 4),
     616             :             _mm_sad_epu8(_mm_and_si128(pctColor, mask_high), thisColor_high));
     617     9240160 :         _mm_store_si128(
     618     9240160 :             reinterpret_cast<__m128i *>(anDistance + 8),
     619             :             _mm_sad_epu8(_mm_and_si128(pctColor2, mask_low), thisColor_low));
     620     9240160 :         _mm_store_si128(
     621     9240160 :             reinterpret_cast<__m128i *>(anDistance + 12),
     622             :             _mm_sad_epu8(_mm_and_si128(pctColor2, mask_high), thisColor_high));
     623             : 
     624     9240160 :         if (anDistance[0] < nBestDist)
     625             :         {
     626      441401 :             nBestIndex = iColor;
     627      441401 :             nBestDist = anDistance[0];
     628             :         }
     629     9240160 :         if (anDistance[4] < nBestDist)
     630             :         {
     631      271510 :             nBestIndex = iColor + 1;
     632      271510 :             nBestDist = anDistance[4];
     633             :         }
     634     9240160 :         if (anDistance[2] < nBestDist)
     635             :         {
     636      150984 :             nBestIndex = iColor + 2;
     637      150984 :             nBestDist = anDistance[2];
     638             :         }
     639     9240160 :         if (anDistance[6] < nBestDist)
     640             :         {
     641      175743 :             nBestIndex = iColor + 3;
     642      175743 :             nBestDist = anDistance[6];
     643             :         }
     644     9240160 :         if (anDistance[8 + 0] < nBestDist)
     645             :         {
     646      119903 :             nBestIndex = iColor + 4;
     647      119903 :             nBestDist = anDistance[8 + 0];
     648             :         }
     649     9240160 :         if (anDistance[8 + 4] < nBestDist)
     650             :         {
     651       98139 :             nBestIndex = iColor + 4 + 1;
     652       98139 :             nBestDist = anDistance[8 + 4];
     653             :         }
     654     9240160 :         if (anDistance[8 + 2] < nBestDist)
     655             :         {
     656      132829 :             nBestIndex = iColor + 4 + 2;
     657      132829 :             nBestDist = anDistance[8 + 2];
     658             :         }
     659     9240160 :         if (anDistance[8 + 6] < nBestDist)
     660             :         {
     661      185338 :             nBestIndex = iColor + 4 + 3;
     662      185338 :             nBestDist = anDistance[8 + 6];
     663             :         }
     664             :     }
     665      351220 :     return nBestIndex;
     666             : #else
     667             :     int nBestDist = 768;
     668             :     int nBestIndex = 0;
     669             : 
     670             :     for (int iColor = 0; iColor < nColors; iColor++)
     671             :     {
     672             :         const int nThisDist = std::abs(nRedValue - panPCT[4 * iColor + 0]) +
     673             :                               std::abs(nGreenValue - panPCT[4 * iColor + 1]) +
     674             :                               std::abs(nBlueValue - panPCT[4 * iColor + 2]);
     675             : 
     676             :         if (nThisDist < nBestDist)
     677             :         {
     678             :             nBestIndex = iColor;
     679             :             nBestDist = nThisDist;
     680             :         }
     681             :     }
     682             :     return nBestIndex;
     683             : #endif
     684             : }
     685             : 
     686             : /************************************************************************/
     687             : /*                          FindNearestColor()                          */
     688             : /*                                                                      */
     689             : /*      Finear near PCT color for any RGB color.                        */
     690             : /************************************************************************/
     691             : 
     692           6 : static void FindNearestColor(int nColors, int *panPCT, GByte *pabyColorMap,
     693             :                              int nCLevels)
     694             : 
     695             : {
     696             :     /* -------------------------------------------------------------------- */
     697             :     /*  Loop over all the cells in the high density cube.                   */
     698             :     /* -------------------------------------------------------------------- */
     699         198 :     for (int iBlue = 0; iBlue < nCLevels; iBlue++)
     700             :     {
     701        6336 :         for (int iGreen = 0; iGreen < nCLevels; iGreen++)
     702             :         {
     703      202752 :             for (int iRed = 0; iRed < nCLevels; iRed++)
     704             :             {
     705      196608 :                 const int nRedValue = (iRed * 255) / (nCLevels - 1);
     706      196608 :                 const int nGreenValue = (iGreen * 255) / (nCLevels - 1);
     707      196608 :                 const int nBlueValue = (iBlue * 255) / (nCLevels - 1);
     708             : 
     709      196608 :                 const int nBestIndex = FindNearestColor(
     710             :                     nColors, panPCT, nRedValue, nGreenValue, nBlueValue);
     711      196608 :                 pabyColorMap[iRed + iGreen * nCLevels +
     712      196608 :                              iBlue * nCLevels * nCLevels] =
     713             :                     static_cast<GByte>(nBestIndex);
     714             :             }
     715             :         }
     716             :     }
     717           6 : }

Generated by: LCOV version 1.14