LCOV - code coverage report
Current view: top level - alg - gdalmediancut.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 569 612 93.0 %
Date: 2026-02-28 20:27:10 Functions: 19 20 95.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CIETMap Phase 2
       4             :  * Purpose:  Use median cut algorithm to generate an near-optimal PCT for a
       5             :  *           given RGB image.  Implemented as function GDALComputeMedianCutPCT.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2001, Frank Warmerdam
      10             :  * Copyright (c) 2007-2010, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ******************************************************************************
      14             :  *
      15             :  * This code was based on the tiffmedian.c code from libtiff (www.libtiff.org)
      16             :  * which was based on a paper by Paul Heckbert:
      17             :  *
      18             :  *      "Color  Image Quantization for Frame Buffer Display", Paul
      19             :  *      Heckbert, SIGGRAPH proceedings, 1982, pp. 297-307.
      20             :  *
      21             :  */
      22             : 
      23             : #include "cpl_port.h"
      24             : #include "gdal_alg.h"
      25             : #include "gdal_alg_priv.h"
      26             : 
      27             : #include <climits>
      28             : #include <cstring>
      29             : 
      30             : #include <algorithm>
      31             : #include <limits>
      32             : 
      33             : #include "cpl_conv.h"
      34             : #include "cpl_error.h"
      35             : #include "cpl_float.h"
      36             : #include "cpl_progress.h"
      37             : #include "cpl_vsi.h"
      38             : #include "gdal.h"
      39             : #include "gdal_priv.h"
      40             : 
      41     4128224 : template <typename T> static T *HISTOGRAM(T *h, int n, int r, int g, int b)
      42             : {
      43     4128224 :     const int index = (r * n + g) * n + b;
      44     4128224 :     return &h[index];
      45             : }
      46             : 
      47             : #ifndef MAKE_COLOR_CODE_defined
      48             : #define MAKE_COLOR_CODE_defined
      49             : 
      50    17939300 : static int MAKE_COLOR_CODE(int r, int g, int b)
      51             : {
      52    17939300 :     return r | (g << 8) | (b << 16);
      53             : }
      54             : #endif
      55             : 
      56             : // NOTE: If changing the size of this structure, edit
      57             : // MEDIAN_CUT_AND_DITHER_BUFFER_SIZE_65536 in gdal_alg_priv.h and take into
      58             : // account ColorIndex in gdaldither.cpp.
      59             : typedef struct
      60             : {
      61             :     GUInt32 nColorCode;
      62             :     int nCount;
      63             :     GUInt32 nColorCode2;
      64             :     int nCount2;
      65             :     GUInt32 nColorCode3;
      66             :     int nCount3;
      67             : } HashHistogram;
      68             : 
      69             : typedef struct colorbox
      70             : {
      71             :     struct colorbox *next, *prev;
      72             :     int rmin, rmax;
      73             :     int gmin, gmax;
      74             :     int bmin, bmax;
      75             :     GUIntBig total;
      76             : } Colorbox;
      77             : 
      78             : template <class T>
      79             : static void splitbox(Colorbox *ptr, const T *histogram,
      80             :                      const HashHistogram *psHashHistogram, int nCLevels,
      81             :                      Colorbox **pfreeboxes, Colorbox **pusedboxes,
      82             :                      GByte *pabyRedBand, GByte *pabyGreenBand,
      83             :                      GByte *pabyBlueBand, T nPixels);
      84             : 
      85             : template <class T>
      86             : static void shrinkbox(Colorbox *box, const T *histogram, int nCLevels);
      87             : 
      88             : static Colorbox *largest_box(Colorbox *usedboxes);
      89             : 
      90             : /************************************************************************/
      91             : /*                      GDALComputeMedianCutPCT()                       */
      92             : /************************************************************************/
      93             : 
      94             : /**
      95             :  * Compute optimal PCT for RGB image.
      96             :  *
      97             :  * This function implements a median cut algorithm to compute an "optimal"
      98             :  * pseudocolor table for representing an input RGB image.  This PCT could
      99             :  * then be used with GDALDitherRGB2PCT() to convert a 24bit RGB image into
     100             :  * an eightbit pseudo-colored image.
     101             :  *
     102             :  * This code was based on the tiffmedian.c code from libtiff (www.libtiff.org)
     103             :  * which was based on a paper by Paul Heckbert:
     104             :  *
     105             :  * \verbatim
     106             :  *   "Color  Image Quantization for Frame Buffer Display", Paul
     107             :  *   Heckbert, SIGGRAPH proceedings, 1982, pp. 297-307.
     108             :  * \endverbatim
     109             :  *
     110             :  * The red, green and blue input bands do not necessarily need to come
     111             :  * from the same file, but they must be the same width and height.  They will
     112             :  * be clipped to 8bit during reading, so non-eight bit bands are generally
     113             :  * inappropriate.
     114             :  *
     115             :  * Since GDAL 3.13, source nodata values or mask band will be taken into account
     116             :  * to determine which pixels are valid.
     117             :  *
     118             :  * @param hRed Red input band.
     119             :  * @param hGreen Green input band.
     120             :  * @param hBlue Blue input band.
     121             :  * @param pfnIncludePixel function used to test which pixels should be included
     122             :  * in the analysis.  At this time this argument is ignored.
     123             :  * This should normally be NULL.
     124             :  * @param nColors the desired number of colors to be returned (2-256).
     125             :  * @param hColorTable the colors will be returned in this color table object.
     126             :  * @param pfnProgress callback for reporting algorithm progress matching the
     127             :  * GDALProgressFunc() semantics.  May be NULL.
     128             :  * @param pProgressArg callback argument passed to pfnProgress.
     129             :  *
     130             :  * @return returns CE_None on success or CE_Failure if an error occurs.
     131             :  */
     132             : 
     133           4 : extern "C" int CPL_STDCALL GDALComputeMedianCutPCT(
     134             :     GDALRasterBandH hRed, GDALRasterBandH hGreen, GDALRasterBandH hBlue,
     135             :     int (*pfnIncludePixel)(int, int, void *), int nColors,
     136             :     GDALColorTableH hColorTable, GDALProgressFunc pfnProgress,
     137             :     void *pProgressArg)
     138             : 
     139             : {
     140           4 :     VALIDATE_POINTER1(hRed, "GDALComputeMedianCutPCT", CE_Failure);
     141           4 :     const int nXSize = GDALGetRasterBandXSize(hRed);
     142           4 :     const int nYSize = GDALGetRasterBandYSize(hRed);
     143           4 :     if (nYSize == 0)
     144           0 :         return CE_Failure;
     145           4 :     if (static_cast<GUInt32>(nXSize) <
     146           4 :         std::numeric_limits<GUInt32>::max() / static_cast<GUInt32>(nYSize))
     147             :     {
     148           4 :         return GDALComputeMedianCutPCTInternal(
     149             :             hRed, hGreen, hBlue, nullptr, nullptr, nullptr, pfnIncludePixel,
     150             :             nColors, 5, static_cast<GUInt32 *>(nullptr), hColorTable,
     151           4 :             pfnProgress, pProgressArg);
     152             :     }
     153             :     else
     154             :     {
     155           0 :         return GDALComputeMedianCutPCTInternal(
     156             :             hRed, hGreen, hBlue, nullptr, nullptr, nullptr, pfnIncludePixel,
     157             :             nColors, 5, static_cast<GUIntBig *>(nullptr), hColorTable,
     158           0 :             pfnProgress, pProgressArg);
     159             :     }
     160             : }
     161             : 
     162             : #ifndef IsColorCodeSet_defined
     163             : #define IsColorCodeSet_defined
     164             : 
     165    18835800 : static inline bool IsColorCodeSet(GUInt32 nColorCode)
     166             : {
     167    18835800 :     return (nColorCode >> 31) == 0;
     168             : }
     169             : #endif
     170             : 
     171    17549600 : static inline int FindColorCount(const HashHistogram *psHashHistogram,
     172             :                                  GUInt32 nColorCode)
     173             : {
     174             : 
     175    17549600 :     GUInt32 nIdx = nColorCode % PRIME_FOR_65536;
     176             :     while (true)
     177             :     {
     178    17550200 :         if (!IsColorCodeSet(psHashHistogram[nIdx].nColorCode))
     179             :         {
     180    16219100 :             return 0;
     181             :         }
     182     1331150 :         if (psHashHistogram[nIdx].nColorCode == nColorCode)
     183             :         {
     184      141464 :             return psHashHistogram[nIdx].nCount;
     185             :         }
     186     1189680 :         if (!IsColorCodeSet(psHashHistogram[nIdx].nColorCode2))
     187             :         {
     188     1151920 :             return 0;
     189             :         }
     190       37761 :         if (psHashHistogram[nIdx].nColorCode2 == nColorCode)
     191             :         {
     192        4030 :             return psHashHistogram[nIdx].nCount2;
     193             :         }
     194       33731 :         if (!IsColorCodeSet(psHashHistogram[nIdx].nColorCode3))
     195             :         {
     196       33078 :             return 0;
     197             :         }
     198         653 :         if (psHashHistogram[nIdx].nColorCode3 == nColorCode)
     199             :         {
     200          59 :             return psHashHistogram[nIdx].nCount3;
     201             :         }
     202             : 
     203           0 :         do
     204             :         {
     205         594 :             nIdx += 257;
     206         594 :             if (nIdx >= PRIME_FOR_65536)
     207           0 :                 nIdx -= PRIME_FOR_65536;
     208         594 :         } while (IsColorCodeSet(psHashHistogram[nIdx].nColorCode) &&
     209         260 :                  psHashHistogram[nIdx].nColorCode != nColorCode &&
     210         130 :                  IsColorCodeSet(psHashHistogram[nIdx].nColorCode2) &&
     211           0 :                  psHashHistogram[nIdx].nColorCode2 != nColorCode &&
     212         724 :                  IsColorCodeSet(psHashHistogram[nIdx].nColorCode3) &&
     213           0 :                  psHashHistogram[nIdx].nColorCode3 != nColorCode);
     214             :     }
     215             : }
     216             : 
     217      389644 : static inline int *FindAndInsertColorCount(HashHistogram *psHashHistogram,
     218             :                                            GUInt32 nColorCode)
     219             : {
     220      389644 :     GUInt32 nIdx = nColorCode % PRIME_FOR_65536;
     221             :     while (true)
     222             :     {
     223      389644 :         if (psHashHistogram[nIdx].nColorCode == nColorCode)
     224             :         {
     225      329957 :             return &(psHashHistogram[nIdx].nCount);
     226             :         }
     227       59687 :         if (!IsColorCodeSet(psHashHistogram[nIdx].nColorCode))
     228             :         {
     229       56306 :             psHashHistogram[nIdx].nColorCode = nColorCode;
     230       56306 :             psHashHistogram[nIdx].nCount = 0;
     231       56306 :             return &(psHashHistogram[nIdx].nCount);
     232             :         }
     233        3381 :         if (psHashHistogram[nIdx].nColorCode2 == nColorCode)
     234             :         {
     235        1661 :             return &(psHashHistogram[nIdx].nCount2);
     236             :         }
     237        1720 :         if (!IsColorCodeSet(psHashHistogram[nIdx].nColorCode2))
     238             :         {
     239        1687 :             psHashHistogram[nIdx].nColorCode2 = nColorCode;
     240        1687 :             psHashHistogram[nIdx].nCount2 = 0;
     241        1687 :             return &(psHashHistogram[nIdx].nCount2);
     242             :         }
     243          33 :         if (psHashHistogram[nIdx].nColorCode3 == nColorCode)
     244             :         {
     245           5 :             return &(psHashHistogram[nIdx].nCount3);
     246             :         }
     247          28 :         if (!IsColorCodeSet(psHashHistogram[nIdx].nColorCode3))
     248             :         {
     249          28 :             psHashHistogram[nIdx].nColorCode3 = nColorCode;
     250          28 :             psHashHistogram[nIdx].nCount3 = 0;
     251          28 :             return &(psHashHistogram[nIdx].nCount3);
     252             :         }
     253             : 
     254           0 :         do
     255             :         {
     256           0 :             nIdx += 257;
     257           0 :             if (nIdx >= PRIME_FOR_65536)
     258           0 :                 nIdx -= PRIME_FOR_65536;
     259           0 :         } while (IsColorCodeSet(psHashHistogram[nIdx].nColorCode) &&
     260           0 :                  psHashHistogram[nIdx].nColorCode != nColorCode &&
     261           0 :                  IsColorCodeSet(psHashHistogram[nIdx].nColorCode2) &&
     262           0 :                  psHashHistogram[nIdx].nColorCode2 != nColorCode &&
     263           0 :                  IsColorCodeSet(psHashHistogram[nIdx].nColorCode3) &&
     264           0 :                  psHashHistogram[nIdx].nColorCode3 != nColorCode);
     265             :     }
     266             : }
     267             : 
     268             : template <class T>
     269          50 : int GDALComputeMedianCutPCTInternal(
     270             :     GDALRasterBandH hRed, GDALRasterBandH hGreen, GDALRasterBandH hBlue,
     271             :     GByte *pabyRedBand, GByte *pabyGreenBand, GByte *pabyBlueBand,
     272             :     int (*pfnIncludePixel)(int, int, void *), int nColors, int nBits,
     273             :     T *panHistogram,  // NULL, or >= size (1<<nBits)^3 * sizeof(T) bytes.
     274             :     GDALColorTableH hColorTable, GDALProgressFunc pfnProgress,
     275             :     void *pProgressArg, std::vector<T> *panPixelCountPerColorTableEntry)
     276             : 
     277             : {
     278          50 :     VALIDATE_POINTER1(hRed, "GDALComputeMedianCutPCT", CE_Failure);
     279          50 :     VALIDATE_POINTER1(hGreen, "GDALComputeMedianCutPCT", CE_Failure);
     280          50 :     VALIDATE_POINTER1(hBlue, "GDALComputeMedianCutPCT", CE_Failure);
     281             : 
     282          50 :     CPLErr err = CE_None;
     283             : 
     284             :     /* -------------------------------------------------------------------- */
     285             :     /*      Validate parameters.                                            */
     286             :     /* -------------------------------------------------------------------- */
     287          50 :     const int nXSize = GDALGetRasterBandXSize(hRed);
     288          50 :     const int nYSize = GDALGetRasterBandYSize(hRed);
     289             : 
     290          50 :     if (GDALGetRasterBandXSize(hGreen) != nXSize ||
     291          50 :         GDALGetRasterBandYSize(hGreen) != nYSize ||
     292         150 :         GDALGetRasterBandXSize(hBlue) != nXSize ||
     293          50 :         GDALGetRasterBandYSize(hBlue) != nYSize)
     294             :     {
     295           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     296             :                  "Green or blue band doesn't match size of red band.");
     297             : 
     298           0 :         return CE_Failure;
     299             :     }
     300             : 
     301          50 :     if (pfnIncludePixel != nullptr)
     302             :     {
     303           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     304             :                  "GDALComputeMedianCutPCT() doesn't currently support "
     305             :                  "pfnIncludePixel function.");
     306             : 
     307           0 :         return CE_Failure;
     308             :     }
     309             : 
     310          50 :     if (nColors <= 0)
     311             :     {
     312           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     313             :                  "GDALComputeMedianCutPCT(): "
     314             :                  "nColors must be strictly greater than 1.");
     315             : 
     316           0 :         return CE_Failure;
     317             :     }
     318             : 
     319          50 :     if (nColors > 256)
     320             :     {
     321           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
     322             :                  "GDALComputeMedianCutPCT(): "
     323             :                  "nColors must be lesser than or equal to 256.");
     324             : 
     325           0 :         return CE_Failure;
     326             :     }
     327             : 
     328          50 :     if (pfnProgress == nullptr)
     329          23 :         pfnProgress = GDALDummyProgress;
     330             : 
     331          50 :     int nSrcNoData = -1;
     332          50 :     GDALRasterBandH hMaskBand = nullptr;
     333          50 :     int bSrcHasNoDataR = FALSE;
     334          50 :     const double dfSrcNoDataR = GDALGetRasterNoDataValue(hRed, &bSrcHasNoDataR);
     335          50 :     int bSrcHasNoDataG = FALSE;
     336             :     const double dfSrcNoDataG =
     337          50 :         GDALGetRasterNoDataValue(hGreen, &bSrcHasNoDataG);
     338          50 :     int bSrcHasNoDataB = FALSE;
     339             :     const double dfSrcNoDataB =
     340          50 :         GDALGetRasterNoDataValue(hBlue, &bSrcHasNoDataB);
     341          50 :     if (bSrcHasNoDataR && bSrcHasNoDataG && bSrcHasNoDataB &&
     342           2 :         dfSrcNoDataR == dfSrcNoDataG && dfSrcNoDataR == dfSrcNoDataB &&
     343           2 :         dfSrcNoDataR >= 0 && dfSrcNoDataR <= 255 &&
     344           2 :         std::round(dfSrcNoDataR) == dfSrcNoDataR)
     345             :     {
     346           2 :         nSrcNoData = static_cast<int>(dfSrcNoDataR);
     347             :     }
     348             :     else
     349             :     {
     350          48 :         const int nMaskFlags = GDALGetMaskFlags(hRed);
     351          48 :         if ((nMaskFlags & GMF_PER_DATASET))
     352           3 :             hMaskBand = GDALGetMaskBand(hRed);
     353             :     }
     354             : 
     355             :     /* ==================================================================== */
     356             :     /*      STEP 1: create empty boxes.                                     */
     357             :     /* ==================================================================== */
     358          71 :     if (static_cast<GUInt32>(nXSize) >
     359          50 :         cpl::NumericLimits<T>::max() / static_cast<GUInt32>(nYSize))
     360             :     {
     361           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     362             :                  "GDALComputeMedianCutPCTInternal() not called "
     363             :                  "with large enough type");
     364             :     }
     365             : 
     366          50 :     T nPixels = 0;
     367          11 :     if (nBits == 8 && pabyRedBand != nullptr && pabyGreenBand != nullptr &&
     368          61 :         pabyBlueBand != nullptr &&
     369          10 :         static_cast<GUInt32>(nXSize) <=
     370          10 :             cpl::NumericLimits<T>::max() / static_cast<GUInt32>(nYSize))
     371             :     {
     372          10 :         nPixels = static_cast<T>(nXSize) * static_cast<T>(nYSize);
     373             :     }
     374             : 
     375          50 :     const int nCLevels = 1 << nBits;
     376          50 :     const int nCLevelsCube = nCLevels * nCLevels * nCLevels;
     377          50 :     T *histogram = nullptr;
     378          50 :     HashHistogram *psHashHistogram = nullptr;
     379          50 :     if (panHistogram)
     380             :     {
     381          10 :         if (nBits == 8 && static_cast<GUIntBig>(nXSize) * nYSize <= 65536)
     382             :         {
     383             :             // If the image is small enough, then the number of colors
     384             :             // will be limited and using a hashmap, rather than a full table
     385             :             // will be more efficient.
     386           9 :             histogram = nullptr;
     387           9 :             psHashHistogram = reinterpret_cast<HashHistogram *>(panHistogram);
     388           9 :             memset(psHashHistogram, 0xFF,
     389             :                    sizeof(HashHistogram) * PRIME_FOR_65536);
     390             :         }
     391             :         else
     392             :         {
     393           1 :             histogram = panHistogram;
     394           1 :             memset(histogram, 0, nCLevelsCube * sizeof(T));
     395             :         }
     396             :     }
     397             :     else
     398             :     {
     399             :         histogram =
     400          40 :             static_cast<T *>(VSI_CALLOC_VERBOSE(nCLevelsCube, sizeof(T)));
     401          40 :         if (histogram == nullptr)
     402             :         {
     403           0 :             return CE_Failure;
     404             :         }
     405             :     }
     406             :     Colorbox *box_list =
     407          50 :         static_cast<Colorbox *>(CPLMalloc(nColors * sizeof(Colorbox)));
     408          50 :     Colorbox *freeboxes = box_list;
     409          50 :     freeboxes[0].next = &freeboxes[1];
     410          50 :     freeboxes[0].prev = nullptr;
     411       11339 :     for (int i = 1; i < nColors - 1; ++i)
     412             :     {
     413       11289 :         freeboxes[i].next = &freeboxes[i + 1];
     414       11289 :         freeboxes[i].prev = &freeboxes[i - 1];
     415             :     }
     416          50 :     freeboxes[nColors - 1].next = nullptr;
     417          50 :     freeboxes[nColors - 1].prev = &freeboxes[nColors - 2];
     418             : 
     419             :     /* ==================================================================== */
     420             :     /*      Build histogram.                                                */
     421             :     /* ==================================================================== */
     422             : 
     423             :     /* -------------------------------------------------------------------- */
     424             :     /*      Initialize the box datastructures.                              */
     425             :     /* -------------------------------------------------------------------- */
     426          50 :     Colorbox *freeboxes_before = freeboxes;
     427          50 :     freeboxes = freeboxes_before->next;
     428          50 :     if (freeboxes)
     429          50 :         freeboxes->prev = nullptr;
     430             : 
     431          50 :     Colorbox *usedboxes = freeboxes_before;
     432          50 :     usedboxes->next = nullptr;
     433          50 :     usedboxes->rmin = 999;
     434          50 :     usedboxes->gmin = 999;
     435          50 :     usedboxes->bmin = 999;
     436          50 :     usedboxes->rmax = -1;
     437          50 :     usedboxes->gmax = -1;
     438          50 :     usedboxes->bmax = -1;
     439          50 :     usedboxes->total =
     440          50 :         static_cast<GUIntBig>(nXSize) * static_cast<GUIntBig>(nYSize);
     441             : 
     442             :     /* -------------------------------------------------------------------- */
     443             :     /*      Collect histogram.                                              */
     444             :     /* -------------------------------------------------------------------- */
     445             : 
     446             :     // TODO(schwehr): Move these closer to usage after removing gotos.
     447          50 :     const int nColorShift = 8 - nBits;
     448          50 :     int nColorCounter = 0;
     449          50 :     GByte anRed[256] = {};
     450          50 :     GByte anGreen[256] = {};
     451          50 :     GByte anBlue[256] = {};
     452             : 
     453          50 :     GByte *pabyRedLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
     454          50 :     GByte *pabyGreenLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
     455          50 :     GByte *pabyBlueLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
     456           0 :     std::unique_ptr<GByte, VSIFreeReleaser> pabyMask;
     457          50 :     if (hMaskBand)
     458           3 :         pabyMask.reset(static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize)));
     459             : 
     460          50 :     if (pabyRedLine == nullptr || pabyGreenLine == nullptr ||
     461         100 :         pabyBlueLine == nullptr || (hMaskBand && !pabyMask))
     462             :     {
     463           0 :         err = CE_Failure;
     464           0 :         goto end_and_cleanup;
     465             :     }
     466             : 
     467        8549 :     for (int iLine = 0; iLine < nYSize; iLine++)
     468             :     {
     469        8499 :         if (!pfnProgress(iLine / static_cast<double>(nYSize),
     470             :                          "Generating Histogram", pProgressArg))
     471             :         {
     472           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User Terminated");
     473           0 :             err = CE_Failure;
     474           0 :             goto end_and_cleanup;
     475             :         }
     476             : 
     477        8499 :         err = GDALRasterIO(hRed, GF_Read, 0, iLine, nXSize, 1, pabyRedLine,
     478             :                            nXSize, 1, GDT_UInt8, 0, 0);
     479        8499 :         if (err == CE_None)
     480        8499 :             err = GDALRasterIO(hGreen, GF_Read, 0, iLine, nXSize, 1,
     481             :                                pabyGreenLine, nXSize, 1, GDT_UInt8, 0, 0);
     482        8499 :         if (err == CE_None)
     483        8499 :             err = GDALRasterIO(hBlue, GF_Read, 0, iLine, nXSize, 1,
     484             :                                pabyBlueLine, nXSize, 1, GDT_UInt8, 0, 0);
     485        8499 :         if (err == CE_None && hMaskBand)
     486        1687 :             err = GDALRasterIO(hMaskBand, GF_Read, 0, iLine, nXSize, 1,
     487        1687 :                                pabyMask.get(), nXSize, 1, GDT_UInt8, 0, 0);
     488        8499 :         if (err != CE_None)
     489           0 :             goto end_and_cleanup;
     490             : 
     491     6324100 :         for (int iPixel = 0; iPixel < nXSize; iPixel++)
     492             :         {
     493    15038530 :             if ((pabyRedLine[iPixel] == nSrcNoData &&
     494       33554 :                  pabyGreenLine[iPixel] == nSrcNoData &&
     495    15014790 :                  pabyBlueLine[iPixel] == nSrcNoData) ||
     496     8665890 :                 (pabyMask && (pabyMask.get())[iPixel] == 0))
     497             :             {
     498     2373738 :                 continue;
     499             :             }
     500             : 
     501     3941870 :             const int nRed = pabyRedLine[iPixel] >> nColorShift;
     502     3941870 :             const int nGreen = pabyGreenLine[iPixel] >> nColorShift;
     503     3941870 :             const int nBlue = pabyBlueLine[iPixel] >> nColorShift;
     504             : 
     505     3941870 :             usedboxes->rmin = std::min(usedboxes->rmin, nRed);
     506     3941870 :             usedboxes->gmin = std::min(usedboxes->gmin, nGreen);
     507     3941870 :             usedboxes->bmin = std::min(usedboxes->bmin, nBlue);
     508     3941870 :             usedboxes->rmax = std::max(usedboxes->rmax, nRed);
     509     3941870 :             usedboxes->gmax = std::max(usedboxes->gmax, nGreen);
     510     3941870 :             usedboxes->bmax = std::max(usedboxes->bmax, nBlue);
     511             : 
     512             :             bool bFirstOccurrence;
     513     3941870 :             if (psHashHistogram)
     514             :             {
     515      389644 :                 int *pnColor = FindAndInsertColorCount(
     516      389644 :                     psHashHistogram, MAKE_COLOR_CODE(nRed, nGreen, nBlue));
     517      389644 :                 bFirstOccurrence = (*pnColor == 0);
     518      389644 :                 (*pnColor)++;
     519             :             }
     520             :             else
     521             :             {
     522             :                 T *pnColor =
     523     3552220 :                     HISTOGRAM(histogram, nCLevels, nRed, nGreen, nBlue);
     524     3552220 :                 bFirstOccurrence = (*pnColor == 0);
     525     3552220 :                 (*pnColor)++;
     526             :             }
     527     3941870 :             if (bFirstOccurrence)
     528             :             {
     529      111366 :                 if (nColorShift == 0 && nColorCounter < nColors)
     530             :                 {
     531        2314 :                     anRed[nColorCounter] = static_cast<GByte>(nRed);
     532        2314 :                     anGreen[nColorCounter] = static_cast<GByte>(nGreen);
     533        2314 :                     anBlue[nColorCounter] = static_cast<GByte>(nBlue);
     534             :                 }
     535      111366 :                 nColorCounter++;
     536             :             }
     537             :         }
     538             :     }
     539             : 
     540          50 :     if (!pfnProgress(1.0, "Generating Histogram", pProgressArg))
     541             :     {
     542           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User Terminated");
     543           0 :         err = CE_Failure;
     544           0 :         goto end_and_cleanup;
     545             :     }
     546             : 
     547          50 :     if (nColorShift == 0 && nColorCounter <= nColors)
     548             :     {
     549             : #if DEBUG_VERBOSE
     550             :         CPLDebug("MEDIAN_CUT", "%d colors found <= %d", nColorCounter, nColors);
     551             : #endif
     552           2 :         if (panPixelCountPerColorTableEntry)
     553             :         {
     554           1 :             panPixelCountPerColorTableEntry->clear();
     555           1 :             panPixelCountPerColorTableEntry->reserve(nColors);
     556             :         }
     557          12 :         for (int iColor = 0; iColor < nColorCounter; iColor++)
     558             :         {
     559          10 :             const GDALColorEntry sEntry = {static_cast<GByte>(anRed[iColor]),
     560          10 :                                            static_cast<GByte>(anGreen[iColor]),
     561          10 :                                            static_cast<GByte>(anBlue[iColor]),
     562             :                                            255};
     563          10 :             GDALSetColorEntry(hColorTable, iColor, &sEntry);
     564          10 :             if (panPixelCountPerColorTableEntry)
     565             :             {
     566           9 :                 if (psHashHistogram)
     567             :                 {
     568           0 :                     int *pnColor = FindAndInsertColorCount(
     569             :                         psHashHistogram,
     570           0 :                         MAKE_COLOR_CODE(anRed[iColor], anGreen[iColor],
     571             :                                         anBlue[iColor]));
     572           0 :                     panPixelCountPerColorTableEntry->push_back(*pnColor);
     573             :                 }
     574             :                 else
     575             :                 {
     576           9 :                     T *pnColor = HISTOGRAM(histogram, nCLevels, anRed[iColor],
     577             :                                            anGreen[iColor], anBlue[iColor]);
     578           9 :                     panPixelCountPerColorTableEntry->push_back(*pnColor);
     579             :                 }
     580             :             }
     581             :         }
     582           2 :         goto end_and_cleanup;
     583             :     }
     584             : 
     585             :     /* ==================================================================== */
     586             :     /*      STEP 3: continually subdivide boxes until no more free          */
     587             :     /*      boxes remain or until all colors assigned.                      */
     588             :     /* ==================================================================== */
     589        7413 :     while (freeboxes != nullptr)
     590             :     {
     591        7365 :         auto ptr = largest_box(usedboxes);
     592        7365 :         if (ptr != nullptr)
     593        7347 :             splitbox(ptr, histogram, psHashHistogram, nCLevels, &freeboxes,
     594             :                      &usedboxes, pabyRedBand, pabyGreenBand, pabyBlueBand,
     595             :                      nPixels);
     596             :         else
     597          18 :             freeboxes = nullptr;
     598             :     }
     599             : 
     600             :     /* ==================================================================== */
     601             :     /*      STEP 4: assign colors to all boxes                              */
     602             :     /* ==================================================================== */
     603             :     {
     604          48 :         Colorbox *ptr = usedboxes;
     605          48 :         if (panPixelCountPerColorTableEntry)
     606             :         {
     607          20 :             panPixelCountPerColorTableEntry->clear();
     608          20 :             panPixelCountPerColorTableEntry->reserve(nColors);
     609             :         }
     610        7443 :         for (int i = 0; ptr != nullptr; ++i, ptr = ptr->next)
     611             :         {
     612        7395 :             const GDALColorEntry sEntry = {
     613        7395 :                 static_cast<GByte>(((ptr->rmin + ptr->rmax) << nColorShift) /
     614             :                                    2),
     615        7395 :                 static_cast<GByte>(((ptr->gmin + ptr->gmax) << nColorShift) /
     616             :                                    2),
     617        7395 :                 static_cast<GByte>(((ptr->bmin + ptr->bmax) << nColorShift) /
     618             :                                    2),
     619             :                 255};
     620        7395 :             GDALSetColorEntry(hColorTable, i, &sEntry);
     621        7395 :             if (panPixelCountPerColorTableEntry)
     622         798 :                 panPixelCountPerColorTableEntry->push_back(
     623         798 :                     static_cast<T>(ptr->total));
     624             :         }
     625             :     }
     626             : 
     627          48 : end_and_cleanup:
     628          50 :     CPLFree(pabyRedLine);
     629          50 :     CPLFree(pabyGreenLine);
     630          50 :     CPLFree(pabyBlueLine);
     631             : 
     632             :     // We're done with the boxes now.
     633          50 :     CPLFree(box_list);
     634          50 :     freeboxes = nullptr;
     635          50 :     usedboxes = nullptr;
     636             : 
     637          50 :     if (panHistogram == nullptr)
     638          40 :         CPLFree(histogram);
     639             : 
     640          50 :     return err;
     641             : }
     642             : 
     643             : /************************************************************************/
     644             : /*                            largest_box()                             */
     645             : /************************************************************************/
     646             : 
     647        7365 : static Colorbox *largest_box(Colorbox *usedboxes)
     648             : {
     649        7365 :     Colorbox *b = nullptr;
     650             : 
     651      900336 :     for (Colorbox *p = usedboxes; p != nullptr; p = p->next)
     652             :     {
     653      892971 :         if ((p->rmax > p->rmin || p->gmax > p->gmin || p->bmax > p->bmin) &&
     654      732940 :             (b == nullptr || p->total > b->total))
     655             :         {
     656       66668 :             b = p;
     657             :         }
     658             :     }
     659        7365 :     return b;
     660             : }
     661             : 
     662         743 : static void shrinkboxFromBand(Colorbox *ptr, const GByte *pabyRedBand,
     663             :                               const GByte *pabyGreenBand,
     664             :                               const GByte *pabyBlueBand, GUIntBig nPixels)
     665             : {
     666         743 :     int rmin_new = 255;
     667         743 :     int rmax_new = 0;
     668         743 :     int gmin_new = 255;
     669         743 :     int gmax_new = 0;
     670         743 :     int bmin_new = 255;
     671         743 :     int bmax_new = 0;
     672    35470300 :     for (GUIntBig i = 0; i < nPixels; i++)
     673             :     {
     674    35469500 :         const int iR = pabyRedBand[i];
     675    35469500 :         const int iG = pabyGreenBand[i];
     676    35469500 :         const int iB = pabyBlueBand[i];
     677    35469500 :         if (iR >= ptr->rmin && iR <= ptr->rmax && iG >= ptr->gmin &&
     678     5781920 :             iG <= ptr->gmax && iB >= ptr->bmin && iB <= ptr->bmax)
     679             :         {
     680     2154140 :             if (iR < rmin_new)
     681        3143 :                 rmin_new = iR;
     682     2154140 :             if (iR > rmax_new)
     683        4274 :                 rmax_new = iR;
     684     2154140 :             if (iG < gmin_new)
     685        3392 :                 gmin_new = iG;
     686     2154140 :             if (iG > gmax_new)
     687        4635 :                 gmax_new = iG;
     688     2154140 :             if (iB < bmin_new)
     689        4455 :                 bmin_new = iB;
     690     2154140 :             if (iB > bmax_new)
     691        3314 :                 bmax_new = iB;
     692             :         }
     693             :     }
     694             : 
     695         743 :     CPLAssert(rmin_new >= ptr->rmin && rmin_new <= rmax_new &&
     696             :               rmax_new <= ptr->rmax);
     697         743 :     CPLAssert(gmin_new >= ptr->gmin && gmin_new <= gmax_new &&
     698             :               gmax_new <= ptr->gmax);
     699         743 :     CPLAssert(bmin_new >= ptr->bmin && bmin_new <= bmax_new &&
     700             :               bmax_new <= ptr->bmax);
     701         743 :     ptr->rmin = rmin_new;
     702         743 :     ptr->rmax = rmax_new;
     703         743 :     ptr->gmin = gmin_new;
     704         743 :     ptr->gmax = gmax_new;
     705         743 :     ptr->bmin = bmin_new;
     706         743 :     ptr->bmax = bmax_new;
     707         743 : }
     708             : 
     709        3415 : static void shrinkboxFromHashHistogram(Colorbox *box,
     710             :                                        const HashHistogram *psHashHistogram)
     711             : {
     712        3415 :     if (box->rmax > box->rmin)
     713             :     {
     714        4917 :         for (int ir = box->rmin; ir <= box->rmax; ++ir)
     715             :         {
     716       54728 :             for (int ig = box->gmin; ig <= box->gmax; ++ig)
     717             :             {
     718      770529 :                 for (int ib = box->bmin; ib <= box->bmax; ++ib)
     719             :                 {
     720      720718 :                     if (FindColorCount(psHashHistogram,
     721     1441440 :                                        MAKE_COLOR_CODE(ir, ig, ib)) != 0)
     722             :                     {
     723        3313 :                         box->rmin = ir;
     724        3313 :                         goto have_rmin;
     725             :                     }
     726             :                 }
     727             :             }
     728             :         }
     729             :     }
     730         102 : have_rmin:
     731        3415 :     if (box->rmax > box->rmin)
     732             :     {
     733        6050 :         for (int ir = box->rmax; ir >= box->rmin; --ir)
     734             :         {
     735       65302 :             for (int ig = box->gmin; ig <= box->gmax; ++ig)
     736             :             {
     737     1301360 :                 for (int ib = box->bmin; ib <= box->bmax; ++ib)
     738             :                 {
     739     1242110 :                     if (FindColorCount(psHashHistogram,
     740     2484210 :                                        MAKE_COLOR_CODE(ir, ig, ib)) != 0)
     741             :                     {
     742        3310 :                         box->rmax = ir;
     743        3310 :                         goto have_rmax;
     744             :                     }
     745             :                 }
     746             :             }
     747             :         }
     748             :     }
     749             : 
     750         105 : have_rmax:
     751        3415 :     if (box->gmax > box->gmin)
     752             :     {
     753        7027 :         for (int ig = box->gmin; ig <= box->gmax; ++ig)
     754             :         {
     755       71453 :             for (int ir = box->rmin; ir <= box->rmax; ++ir)
     756             :             {
     757     1233930 :                 for (int ib = box->bmin; ib <= box->bmax; ++ib)
     758             :                 {
     759     1169500 :                     if (FindColorCount(psHashHistogram,
     760     2339010 :                                        MAKE_COLOR_CODE(ir, ig, ib)) != 0)
     761             :                     {
     762        3252 :                         box->gmin = ig;
     763        3252 :                         goto have_gmin;
     764             :                     }
     765             :                 }
     766             :             }
     767             :         }
     768             :     }
     769             : 
     770         163 : have_gmin:
     771        3415 :     if (box->gmax > box->gmin)
     772             :     {
     773        8709 :         for (int ig = box->gmax; ig >= box->gmin; --ig)
     774             :         {
     775       99428 :             for (int ir = box->rmin; ir <= box->rmax; ++ir)
     776             :             {
     777       93961 :                 int ib = box->bmin;
     778     1785560 :                 for (; ib <= box->bmax; ++ib)
     779             :                 {
     780     1694840 :                     if (FindColorCount(psHashHistogram,
     781     3389690 :                                        MAKE_COLOR_CODE(ir, ig, ib)) != 0)
     782             :                     {
     783        3242 :                         box->gmax = ig;
     784        3242 :                         goto have_gmax;
     785             :                     }
     786             :                 }
     787             :             }
     788             :         }
     789             :     }
     790             : 
     791         173 : have_gmax:
     792        3415 :     if (box->bmax > box->bmin)
     793             :     {
     794        5004 :         for (int ib = box->bmin; ib <= box->bmax; ++ib)
     795             :         {
     796       36216 :             for (int ir = box->rmin; ir <= box->rmax; ++ir)
     797             :             {
     798      572282 :                 for (int ig = box->gmin; ig <= box->gmax; ++ig)
     799             :                 {
     800      541070 :                     if (FindColorCount(psHashHistogram,
     801     1082140 :                                        MAKE_COLOR_CODE(ir, ig, ib)) != 0)
     802             :                     {
     803        3312 :                         box->bmin = ib;
     804        3312 :                         goto have_bmin;
     805             :                     }
     806             :                 }
     807             :             }
     808             :         }
     809             :     }
     810             : 
     811         103 : have_bmin:
     812        3415 :     if (box->bmax > box->bmin)
     813             :     {
     814        6196 :         for (int ib = box->bmax; ib >= box->bmin; --ib)
     815             :         {
     816       48883 :             for (int ir = box->rmin; ir <= box->rmax; ++ir)
     817             :             {
     818      675385 :                 for (int ig = box->gmin; ig <= box->gmax; ++ig)
     819             :                 {
     820      632698 :                     if (FindColorCount(psHashHistogram,
     821     1265400 :                                        MAKE_COLOR_CODE(ir, ig, ib)) != 0)
     822             :                     {
     823        3298 :                         box->bmax = ib;
     824        3298 :                         goto have_bmax;
     825             :                     }
     826             :                 }
     827             :             }
     828             :         }
     829             :     }
     830             : 
     831         117 : have_bmax:;
     832        3415 : }
     833             : 
     834             : /************************************************************************/
     835             : /*                              splitbox()                              */
     836             : /************************************************************************/
     837             : template <class T>
     838        7347 : static void splitbox(Colorbox *ptr, const T *histogram,
     839             :                      const HashHistogram *psHashHistogram, int nCLevels,
     840             :                      Colorbox **pfreeboxes, Colorbox **pusedboxes,
     841             :                      GByte *pabyRedBand, GByte *pabyGreenBand,
     842             :                      GByte *pabyBlueBand, T nPixels)
     843             : {
     844        7347 :     T hist2[256] = {};
     845        7347 :     int first = 0;
     846        7347 :     int last = 0;
     847             : 
     848             :     enum
     849             :     {
     850             :         RED,
     851             :         GREEN,
     852             :         BLUE
     853             :     } axis;
     854             : 
     855             :     // See which axis is the largest, do a histogram along that axis.  Split at
     856             :     // median point.  Contract both new boxes to fit points and return.
     857             :     {
     858        7347 :         int i = ptr->rmax - ptr->rmin;
     859        7347 :         if (i >= ptr->gmax - ptr->gmin && i >= ptr->bmax - ptr->bmin)
     860        2466 :             axis = RED;
     861        4881 :         else if (ptr->gmax - ptr->gmin >= ptr->bmax - ptr->bmin)
     862        2385 :             axis = GREEN;
     863             :         else
     864        2496 :             axis = BLUE;
     865             :     }
     866             :     // Get histogram along longest axis.
     867        7347 :     const GUInt32 nIters = (ptr->rmax - ptr->rmin + 1) *
     868        7347 :                            (ptr->gmax - ptr->gmin + 1) *
     869        7347 :                            (ptr->bmax - ptr->bmin + 1);
     870             : 
     871        7347 :     switch (axis)
     872             :     {
     873        2466 :         case RED:
     874             :         {
     875        2466 :             if (nPixels != 0 && nIters > nPixels)
     876             :             {
     877         229 :                 const int rmin = ptr->rmin;
     878         229 :                 const int rmax = ptr->rmax;
     879         229 :                 const int gmin = ptr->gmin;
     880         229 :                 const int gmax = ptr->gmax;
     881         229 :                 const int bmin = ptr->bmin;
     882         229 :                 const int bmax = ptr->bmax;
     883    11014200 :                 for (T iPixel = 0; iPixel < nPixels; iPixel++)
     884             :                 {
     885    11014000 :                     int iR = pabyRedBand[iPixel];
     886    11014000 :                     int iG = pabyGreenBand[iPixel];
     887    11014000 :                     int iB = pabyBlueBand[iPixel];
     888    11014000 :                     if (iR >= rmin && iR <= rmax && iG >= gmin && iG <= gmax &&
     889     1379080 :                         iB >= bmin && iB <= bmax)
     890             :                     {
     891      893690 :                         hist2[iR]++;
     892             :                     }
     893         229 :                 }
     894             :             }
     895        2237 :             else if (psHashHistogram)
     896             :             {
     897         472 :                 T *histp = &hist2[ptr->rmin];
     898       11243 :                 for (int ir = ptr->rmin; ir <= ptr->rmax; ++ir)
     899             :                 {
     900       10771 :                     *histp = 0;
     901      226089 :                     for (int ig = ptr->gmin; ig <= ptr->gmax; ++ig)
     902             :                     {
     903     3704290 :                         for (int ib = ptr->bmin; ib <= ptr->bmax; ++ib)
     904             :                         {
     905     3488980 :                             *histp += FindColorCount(
     906     3488980 :                                 psHashHistogram, MAKE_COLOR_CODE(ir, ig, ib));
     907             :                         }
     908             :                     }
     909       10771 :                     histp++;
     910             :                 }
     911             :             }
     912             :             else
     913             :             {
     914        1765 :                 T *histp = &hist2[ptr->rmin];
     915       16617 :                 for (int ir = ptr->rmin; ir <= ptr->rmax; ++ir)
     916             :                 {
     917       14852 :                     *histp = 0;
     918      188612 :                     for (int ig = ptr->gmin; ig <= ptr->gmax; ++ig)
     919             :                     {
     920             :                         const T *iptr =
     921      173760 :                             HISTOGRAM(histogram, nCLevels, ir, ig, ptr->bmin);
     922     3151039 :                         for (int ib = ptr->bmin; ib <= ptr->bmax; ++ib)
     923     2977279 :                             *histp += *iptr++;
     924             :                     }
     925       14852 :                     histp++;
     926             :                 }
     927             :             }
     928        2466 :             first = ptr->rmin;
     929        2466 :             last = ptr->rmax;
     930        2466 :             break;
     931             :         }
     932        2385 :         case GREEN:
     933             :         {
     934        2385 :             if (nPixels != 0 && nIters > nPixels)
     935             :             {
     936         153 :                 const int rmin = ptr->rmin;
     937         153 :                 const int rmax = ptr->rmax;
     938         153 :                 const int gmin = ptr->gmin;
     939         153 :                 const int gmax = ptr->gmax;
     940         153 :                 const int bmin = ptr->bmin;
     941         153 :                 const int bmax = ptr->bmax;
     942     7442490 :                 for (T iPixel = 0; iPixel < nPixels; iPixel++)
     943             :                 {
     944     7442340 :                     const int iR = pabyRedBand[iPixel];
     945     7442340 :                     const int iG = pabyGreenBand[iPixel];
     946     7442340 :                     const int iB = pabyBlueBand[iPixel];
     947     7442340 :                     if (iR >= rmin && iR <= rmax && iG >= gmin && iG <= gmax &&
     948     1575360 :                         iB >= bmin && iB <= bmax)
     949             :                     {
     950      948439 :                         hist2[iG]++;
     951             :                     }
     952         153 :                 }
     953             :             }
     954        2232 :             else if (psHashHistogram)
     955             :             {
     956         476 :                 T *histp = &hist2[ptr->gmin];
     957       12600 :                 for (int ig = ptr->gmin; ig <= ptr->gmax; ++ig)
     958             :                 {
     959       12124 :                     *histp = 0;
     960      183557 :                     for (int ir = ptr->rmin; ir <= ptr->rmax; ++ir)
     961             :                     {
     962     3551710 :                         for (int ib = ptr->bmin; ib <= ptr->bmax; ++ib)
     963             :                         {
     964     3380280 :                             *histp += FindColorCount(
     965     3380280 :                                 psHashHistogram, MAKE_COLOR_CODE(ir, ig, ib));
     966             :                         }
     967             :                     }
     968       12124 :                     histp++;
     969             :                 }
     970             :             }
     971             :             else
     972             :             {
     973        1756 :                 T *histp = &hist2[ptr->gmin];
     974       13924 :                 for (int ig = ptr->gmin; ig <= ptr->gmax; ++ig)
     975             :                 {
     976       12168 :                     *histp = 0;
     977      110326 :                     for (int ir = ptr->rmin; ir <= ptr->rmax; ++ir)
     978             :                     {
     979             :                         const T *iptr =
     980       98158 :                             HISTOGRAM(histogram, nCLevels, ir, ig, ptr->bmin);
     981     1913429 :                         for (int ib = ptr->bmin; ib <= ptr->bmax; ++ib)
     982     1815270 :                             *histp += *iptr++;
     983             :                     }
     984       12168 :                     histp++;
     985             :                 }
     986             :             }
     987        2385 :             first = ptr->gmin;
     988        2385 :             last = ptr->gmax;
     989        2385 :             break;
     990             :         }
     991        2496 :         case BLUE:
     992             :         {
     993        2496 :             if (nPixels != 0 && nIters > nPixels)
     994             :             {
     995         199 :                 const int rmin = ptr->rmin;
     996         199 :                 const int rmax = ptr->rmax;
     997         199 :                 const int gmin = ptr->gmin;
     998         199 :                 const int gmax = ptr->gmax;
     999         199 :                 const int bmin = ptr->bmin;
    1000         199 :                 const int bmax = ptr->bmax;
    1001     9614150 :                 for (T iPixel = 0; iPixel < nPixels; iPixel++)
    1002             :                 {
    1003     9613950 :                     const int iR = pabyRedBand[iPixel];
    1004     9613950 :                     const int iG = pabyGreenBand[iPixel];
    1005     9613950 :                     const int iB = pabyBlueBand[iPixel];
    1006     9613950 :                     if (iR >= rmin && iR <= rmax && iG >= gmin && iG <= gmax &&
    1007      832271 :                         iB >= bmin && iB <= bmax)
    1008             :                     {
    1009      649243 :                         hist2[iB]++;
    1010             :                     }
    1011         199 :                 }
    1012             :             }
    1013        2297 :             else if (psHashHistogram)
    1014             :             {
    1015         573 :                 T *histp = &hist2[ptr->bmin];
    1016       16540 :                 for (int ib = ptr->bmin; ib <= ptr->bmax; ++ib)
    1017             :                 {
    1018       15967 :                     *histp = 0;
    1019      252363 :                     for (int ir = ptr->rmin; ir <= ptr->rmax; ++ir)
    1020             :                     {
    1021     4915820 :                         for (int ig = ptr->gmin; ig <= ptr->gmax; ++ig)
    1022             :                         {
    1023     4679430 :                             *histp += FindColorCount(
    1024     4679430 :                                 psHashHistogram, MAKE_COLOR_CODE(ir, ig, ib));
    1025             :                         }
    1026             :                     }
    1027       15967 :                     histp++;
    1028             :                 }
    1029             :             }
    1030             :             else
    1031             :             {
    1032        1724 :                 T *histp = &hist2[ptr->bmin];
    1033       15464 :                 for (int ib = ptr->bmin; ib <= ptr->bmax; ++ib)
    1034             :                 {
    1035       13740 :                     *histp = 0;
    1036      136611 :                     for (int ir = ptr->rmin; ir <= ptr->rmax; ++ir)
    1037             :                     {
    1038             :                         const T *iptr =
    1039      122871 :                             HISTOGRAM(histogram, nCLevels, ir, ptr->gmin, ib);
    1040     2358380 :                         for (int ig = ptr->gmin; ig <= ptr->gmax; ++ig)
    1041             :                         {
    1042     2235514 :                             *histp += *iptr;
    1043     2235514 :                             iptr += nCLevels;
    1044             :                         }
    1045             :                     }
    1046       13740 :                     histp++;
    1047             :                 }
    1048             :             }
    1049        2496 :             first = ptr->bmin;
    1050        2496 :             last = ptr->bmax;
    1051        2496 :             break;
    1052             :         }
    1053             :     }
    1054             :     // Find median point.
    1055        7347 :     T *histp = &hist2[first];
    1056        7347 :     int i = first;  // TODO(schwehr): Rename i.
    1057             :     {
    1058        7347 :         T sum = 0;
    1059        7347 :         T sum2 = static_cast<T>(ptr->total / 2);
    1060       57172 :         for (; i <= last && (sum += *histp++) < sum2; ++i)
    1061             :         {
    1062             :         }
    1063             :     }
    1064        7347 :     if (i == first)
    1065        1799 :         i++;
    1066             : 
    1067             :     // Create new box, re-allocate points.
    1068        7347 :     Colorbox *new_cb = *pfreeboxes;
    1069        7347 :     *pfreeboxes = new_cb->next;
    1070        7347 :     if (*pfreeboxes)
    1071        7317 :         (*pfreeboxes)->prev = nullptr;
    1072        7347 :     if (*pusedboxes)
    1073        7347 :         (*pusedboxes)->prev = new_cb;
    1074        7347 :     new_cb->next = *pusedboxes;
    1075        7347 :     *pusedboxes = new_cb;
    1076             : 
    1077        7347 :     histp = &hist2[first];
    1078             :     {
    1079        7347 :         T sum1 = 0;
    1080       58971 :         for (int j = first; j < i; j++)
    1081       51624 :             sum1 += *histp++;
    1082        7347 :         T sum2 = 0;
    1083       91652 :         for (int j = i; j <= last; j++)
    1084       84305 :             sum2 += *histp++;
    1085        7347 :         new_cb->total = sum1;
    1086        7347 :         ptr->total = sum2;
    1087             :     }
    1088        7347 :     new_cb->rmin = ptr->rmin;
    1089        7347 :     new_cb->rmax = ptr->rmax;
    1090        7347 :     new_cb->gmin = ptr->gmin;
    1091        7347 :     new_cb->gmax = ptr->gmax;
    1092        7347 :     new_cb->bmin = ptr->bmin;
    1093        7347 :     new_cb->bmax = ptr->bmax;
    1094        7347 :     switch (axis)
    1095             :     {
    1096        2466 :         case RED:
    1097        2466 :             new_cb->rmax = i - 1;
    1098        2466 :             ptr->rmin = i;
    1099        2466 :             break;
    1100        2385 :         case GREEN:
    1101        2385 :             new_cb->gmax = i - 1;
    1102        2385 :             ptr->gmin = i;
    1103        2385 :             break;
    1104        2496 :         case BLUE:
    1105        2496 :             new_cb->bmax = i - 1;
    1106        2496 :             ptr->bmin = i;
    1107        2496 :             break;
    1108             :     }
    1109             : 
    1110        7347 :     if (nPixels != 0 &&
    1111        2295 :         static_cast<T>(new_cb->rmax - new_cb->rmin + 1) *
    1112        2295 :                 static_cast<T>(new_cb->gmax - new_cb->gmin + 1) *
    1113        2295 :                 static_cast<T>(new_cb->bmax - new_cb->bmin + 1) >
    1114             :             nPixels)
    1115             :     {
    1116         325 :         shrinkboxFromBand(new_cb, pabyRedBand, pabyGreenBand, pabyBlueBand,
    1117             :                           nPixels);
    1118             :     }
    1119        7022 :     else if (psHashHistogram != nullptr)
    1120             :     {
    1121        1748 :         shrinkboxFromHashHistogram(new_cb, psHashHistogram);
    1122             :     }
    1123             :     else
    1124             :     {
    1125        5274 :         shrinkbox(new_cb, histogram, nCLevels);
    1126             :     }
    1127             : 
    1128        7347 :     if (nPixels != 0 && static_cast<T>(ptr->rmax - ptr->rmin + 1) *
    1129        2295 :                                 static_cast<T>(ptr->gmax - ptr->gmin + 1) *
    1130        2295 :                                 static_cast<T>(ptr->bmax - ptr->bmin + 1) >
    1131             :                             nPixels)
    1132             :     {
    1133         418 :         shrinkboxFromBand(ptr, pabyRedBand, pabyGreenBand, pabyBlueBand,
    1134             :                           nPixels);
    1135             :     }
    1136        6929 :     else if (psHashHistogram != nullptr)
    1137             :     {
    1138        1667 :         shrinkboxFromHashHistogram(ptr, psHashHistogram);
    1139             :     }
    1140             :     else
    1141             :     {
    1142        5262 :         shrinkbox(ptr, histogram, nCLevels);
    1143             :     }
    1144        7347 : }
    1145             : 
    1146             : /************************************************************************/
    1147             : /*                             shrinkbox()                              */
    1148             : /************************************************************************/
    1149             : template <class T>
    1150       10536 : static void shrinkbox(Colorbox *box, const T *histogram, int nCLevels)
    1151             : {
    1152       10536 :     if (box->rmax > box->rmin)
    1153             :     {
    1154        7042 :         for (int ir = box->rmin; ir <= box->rmax; ++ir)
    1155             :         {
    1156       20590 :             for (int ig = box->gmin; ig <= box->gmax; ++ig)
    1157             :             {
    1158             :                 const T *histp =
    1159       19589 :                     HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin);
    1160      244021 :                 for (int ib = box->bmin; ib <= box->bmax; ++ib)
    1161             :                 {
    1162      230473 :                     if (*histp++ != 0)
    1163             :                     {
    1164        6041 :                         box->rmin = ir;
    1165        6041 :                         goto have_rmin;
    1166             :                     }
    1167             :                 }
    1168             :             }
    1169             :         }
    1170             :     }
    1171        4495 : have_rmin:
    1172       10536 :     if (box->rmax > box->rmin)
    1173             :     {
    1174        7820 :         for (int ir = box->rmax; ir >= box->rmin; --ir)
    1175             :         {
    1176       34920 :             for (int ig = box->gmin; ig <= box->gmax; ++ig)
    1177             :             {
    1178             :                 const T *histp =
    1179       33087 :                     HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin);
    1180      463269 :                 for (int ib = box->bmin; ib <= box->bmax; ++ib)
    1181             :                 {
    1182      436169 :                     if (*histp++ != 0)
    1183             :                     {
    1184        5987 :                         box->rmax = ir;
    1185        5987 :                         goto have_rmax;
    1186             :                     }
    1187             :                 }
    1188             :             }
    1189             :         }
    1190             :     }
    1191             : 
    1192        4549 : have_rmax:
    1193       10536 :     if (box->gmax > box->gmin)
    1194             :     {
    1195        9144 :         for (int ig = box->gmin; ig <= box->gmax; ++ig)
    1196             :         {
    1197       29845 :             for (int ir = box->rmin; ir <= box->rmax; ++ir)
    1198             :             {
    1199             :                 const T *histp =
    1200       27743 :                     HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin);
    1201      401339 :                 for (int ib = box->bmin; ib <= box->bmax; ++ib)
    1202             :                 {
    1203      380620 :                     if (*histp++ != 0)
    1204             :                     {
    1205        7024 :                         box->gmin = ig;
    1206        7024 :                         goto have_gmin;
    1207             :                     }
    1208             :                 }
    1209             :             }
    1210             :         }
    1211             :     }
    1212             : 
    1213        3494 : have_gmin:
    1214       10536 :     if (box->gmax > box->gmin)
    1215             :     {
    1216       11393 :         for (int ig = box->gmax; ig >= box->gmin; --ig)
    1217             :         {
    1218       51896 :             for (int ir = box->rmin; ir <= box->rmax; ++ir)
    1219             :             {
    1220             :                 const T *histp =
    1221       47405 :                     HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin);
    1222      666264 :                 for (int ib = box->bmin; ib <= box->bmax; ++ib)
    1223             :                 {
    1224      625743 :                     if (*histp++ != 0)
    1225             :                     {
    1226        6884 :                         box->gmax = ig;
    1227        6884 :                         goto have_gmax;
    1228             :                     }
    1229             :                 }
    1230             :             }
    1231             :         }
    1232             :     }
    1233             : 
    1234        3634 : have_gmax:
    1235       10536 :     if (box->bmax > box->bmin)
    1236             :     {
    1237        7845 :         for (int ib = box->bmin; ib <= box->bmax; ++ib)
    1238             :         {
    1239       18296 :             for (int ir = box->rmin; ir <= box->rmax; ++ir)
    1240             :             {
    1241             :                 const T *histp =
    1242       17082 :                     HISTOGRAM(histogram, nCLevels, ir, box->gmin, ib);
    1243      166256 :                 for (int ig = box->gmin; ig <= box->gmax; ++ig)
    1244             :                 {
    1245      155789 :                     if (*histp != 0)
    1246             :                     {
    1247        6615 :                         box->bmin = ib;
    1248        6615 :                         goto have_bmin;
    1249             :                     }
    1250      149174 :                     histp += nCLevels;
    1251             :                 }
    1252             :             }
    1253             :         }
    1254             :     }
    1255             : 
    1256        3905 : have_bmin:
    1257       10536 :     if (box->bmax > box->bmin)
    1258             :     {
    1259       10612 :         for (int ib = box->bmax; ib >= box->bmin; --ib)
    1260             :         {
    1261       40412 :             for (int ir = box->rmin; ir <= box->rmax; ++ir)
    1262             :             {
    1263             :                 const T *histp =
    1264       36299 :                     HISTOGRAM(histogram, nCLevels, ir, box->gmin, ib);
    1265      379858 :                 for (int ig = box->gmin; ig <= box->gmax; ++ig)
    1266             :                 {
    1267      350042 :                     if (*histp != 0)
    1268             :                     {
    1269        6483 :                         box->bmax = ib;
    1270        6483 :                         goto have_bmax;
    1271             :                     }
    1272      343559 :                     histp += nCLevels;
    1273             :                 }
    1274             :             }
    1275             :         }
    1276             :     }
    1277             : 
    1278        4037 : have_bmax:;
    1279       10536 : }
    1280             : 
    1281             : // Explicitly instantiate template functions.
    1282             : template int GDALComputeMedianCutPCTInternal<GUInt32>(
    1283             :     GDALRasterBandH hRed, GDALRasterBandH hGreen, GDALRasterBandH hBlue,
    1284             :     GByte *pabyRedBand, GByte *pabyGreenBand, GByte *pabyBlueBand,
    1285             :     int (*pfnIncludePixel)(int, int, void *), int nColors, int nBits,
    1286             :     GUInt32 *panHistogram, GDALColorTableH hColorTable,
    1287             :     GDALProgressFunc pfnProgress, void *pProgressArg,
    1288             :     std::vector<GUInt32> *panPixelCountPerColorTableEntry);
    1289             : 
    1290             : template int GDALComputeMedianCutPCTInternal<GUIntBig>(
    1291             :     GDALRasterBandH hRed, GDALRasterBandH hGreen, GDALRasterBandH hBlue,
    1292             :     GByte *pabyRedBand, GByte *pabyGreenBand, GByte *pabyBlueBand,
    1293             :     int (*pfnIncludePixel)(int, int, void *), int nColors, int nBits,
    1294             :     GUIntBig *panHistogram, GDALColorTableH hColorTable,
    1295             :     GDALProgressFunc pfnProgress, void *pProgressArg,
    1296             :     std::vector<GUIntBig> *panPixelCountPerColorTableEntry);
    1297             : 
    1298           0 : int GDALComputeMedianCutPCT(
    1299             :     GDALRasterBandH hRed, GDALRasterBandH hGreen, GDALRasterBandH hBlue,
    1300             :     GByte *pabyRedBand, GByte *pabyGreenBand, GByte *pabyBlueBand,
    1301             :     int (*pfnIncludePixel)(int, int, void *), int nColors, int nBits,
    1302             :     GUInt32 *panHistogram, GDALColorTableH hColorTable,
    1303             :     GDALProgressFunc pfnProgress, void *pProgressArg,
    1304             :     std::vector<GUInt32> *panPixelCountPerColorTableEntry)
    1305             : {
    1306           0 :     return GDALComputeMedianCutPCTInternal<GUInt32>(
    1307             :         hRed, hGreen, hBlue, pabyRedBand, pabyGreenBand, pabyBlueBand,
    1308             :         pfnIncludePixel, nColors, nBits, panHistogram, hColorTable, pfnProgress,
    1309           0 :         pProgressArg, panPixelCountPerColorTableEntry);
    1310             : }
    1311             : 
    1312          21 : int GDALComputeMedianCutPCT(
    1313             :     GDALRasterBandH hRed, GDALRasterBandH hGreen, GDALRasterBandH hBlue,
    1314             :     GByte *pabyRedBand, GByte *pabyGreenBand, GByte *pabyBlueBand,
    1315             :     int (*pfnIncludePixel)(int, int, void *), int nColors, int nBits,
    1316             :     GUIntBig *panHistogram, GDALColorTableH hColorTable,
    1317             :     GDALProgressFunc pfnProgress, void *pProgressArg,
    1318             :     std::vector<GUIntBig> *panPixelCountPerColorTableEntry)
    1319             : {
    1320          21 :     return GDALComputeMedianCutPCTInternal<GUIntBig>(
    1321             :         hRed, hGreen, hBlue, pabyRedBand, pabyGreenBand, pabyBlueBand,
    1322             :         pfnIncludePixel, nColors, nBits, panHistogram, hColorTable, pfnProgress,
    1323          21 :         pProgressArg, panPixelCountPerColorTableEntry);
    1324             : }

Generated by: LCOV version 1.14