Coverage Report

Created: 2026-03-30 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/alg/gdalmediancut.cpp
Line
Count
Source
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
template <typename T> static T *HISTOGRAM(T *h, int n, int r, int g, int b)
42
0
{
43
0
    const int index = (r * n + g) * n + b;
44
0
    return &h[index];
45
0
}
Unexecuted instantiation: gdalmediancut.cpp:unsigned int* HISTOGRAM<unsigned int>(unsigned int*, int, int, int, int)
Unexecuted instantiation: gdalmediancut.cpp:unsigned int const* HISTOGRAM<unsigned int const>(unsigned int const*, int, int, int, int)
Unexecuted instantiation: gdalmediancut.cpp:unsigned long long* HISTOGRAM<unsigned long long>(unsigned long long*, int, int, int, int)
Unexecuted instantiation: gdalmediancut.cpp:unsigned long long const* HISTOGRAM<unsigned long long const>(unsigned long long const*, int, int, int, int)
46
47
#ifndef MAKE_COLOR_CODE_defined
48
#define MAKE_COLOR_CODE_defined
49
50
static int MAKE_COLOR_CODE(int r, int g, int b)
51
0
{
52
0
    return r | (g << 8) | (b << 16);
53
0
}
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
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
0
{
140
0
    VALIDATE_POINTER1(hRed, "GDALComputeMedianCutPCT", CE_Failure);
141
0
    const int nXSize = GDALGetRasterBandXSize(hRed);
142
0
    const int nYSize = GDALGetRasterBandYSize(hRed);
143
0
    if (nYSize == 0)
144
0
        return CE_Failure;
145
0
    if (static_cast<GUInt32>(nXSize) <
146
0
        std::numeric_limits<GUInt32>::max() / static_cast<GUInt32>(nYSize))
147
0
    {
148
0
        return GDALComputeMedianCutPCTInternal(
149
0
            hRed, hGreen, hBlue, nullptr, nullptr, nullptr, pfnIncludePixel,
150
0
            nColors, 5, static_cast<GUInt32 *>(nullptr), hColorTable,
151
0
            pfnProgress, pProgressArg);
152
0
    }
153
0
    else
154
0
    {
155
0
        return GDALComputeMedianCutPCTInternal(
156
0
            hRed, hGreen, hBlue, nullptr, nullptr, nullptr, pfnIncludePixel,
157
0
            nColors, 5, static_cast<GUIntBig *>(nullptr), hColorTable,
158
0
            pfnProgress, pProgressArg);
159
0
    }
160
0
}
161
162
#ifndef IsColorCodeSet_defined
163
#define IsColorCodeSet_defined
164
165
static inline bool IsColorCodeSet(GUInt32 nColorCode)
166
0
{
167
0
    return (nColorCode >> 31) == 0;
168
0
}
169
#endif
170
171
static inline int FindColorCount(const HashHistogram *psHashHistogram,
172
                                 GUInt32 nColorCode)
173
0
{
174
175
0
    GUInt32 nIdx = nColorCode % PRIME_FOR_65536;
176
0
    while (true)
177
0
    {
178
0
        if (!IsColorCodeSet(psHashHistogram[nIdx].nColorCode))
179
0
        {
180
0
            return 0;
181
0
        }
182
0
        if (psHashHistogram[nIdx].nColorCode == nColorCode)
183
0
        {
184
0
            return psHashHistogram[nIdx].nCount;
185
0
        }
186
0
        if (!IsColorCodeSet(psHashHistogram[nIdx].nColorCode2))
187
0
        {
188
0
            return 0;
189
0
        }
190
0
        if (psHashHistogram[nIdx].nColorCode2 == nColorCode)
191
0
        {
192
0
            return psHashHistogram[nIdx].nCount2;
193
0
        }
194
0
        if (!IsColorCodeSet(psHashHistogram[nIdx].nColorCode3))
195
0
        {
196
0
            return 0;
197
0
        }
198
0
        if (psHashHistogram[nIdx].nColorCode3 == nColorCode)
199
0
        {
200
0
            return psHashHistogram[nIdx].nCount3;
201
0
        }
202
203
0
        do
204
0
        {
205
0
            nIdx += 257;
206
0
            if (nIdx >= PRIME_FOR_65536)
207
0
                nIdx -= PRIME_FOR_65536;
208
0
        } while (IsColorCodeSet(psHashHistogram[nIdx].nColorCode) &&
209
0
                 psHashHistogram[nIdx].nColorCode != nColorCode &&
210
0
                 IsColorCodeSet(psHashHistogram[nIdx].nColorCode2) &&
211
0
                 psHashHistogram[nIdx].nColorCode2 != nColorCode &&
212
0
                 IsColorCodeSet(psHashHistogram[nIdx].nColorCode3) &&
213
0
                 psHashHistogram[nIdx].nColorCode3 != nColorCode);
214
0
    }
215
0
}
216
217
static inline int *FindAndInsertColorCount(HashHistogram *psHashHistogram,
218
                                           GUInt32 nColorCode)
219
0
{
220
0
    GUInt32 nIdx = nColorCode % PRIME_FOR_65536;
221
0
    while (true)
222
0
    {
223
0
        if (psHashHistogram[nIdx].nColorCode == nColorCode)
224
0
        {
225
0
            return &(psHashHistogram[nIdx].nCount);
226
0
        }
227
0
        if (!IsColorCodeSet(psHashHistogram[nIdx].nColorCode))
228
0
        {
229
0
            psHashHistogram[nIdx].nColorCode = nColorCode;
230
0
            psHashHistogram[nIdx].nCount = 0;
231
0
            return &(psHashHistogram[nIdx].nCount);
232
0
        }
233
0
        if (psHashHistogram[nIdx].nColorCode2 == nColorCode)
234
0
        {
235
0
            return &(psHashHistogram[nIdx].nCount2);
236
0
        }
237
0
        if (!IsColorCodeSet(psHashHistogram[nIdx].nColorCode2))
238
0
        {
239
0
            psHashHistogram[nIdx].nColorCode2 = nColorCode;
240
0
            psHashHistogram[nIdx].nCount2 = 0;
241
0
            return &(psHashHistogram[nIdx].nCount2);
242
0
        }
243
0
        if (psHashHistogram[nIdx].nColorCode3 == nColorCode)
244
0
        {
245
0
            return &(psHashHistogram[nIdx].nCount3);
246
0
        }
247
0
        if (!IsColorCodeSet(psHashHistogram[nIdx].nColorCode3))
248
0
        {
249
0
            psHashHistogram[nIdx].nColorCode3 = nColorCode;
250
0
            psHashHistogram[nIdx].nCount3 = 0;
251
0
            return &(psHashHistogram[nIdx].nCount3);
252
0
        }
253
254
0
        do
255
0
        {
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
0
    }
266
0
}
267
268
template <class T>
269
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
0
{
278
0
    VALIDATE_POINTER1(hRed, "GDALComputeMedianCutPCT", CE_Failure);
279
0
    VALIDATE_POINTER1(hGreen, "GDALComputeMedianCutPCT", CE_Failure);
280
0
    VALIDATE_POINTER1(hBlue, "GDALComputeMedianCutPCT", CE_Failure);
281
282
0
    CPLErr err = CE_None;
283
284
    /* -------------------------------------------------------------------- */
285
    /*      Validate parameters.                                            */
286
    /* -------------------------------------------------------------------- */
287
0
    const int nXSize = GDALGetRasterBandXSize(hRed);
288
0
    const int nYSize = GDALGetRasterBandYSize(hRed);
289
290
0
    if (GDALGetRasterBandXSize(hGreen) != nXSize ||
291
0
        GDALGetRasterBandYSize(hGreen) != nYSize ||
292
0
        GDALGetRasterBandXSize(hBlue) != nXSize ||
293
0
        GDALGetRasterBandYSize(hBlue) != nYSize)
294
0
    {
295
0
        CPLError(CE_Failure, CPLE_IllegalArg,
296
0
                 "Green or blue band doesn't match size of red band.");
297
298
0
        return CE_Failure;
299
0
    }
300
301
0
    if (pfnIncludePixel != nullptr)
302
0
    {
303
0
        CPLError(CE_Failure, CPLE_IllegalArg,
304
0
                 "GDALComputeMedianCutPCT() doesn't currently support "
305
0
                 "pfnIncludePixel function.");
306
307
0
        return CE_Failure;
308
0
    }
309
310
0
    if (nColors <= 0)
311
0
    {
312
0
        CPLError(CE_Failure, CPLE_IllegalArg,
313
0
                 "GDALComputeMedianCutPCT(): "
314
0
                 "nColors must be strictly greater than 1.");
315
316
0
        return CE_Failure;
317
0
    }
318
319
0
    if (nColors > 256)
320
0
    {
321
0
        CPLError(CE_Failure, CPLE_IllegalArg,
322
0
                 "GDALComputeMedianCutPCT(): "
323
0
                 "nColors must be lesser than or equal to 256.");
324
325
0
        return CE_Failure;
326
0
    }
327
328
0
    if (pfnProgress == nullptr)
329
0
        pfnProgress = GDALDummyProgress;
330
331
0
    int nSrcNoData = -1;
332
0
    GDALRasterBandH hMaskBand = nullptr;
333
0
    int bSrcHasNoDataR = FALSE;
334
0
    const double dfSrcNoDataR = GDALGetRasterNoDataValue(hRed, &bSrcHasNoDataR);
335
0
    int bSrcHasNoDataG = FALSE;
336
0
    const double dfSrcNoDataG =
337
0
        GDALGetRasterNoDataValue(hGreen, &bSrcHasNoDataG);
338
0
    int bSrcHasNoDataB = FALSE;
339
0
    const double dfSrcNoDataB =
340
0
        GDALGetRasterNoDataValue(hBlue, &bSrcHasNoDataB);
341
0
    if (bSrcHasNoDataR && bSrcHasNoDataG && bSrcHasNoDataB &&
342
0
        dfSrcNoDataR == dfSrcNoDataG && dfSrcNoDataR == dfSrcNoDataB &&
343
0
        dfSrcNoDataR >= 0 && dfSrcNoDataR <= 255 &&
344
0
        std::round(dfSrcNoDataR) == dfSrcNoDataR)
345
0
    {
346
0
        nSrcNoData = static_cast<int>(dfSrcNoDataR);
347
0
    }
348
0
    else
349
0
    {
350
0
        const int nMaskFlags = GDALGetMaskFlags(hRed);
351
0
        if ((nMaskFlags & GMF_PER_DATASET))
352
0
            hMaskBand = GDALGetMaskBand(hRed);
353
0
    }
354
355
    /* ==================================================================== */
356
    /*      STEP 1: create empty boxes.                                     */
357
    /* ==================================================================== */
358
0
    if (static_cast<GUInt32>(nXSize) >
359
0
        cpl::NumericLimits<T>::max() / static_cast<GUInt32>(nYSize))
360
0
    {
361
0
        CPLError(CE_Warning, CPLE_AppDefined,
362
0
                 "GDALComputeMedianCutPCTInternal() not called "
363
0
                 "with large enough type");
364
0
    }
365
366
0
    T nPixels = 0;
367
0
    if (nBits == 8 && pabyRedBand != nullptr && pabyGreenBand != nullptr &&
368
0
        pabyBlueBand != nullptr &&
369
0
        static_cast<GUInt32>(nXSize) <=
370
0
            cpl::NumericLimits<T>::max() / static_cast<GUInt32>(nYSize))
371
0
    {
372
0
        nPixels = static_cast<T>(nXSize) * static_cast<T>(nYSize);
373
0
    }
374
375
0
    const int nCLevels = 1 << nBits;
376
0
    const int nCLevelsCube = nCLevels * nCLevels * nCLevels;
377
0
    T *histogram = nullptr;
378
0
    HashHistogram *psHashHistogram = nullptr;
379
0
    if (panHistogram)
380
0
    {
381
0
        if (nBits == 8 && static_cast<GUIntBig>(nXSize) * nYSize <= 65536)
382
0
        {
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
0
            histogram = nullptr;
387
0
            psHashHistogram = reinterpret_cast<HashHistogram *>(panHistogram);
388
0
            memset(psHashHistogram, 0xFF,
389
0
                   sizeof(HashHistogram) * PRIME_FOR_65536);
390
0
        }
391
0
        else
392
0
        {
393
0
            histogram = panHistogram;
394
0
            memset(histogram, 0, nCLevelsCube * sizeof(T));
395
0
        }
396
0
    }
397
0
    else
398
0
    {
399
0
        histogram =
400
0
            static_cast<T *>(VSI_CALLOC_VERBOSE(nCLevelsCube, sizeof(T)));
401
0
        if (histogram == nullptr)
402
0
        {
403
0
            return CE_Failure;
404
0
        }
405
0
    }
406
0
    Colorbox *box_list =
407
0
        static_cast<Colorbox *>(CPLMalloc(nColors * sizeof(Colorbox)));
408
0
    Colorbox *freeboxes = box_list;
409
0
    freeboxes[0].next = &freeboxes[1];
410
0
    freeboxes[0].prev = nullptr;
411
0
    for (int i = 1; i < nColors - 1; ++i)
412
0
    {
413
0
        freeboxes[i].next = &freeboxes[i + 1];
414
0
        freeboxes[i].prev = &freeboxes[i - 1];
415
0
    }
416
0
    freeboxes[nColors - 1].next = nullptr;
417
0
    freeboxes[nColors - 1].prev = &freeboxes[nColors - 2];
418
419
    /* ==================================================================== */
420
    /*      Build histogram.                                                */
421
    /* ==================================================================== */
422
423
    /* -------------------------------------------------------------------- */
424
    /*      Initialize the box datastructures.                              */
425
    /* -------------------------------------------------------------------- */
426
0
    Colorbox *freeboxes_before = freeboxes;
427
0
    freeboxes = freeboxes_before->next;
428
0
    if (freeboxes)
429
0
        freeboxes->prev = nullptr;
430
431
0
    Colorbox *usedboxes = freeboxes_before;
432
0
    usedboxes->next = nullptr;
433
0
    usedboxes->rmin = 999;
434
0
    usedboxes->gmin = 999;
435
0
    usedboxes->bmin = 999;
436
0
    usedboxes->rmax = -1;
437
0
    usedboxes->gmax = -1;
438
0
    usedboxes->bmax = -1;
439
0
    usedboxes->total =
440
0
        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
0
    const int nColorShift = 8 - nBits;
448
0
    int nColorCounter = 0;
449
0
    GByte anRed[256] = {};
450
0
    GByte anGreen[256] = {};
451
0
    GByte anBlue[256] = {};
452
453
0
    GByte *pabyRedLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
454
0
    GByte *pabyGreenLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
455
0
    GByte *pabyBlueLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
456
0
    std::unique_ptr<GByte, VSIFreeReleaser> pabyMask;
457
0
    if (hMaskBand)
458
0
        pabyMask.reset(static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize)));
459
460
0
    if (pabyRedLine == nullptr || pabyGreenLine == nullptr ||
461
0
        pabyBlueLine == nullptr || (hMaskBand && !pabyMask))
462
0
    {
463
0
        err = CE_Failure;
464
0
        goto end_and_cleanup;
465
0
    }
466
467
0
    for (int iLine = 0; iLine < nYSize; iLine++)
468
0
    {
469
0
        if (!pfnProgress(iLine / static_cast<double>(nYSize),
470
0
                         "Generating Histogram", pProgressArg))
471
0
        {
472
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User Terminated");
473
0
            err = CE_Failure;
474
0
            goto end_and_cleanup;
475
0
        }
476
477
0
        err = GDALRasterIO(hRed, GF_Read, 0, iLine, nXSize, 1, pabyRedLine,
478
0
                           nXSize, 1, GDT_UInt8, 0, 0);
479
0
        if (err == CE_None)
480
0
            err = GDALRasterIO(hGreen, GF_Read, 0, iLine, nXSize, 1,
481
0
                               pabyGreenLine, nXSize, 1, GDT_UInt8, 0, 0);
482
0
        if (err == CE_None)
483
0
            err = GDALRasterIO(hBlue, GF_Read, 0, iLine, nXSize, 1,
484
0
                               pabyBlueLine, nXSize, 1, GDT_UInt8, 0, 0);
485
0
        if (err == CE_None && hMaskBand)
486
0
            err = GDALRasterIO(hMaskBand, GF_Read, 0, iLine, nXSize, 1,
487
0
                               pabyMask.get(), nXSize, 1, GDT_UInt8, 0, 0);
488
0
        if (err != CE_None)
489
0
            goto end_and_cleanup;
490
491
0
        for (int iPixel = 0; iPixel < nXSize; iPixel++)
492
0
        {
493
0
            if ((pabyRedLine[iPixel] == nSrcNoData &&
494
0
                 pabyGreenLine[iPixel] == nSrcNoData &&
495
0
                 pabyBlueLine[iPixel] == nSrcNoData) ||
496
0
                (pabyMask && (pabyMask.get())[iPixel] == 0))
497
0
            {
498
0
                continue;
499
0
            }
500
501
0
            const int nRed = pabyRedLine[iPixel] >> nColorShift;
502
0
            const int nGreen = pabyGreenLine[iPixel] >> nColorShift;
503
0
            const int nBlue = pabyBlueLine[iPixel] >> nColorShift;
504
505
0
            usedboxes->rmin = std::min(usedboxes->rmin, nRed);
506
0
            usedboxes->gmin = std::min(usedboxes->gmin, nGreen);
507
0
            usedboxes->bmin = std::min(usedboxes->bmin, nBlue);
508
0
            usedboxes->rmax = std::max(usedboxes->rmax, nRed);
509
0
            usedboxes->gmax = std::max(usedboxes->gmax, nGreen);
510
0
            usedboxes->bmax = std::max(usedboxes->bmax, nBlue);
511
512
0
            bool bFirstOccurrence;
513
0
            if (psHashHistogram)
514
0
            {
515
0
                int *pnColor = FindAndInsertColorCount(
516
0
                    psHashHistogram, MAKE_COLOR_CODE(nRed, nGreen, nBlue));
517
0
                bFirstOccurrence = (*pnColor == 0);
518
0
                (*pnColor)++;
519
0
            }
520
0
            else
521
0
            {
522
0
                T *pnColor =
523
0
                    HISTOGRAM(histogram, nCLevels, nRed, nGreen, nBlue);
524
0
                bFirstOccurrence = (*pnColor == 0);
525
0
                (*pnColor)++;
526
0
            }
527
0
            if (bFirstOccurrence)
528
0
            {
529
0
                if (nColorShift == 0 && nColorCounter < nColors)
530
0
                {
531
0
                    anRed[nColorCounter] = static_cast<GByte>(nRed);
532
0
                    anGreen[nColorCounter] = static_cast<GByte>(nGreen);
533
0
                    anBlue[nColorCounter] = static_cast<GByte>(nBlue);
534
0
                }
535
0
                nColorCounter++;
536
0
            }
537
0
        }
538
0
    }
539
540
0
    if (!pfnProgress(1.0, "Generating Histogram", pProgressArg))
541
0
    {
542
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User Terminated");
543
0
        err = CE_Failure;
544
0
        goto end_and_cleanup;
545
0
    }
546
547
0
    if (nColorShift == 0 && nColorCounter <= nColors)
548
0
    {
549
#if DEBUG_VERBOSE
550
        CPLDebug("MEDIAN_CUT", "%d colors found <= %d", nColorCounter, nColors);
551
#endif
552
0
        if (panPixelCountPerColorTableEntry)
553
0
        {
554
0
            panPixelCountPerColorTableEntry->clear();
555
0
            panPixelCountPerColorTableEntry->reserve(nColors);
556
0
        }
557
0
        for (int iColor = 0; iColor < nColorCounter; iColor++)
558
0
        {
559
0
            const GDALColorEntry sEntry = {static_cast<GByte>(anRed[iColor]),
560
0
                                           static_cast<GByte>(anGreen[iColor]),
561
0
                                           static_cast<GByte>(anBlue[iColor]),
562
0
                                           255};
563
0
            GDALSetColorEntry(hColorTable, iColor, &sEntry);
564
0
            if (panPixelCountPerColorTableEntry)
565
0
            {
566
0
                if (psHashHistogram)
567
0
                {
568
0
                    int *pnColor = FindAndInsertColorCount(
569
0
                        psHashHistogram,
570
0
                        MAKE_COLOR_CODE(anRed[iColor], anGreen[iColor],
571
0
                                        anBlue[iColor]));
572
0
                    panPixelCountPerColorTableEntry->push_back(*pnColor);
573
0
                }
574
0
                else
575
0
                {
576
0
                    T *pnColor = HISTOGRAM(histogram, nCLevels, anRed[iColor],
577
0
                                           anGreen[iColor], anBlue[iColor]);
578
0
                    panPixelCountPerColorTableEntry->push_back(*pnColor);
579
0
                }
580
0
            }
581
0
        }
582
0
        goto end_and_cleanup;
583
0
    }
584
585
    /* ==================================================================== */
586
    /*      STEP 3: continually subdivide boxes until no more free          */
587
    /*      boxes remain or until all colors assigned.                      */
588
    /* ==================================================================== */
589
0
    while (freeboxes != nullptr)
590
0
    {
591
0
        auto ptr = largest_box(usedboxes);
592
0
        if (ptr != nullptr)
593
0
            splitbox(ptr, histogram, psHashHistogram, nCLevels, &freeboxes,
594
0
                     &usedboxes, pabyRedBand, pabyGreenBand, pabyBlueBand,
595
0
                     nPixels);
596
0
        else
597
0
            freeboxes = nullptr;
598
0
    }
599
600
    /* ==================================================================== */
601
    /*      STEP 4: assign colors to all boxes                              */
602
    /* ==================================================================== */
603
0
    {
604
0
        Colorbox *ptr = usedboxes;
605
0
        if (panPixelCountPerColorTableEntry)
606
0
        {
607
0
            panPixelCountPerColorTableEntry->clear();
608
0
            panPixelCountPerColorTableEntry->reserve(nColors);
609
0
        }
610
0
        for (int i = 0; ptr != nullptr; ++i, ptr = ptr->next)
611
0
        {
612
0
            const GDALColorEntry sEntry = {
613
0
                static_cast<GByte>(((ptr->rmin + ptr->rmax) << nColorShift) /
614
0
                                   2),
615
0
                static_cast<GByte>(((ptr->gmin + ptr->gmax) << nColorShift) /
616
0
                                   2),
617
0
                static_cast<GByte>(((ptr->bmin + ptr->bmax) << nColorShift) /
618
0
                                   2),
619
0
                255};
620
0
            GDALSetColorEntry(hColorTable, i, &sEntry);
621
0
            if (panPixelCountPerColorTableEntry)
622
0
                panPixelCountPerColorTableEntry->push_back(
623
0
                    static_cast<T>(ptr->total));
624
0
        }
625
0
    }
626
627
0
end_and_cleanup:
628
0
    CPLFree(pabyRedLine);
629
0
    CPLFree(pabyGreenLine);
630
0
    CPLFree(pabyBlueLine);
631
632
    // We're done with the boxes now.
633
0
    CPLFree(box_list);
634
0
    freeboxes = nullptr;
635
0
    usedboxes = nullptr;
636
637
0
    if (panHistogram == nullptr)
638
0
        CPLFree(histogram);
639
640
0
    return err;
641
0
}
Unexecuted instantiation: int GDALComputeMedianCutPCTInternal<unsigned int>(void*, void*, void*, unsigned char*, unsigned char*, unsigned char*, int (*)(int, int, void*), int, int, unsigned int*, void*, int (*)(double, char const*, void*), void*, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >*)
Unexecuted instantiation: int GDALComputeMedianCutPCTInternal<unsigned long long>(void*, void*, void*, unsigned char*, unsigned char*, unsigned char*, int (*)(int, int, void*), int, int, unsigned long long*, void*, int (*)(double, char const*, void*), void*, std::__1::vector<unsigned long long, std::__1::allocator<unsigned long long> >*)
642
643
/************************************************************************/
644
/*                            largest_box()                             */
645
/************************************************************************/
646
647
static Colorbox *largest_box(Colorbox *usedboxes)
648
0
{
649
0
    Colorbox *b = nullptr;
650
651
0
    for (Colorbox *p = usedboxes; p != nullptr; p = p->next)
652
0
    {
653
0
        if ((p->rmax > p->rmin || p->gmax > p->gmin || p->bmax > p->bmin) &&
654
0
            (b == nullptr || p->total > b->total))
655
0
        {
656
0
            b = p;
657
0
        }
658
0
    }
659
0
    return b;
660
0
}
661
662
static void shrinkboxFromBand(Colorbox *ptr, const GByte *pabyRedBand,
663
                              const GByte *pabyGreenBand,
664
                              const GByte *pabyBlueBand, GUIntBig nPixels)
665
0
{
666
0
    int rmin_new = 255;
667
0
    int rmax_new = 0;
668
0
    int gmin_new = 255;
669
0
    int gmax_new = 0;
670
0
    int bmin_new = 255;
671
0
    int bmax_new = 0;
672
0
    for (GUIntBig i = 0; i < nPixels; i++)
673
0
    {
674
0
        const int iR = pabyRedBand[i];
675
0
        const int iG = pabyGreenBand[i];
676
0
        const int iB = pabyBlueBand[i];
677
0
        if (iR >= ptr->rmin && iR <= ptr->rmax && iG >= ptr->gmin &&
678
0
            iG <= ptr->gmax && iB >= ptr->bmin && iB <= ptr->bmax)
679
0
        {
680
0
            if (iR < rmin_new)
681
0
                rmin_new = iR;
682
0
            if (iR > rmax_new)
683
0
                rmax_new = iR;
684
0
            if (iG < gmin_new)
685
0
                gmin_new = iG;
686
0
            if (iG > gmax_new)
687
0
                gmax_new = iG;
688
0
            if (iB < bmin_new)
689
0
                bmin_new = iB;
690
0
            if (iB > bmax_new)
691
0
                bmax_new = iB;
692
0
        }
693
0
    }
694
695
0
    CPLAssert(rmin_new >= ptr->rmin && rmin_new <= rmax_new &&
696
0
              rmax_new <= ptr->rmax);
697
0
    CPLAssert(gmin_new >= ptr->gmin && gmin_new <= gmax_new &&
698
0
              gmax_new <= ptr->gmax);
699
0
    CPLAssert(bmin_new >= ptr->bmin && bmin_new <= bmax_new &&
700
0
              bmax_new <= ptr->bmax);
701
0
    ptr->rmin = rmin_new;
702
0
    ptr->rmax = rmax_new;
703
0
    ptr->gmin = gmin_new;
704
0
    ptr->gmax = gmax_new;
705
0
    ptr->bmin = bmin_new;
706
0
    ptr->bmax = bmax_new;
707
0
}
708
709
static void shrinkboxFromHashHistogram(Colorbox *box,
710
                                       const HashHistogram *psHashHistogram)
711
0
{
712
0
    if (box->rmax > box->rmin)
713
0
    {
714
0
        for (int ir = box->rmin; ir <= box->rmax; ++ir)
715
0
        {
716
0
            for (int ig = box->gmin; ig <= box->gmax; ++ig)
717
0
            {
718
0
                for (int ib = box->bmin; ib <= box->bmax; ++ib)
719
0
                {
720
0
                    if (FindColorCount(psHashHistogram,
721
0
                                       MAKE_COLOR_CODE(ir, ig, ib)) != 0)
722
0
                    {
723
0
                        box->rmin = ir;
724
0
                        goto have_rmin;
725
0
                    }
726
0
                }
727
0
            }
728
0
        }
729
0
    }
730
0
have_rmin:
731
0
    if (box->rmax > box->rmin)
732
0
    {
733
0
        for (int ir = box->rmax; ir >= box->rmin; --ir)
734
0
        {
735
0
            for (int ig = box->gmin; ig <= box->gmax; ++ig)
736
0
            {
737
0
                for (int ib = box->bmin; ib <= box->bmax; ++ib)
738
0
                {
739
0
                    if (FindColorCount(psHashHistogram,
740
0
                                       MAKE_COLOR_CODE(ir, ig, ib)) != 0)
741
0
                    {
742
0
                        box->rmax = ir;
743
0
                        goto have_rmax;
744
0
                    }
745
0
                }
746
0
            }
747
0
        }
748
0
    }
749
750
0
have_rmax:
751
0
    if (box->gmax > box->gmin)
752
0
    {
753
0
        for (int ig = box->gmin; ig <= box->gmax; ++ig)
754
0
        {
755
0
            for (int ir = box->rmin; ir <= box->rmax; ++ir)
756
0
            {
757
0
                for (int ib = box->bmin; ib <= box->bmax; ++ib)
758
0
                {
759
0
                    if (FindColorCount(psHashHistogram,
760
0
                                       MAKE_COLOR_CODE(ir, ig, ib)) != 0)
761
0
                    {
762
0
                        box->gmin = ig;
763
0
                        goto have_gmin;
764
0
                    }
765
0
                }
766
0
            }
767
0
        }
768
0
    }
769
770
0
have_gmin:
771
0
    if (box->gmax > box->gmin)
772
0
    {
773
0
        for (int ig = box->gmax; ig >= box->gmin; --ig)
774
0
        {
775
0
            for (int ir = box->rmin; ir <= box->rmax; ++ir)
776
0
            {
777
0
                int ib = box->bmin;
778
0
                for (; ib <= box->bmax; ++ib)
779
0
                {
780
0
                    if (FindColorCount(psHashHistogram,
781
0
                                       MAKE_COLOR_CODE(ir, ig, ib)) != 0)
782
0
                    {
783
0
                        box->gmax = ig;
784
0
                        goto have_gmax;
785
0
                    }
786
0
                }
787
0
            }
788
0
        }
789
0
    }
790
791
0
have_gmax:
792
0
    if (box->bmax > box->bmin)
793
0
    {
794
0
        for (int ib = box->bmin; ib <= box->bmax; ++ib)
795
0
        {
796
0
            for (int ir = box->rmin; ir <= box->rmax; ++ir)
797
0
            {
798
0
                for (int ig = box->gmin; ig <= box->gmax; ++ig)
799
0
                {
800
0
                    if (FindColorCount(psHashHistogram,
801
0
                                       MAKE_COLOR_CODE(ir, ig, ib)) != 0)
802
0
                    {
803
0
                        box->bmin = ib;
804
0
                        goto have_bmin;
805
0
                    }
806
0
                }
807
0
            }
808
0
        }
809
0
    }
810
811
0
have_bmin:
812
0
    if (box->bmax > box->bmin)
813
0
    {
814
0
        for (int ib = box->bmax; ib >= box->bmin; --ib)
815
0
        {
816
0
            for (int ir = box->rmin; ir <= box->rmax; ++ir)
817
0
            {
818
0
                for (int ig = box->gmin; ig <= box->gmax; ++ig)
819
0
                {
820
0
                    if (FindColorCount(psHashHistogram,
821
0
                                       MAKE_COLOR_CODE(ir, ig, ib)) != 0)
822
0
                    {
823
0
                        box->bmax = ib;
824
0
                        goto have_bmax;
825
0
                    }
826
0
                }
827
0
            }
828
0
        }
829
0
    }
830
831
0
have_bmax:;
832
0
}
833
834
/************************************************************************/
835
/*                              splitbox()                              */
836
/************************************************************************/
837
template <class T>
838
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
0
{
844
0
    T hist2[256] = {};
845
0
    int first = 0;
846
0
    int last = 0;
847
848
0
    enum
849
0
    {
850
0
        RED,
851
0
        GREEN,
852
0
        BLUE
853
0
    } 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
0
    {
858
0
        int i = ptr->rmax - ptr->rmin;
859
0
        if (i >= ptr->gmax - ptr->gmin && i >= ptr->bmax - ptr->bmin)
860
0
            axis = RED;
861
0
        else if (ptr->gmax - ptr->gmin >= ptr->bmax - ptr->bmin)
862
0
            axis = GREEN;
863
0
        else
864
0
            axis = BLUE;
865
0
    }
866
    // Get histogram along longest axis.
867
0
    const GUInt32 nIters = (ptr->rmax - ptr->rmin + 1) *
868
0
                           (ptr->gmax - ptr->gmin + 1) *
869
0
                           (ptr->bmax - ptr->bmin + 1);
870
871
0
    switch (axis)
872
0
    {
873
0
        case RED:
874
0
        {
875
0
            if (nPixels != 0 && nIters > nPixels)
876
0
            {
877
0
                const int rmin = ptr->rmin;
878
0
                const int rmax = ptr->rmax;
879
0
                const int gmin = ptr->gmin;
880
0
                const int gmax = ptr->gmax;
881
0
                const int bmin = ptr->bmin;
882
0
                const int bmax = ptr->bmax;
883
0
                for (T iPixel = 0; iPixel < nPixels; iPixel++)
884
0
                {
885
0
                    int iR = pabyRedBand[iPixel];
886
0
                    int iG = pabyGreenBand[iPixel];
887
0
                    int iB = pabyBlueBand[iPixel];
888
0
                    if (iR >= rmin && iR <= rmax && iG >= gmin && iG <= gmax &&
889
0
                        iB >= bmin && iB <= bmax)
890
0
                    {
891
0
                        hist2[iR]++;
892
0
                    }
893
0
                }
894
0
            }
895
0
            else if (psHashHistogram)
896
0
            {
897
0
                T *histp = &hist2[ptr->rmin];
898
0
                for (int ir = ptr->rmin; ir <= ptr->rmax; ++ir)
899
0
                {
900
0
                    *histp = 0;
901
0
                    for (int ig = ptr->gmin; ig <= ptr->gmax; ++ig)
902
0
                    {
903
0
                        for (int ib = ptr->bmin; ib <= ptr->bmax; ++ib)
904
0
                        {
905
0
                            *histp += FindColorCount(
906
0
                                psHashHistogram, MAKE_COLOR_CODE(ir, ig, ib));
907
0
                        }
908
0
                    }
909
0
                    histp++;
910
0
                }
911
0
            }
912
0
            else
913
0
            {
914
0
                T *histp = &hist2[ptr->rmin];
915
0
                for (int ir = ptr->rmin; ir <= ptr->rmax; ++ir)
916
0
                {
917
0
                    *histp = 0;
918
0
                    for (int ig = ptr->gmin; ig <= ptr->gmax; ++ig)
919
0
                    {
920
0
                        const T *iptr =
921
0
                            HISTOGRAM(histogram, nCLevels, ir, ig, ptr->bmin);
922
0
                        for (int ib = ptr->bmin; ib <= ptr->bmax; ++ib)
923
0
                            *histp += *iptr++;
924
0
                    }
925
0
                    histp++;
926
0
                }
927
0
            }
928
0
            first = ptr->rmin;
929
0
            last = ptr->rmax;
930
0
            break;
931
0
        }
932
0
        case GREEN:
933
0
        {
934
0
            if (nPixels != 0 && nIters > nPixels)
935
0
            {
936
0
                const int rmin = ptr->rmin;
937
0
                const int rmax = ptr->rmax;
938
0
                const int gmin = ptr->gmin;
939
0
                const int gmax = ptr->gmax;
940
0
                const int bmin = ptr->bmin;
941
0
                const int bmax = ptr->bmax;
942
0
                for (T iPixel = 0; iPixel < nPixels; iPixel++)
943
0
                {
944
0
                    const int iR = pabyRedBand[iPixel];
945
0
                    const int iG = pabyGreenBand[iPixel];
946
0
                    const int iB = pabyBlueBand[iPixel];
947
0
                    if (iR >= rmin && iR <= rmax && iG >= gmin && iG <= gmax &&
948
0
                        iB >= bmin && iB <= bmax)
949
0
                    {
950
0
                        hist2[iG]++;
951
0
                    }
952
0
                }
953
0
            }
954
0
            else if (psHashHistogram)
955
0
            {
956
0
                T *histp = &hist2[ptr->gmin];
957
0
                for (int ig = ptr->gmin; ig <= ptr->gmax; ++ig)
958
0
                {
959
0
                    *histp = 0;
960
0
                    for (int ir = ptr->rmin; ir <= ptr->rmax; ++ir)
961
0
                    {
962
0
                        for (int ib = ptr->bmin; ib <= ptr->bmax; ++ib)
963
0
                        {
964
0
                            *histp += FindColorCount(
965
0
                                psHashHistogram, MAKE_COLOR_CODE(ir, ig, ib));
966
0
                        }
967
0
                    }
968
0
                    histp++;
969
0
                }
970
0
            }
971
0
            else
972
0
            {
973
0
                T *histp = &hist2[ptr->gmin];
974
0
                for (int ig = ptr->gmin; ig <= ptr->gmax; ++ig)
975
0
                {
976
0
                    *histp = 0;
977
0
                    for (int ir = ptr->rmin; ir <= ptr->rmax; ++ir)
978
0
                    {
979
0
                        const T *iptr =
980
0
                            HISTOGRAM(histogram, nCLevels, ir, ig, ptr->bmin);
981
0
                        for (int ib = ptr->bmin; ib <= ptr->bmax; ++ib)
982
0
                            *histp += *iptr++;
983
0
                    }
984
0
                    histp++;
985
0
                }
986
0
            }
987
0
            first = ptr->gmin;
988
0
            last = ptr->gmax;
989
0
            break;
990
0
        }
991
0
        case BLUE:
992
0
        {
993
0
            if (nPixels != 0 && nIters > nPixels)
994
0
            {
995
0
                const int rmin = ptr->rmin;
996
0
                const int rmax = ptr->rmax;
997
0
                const int gmin = ptr->gmin;
998
0
                const int gmax = ptr->gmax;
999
0
                const int bmin = ptr->bmin;
1000
0
                const int bmax = ptr->bmax;
1001
0
                for (T iPixel = 0; iPixel < nPixels; iPixel++)
1002
0
                {
1003
0
                    const int iR = pabyRedBand[iPixel];
1004
0
                    const int iG = pabyGreenBand[iPixel];
1005
0
                    const int iB = pabyBlueBand[iPixel];
1006
0
                    if (iR >= rmin && iR <= rmax && iG >= gmin && iG <= gmax &&
1007
0
                        iB >= bmin && iB <= bmax)
1008
0
                    {
1009
0
                        hist2[iB]++;
1010
0
                    }
1011
0
                }
1012
0
            }
1013
0
            else if (psHashHistogram)
1014
0
            {
1015
0
                T *histp = &hist2[ptr->bmin];
1016
0
                for (int ib = ptr->bmin; ib <= ptr->bmax; ++ib)
1017
0
                {
1018
0
                    *histp = 0;
1019
0
                    for (int ir = ptr->rmin; ir <= ptr->rmax; ++ir)
1020
0
                    {
1021
0
                        for (int ig = ptr->gmin; ig <= ptr->gmax; ++ig)
1022
0
                        {
1023
0
                            *histp += FindColorCount(
1024
0
                                psHashHistogram, MAKE_COLOR_CODE(ir, ig, ib));
1025
0
                        }
1026
0
                    }
1027
0
                    histp++;
1028
0
                }
1029
0
            }
1030
0
            else
1031
0
            {
1032
0
                T *histp = &hist2[ptr->bmin];
1033
0
                for (int ib = ptr->bmin; ib <= ptr->bmax; ++ib)
1034
0
                {
1035
0
                    *histp = 0;
1036
0
                    for (int ir = ptr->rmin; ir <= ptr->rmax; ++ir)
1037
0
                    {
1038
0
                        const T *iptr =
1039
0
                            HISTOGRAM(histogram, nCLevels, ir, ptr->gmin, ib);
1040
0
                        for (int ig = ptr->gmin; ig <= ptr->gmax; ++ig)
1041
0
                        {
1042
0
                            *histp += *iptr;
1043
0
                            iptr += nCLevels;
1044
0
                        }
1045
0
                    }
1046
0
                    histp++;
1047
0
                }
1048
0
            }
1049
0
            first = ptr->bmin;
1050
0
            last = ptr->bmax;
1051
0
            break;
1052
0
        }
1053
0
    }
1054
    // Find median point.
1055
0
    T *histp = &hist2[first];
1056
0
    int i = first;  // TODO(schwehr): Rename i.
1057
0
    {
1058
0
        T sum = 0;
1059
0
        T sum2 = static_cast<T>(ptr->total / 2);
1060
0
        for (; i <= last && (sum += *histp++) < sum2; ++i)
1061
0
        {
1062
0
        }
1063
0
    }
1064
0
    if (i == first)
1065
0
        i++;
1066
1067
    // Create new box, re-allocate points.
1068
0
    Colorbox *new_cb = *pfreeboxes;
1069
0
    *pfreeboxes = new_cb->next;
1070
0
    if (*pfreeboxes)
1071
0
        (*pfreeboxes)->prev = nullptr;
1072
0
    if (*pusedboxes)
1073
0
        (*pusedboxes)->prev = new_cb;
1074
0
    new_cb->next = *pusedboxes;
1075
0
    *pusedboxes = new_cb;
1076
1077
0
    histp = &hist2[first];
1078
0
    {
1079
0
        T sum1 = 0;
1080
0
        for (int j = first; j < i; j++)
1081
0
            sum1 += *histp++;
1082
0
        T sum2 = 0;
1083
0
        for (int j = i; j <= last; j++)
1084
0
            sum2 += *histp++;
1085
0
        new_cb->total = sum1;
1086
0
        ptr->total = sum2;
1087
0
    }
1088
0
    new_cb->rmin = ptr->rmin;
1089
0
    new_cb->rmax = ptr->rmax;
1090
0
    new_cb->gmin = ptr->gmin;
1091
0
    new_cb->gmax = ptr->gmax;
1092
0
    new_cb->bmin = ptr->bmin;
1093
0
    new_cb->bmax = ptr->bmax;
1094
0
    switch (axis)
1095
0
    {
1096
0
        case RED:
1097
0
            new_cb->rmax = i - 1;
1098
0
            ptr->rmin = i;
1099
0
            break;
1100
0
        case GREEN:
1101
0
            new_cb->gmax = i - 1;
1102
0
            ptr->gmin = i;
1103
0
            break;
1104
0
        case BLUE:
1105
0
            new_cb->bmax = i - 1;
1106
0
            ptr->bmin = i;
1107
0
            break;
1108
0
    }
1109
1110
0
    if (nPixels != 0 &&
1111
0
        static_cast<T>(new_cb->rmax - new_cb->rmin + 1) *
1112
0
                static_cast<T>(new_cb->gmax - new_cb->gmin + 1) *
1113
0
                static_cast<T>(new_cb->bmax - new_cb->bmin + 1) >
1114
0
            nPixels)
1115
0
    {
1116
0
        shrinkboxFromBand(new_cb, pabyRedBand, pabyGreenBand, pabyBlueBand,
1117
0
                          nPixels);
1118
0
    }
1119
0
    else if (psHashHistogram != nullptr)
1120
0
    {
1121
0
        shrinkboxFromHashHistogram(new_cb, psHashHistogram);
1122
0
    }
1123
0
    else
1124
0
    {
1125
0
        shrinkbox(new_cb, histogram, nCLevels);
1126
0
    }
1127
1128
0
    if (nPixels != 0 && static_cast<T>(ptr->rmax - ptr->rmin + 1) *
1129
0
                                static_cast<T>(ptr->gmax - ptr->gmin + 1) *
1130
0
                                static_cast<T>(ptr->bmax - ptr->bmin + 1) >
1131
0
                            nPixels)
1132
0
    {
1133
0
        shrinkboxFromBand(ptr, pabyRedBand, pabyGreenBand, pabyBlueBand,
1134
0
                          nPixels);
1135
0
    }
1136
0
    else if (psHashHistogram != nullptr)
1137
0
    {
1138
0
        shrinkboxFromHashHistogram(ptr, psHashHistogram);
1139
0
    }
1140
0
    else
1141
0
    {
1142
0
        shrinkbox(ptr, histogram, nCLevels);
1143
0
    }
1144
0
}
Unexecuted instantiation: gdalmediancut.cpp:void splitbox<unsigned int>(colorbox*, unsigned int const*, HashHistogram const*, int, colorbox**, colorbox**, unsigned char*, unsigned char*, unsigned char*, unsigned int)
Unexecuted instantiation: gdalmediancut.cpp:void splitbox<unsigned long long>(colorbox*, unsigned long long const*, HashHistogram const*, int, colorbox**, colorbox**, unsigned char*, unsigned char*, unsigned char*, unsigned long long)
1145
1146
/************************************************************************/
1147
/*                             shrinkbox()                              */
1148
/************************************************************************/
1149
template <class T>
1150
static void shrinkbox(Colorbox *box, const T *histogram, int nCLevels)
1151
0
{
1152
0
    if (box->rmax > box->rmin)
1153
0
    {
1154
0
        for (int ir = box->rmin; ir <= box->rmax; ++ir)
1155
0
        {
1156
0
            for (int ig = box->gmin; ig <= box->gmax; ++ig)
1157
0
            {
1158
0
                const T *histp =
1159
0
                    HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin);
1160
0
                for (int ib = box->bmin; ib <= box->bmax; ++ib)
1161
0
                {
1162
0
                    if (*histp++ != 0)
1163
0
                    {
1164
0
                        box->rmin = ir;
1165
0
                        goto have_rmin;
1166
0
                    }
1167
0
                }
1168
0
            }
1169
0
        }
1170
0
    }
1171
0
have_rmin:
1172
0
    if (box->rmax > box->rmin)
1173
0
    {
1174
0
        for (int ir = box->rmax; ir >= box->rmin; --ir)
1175
0
        {
1176
0
            for (int ig = box->gmin; ig <= box->gmax; ++ig)
1177
0
            {
1178
0
                const T *histp =
1179
0
                    HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin);
1180
0
                for (int ib = box->bmin; ib <= box->bmax; ++ib)
1181
0
                {
1182
0
                    if (*histp++ != 0)
1183
0
                    {
1184
0
                        box->rmax = ir;
1185
0
                        goto have_rmax;
1186
0
                    }
1187
0
                }
1188
0
            }
1189
0
        }
1190
0
    }
1191
1192
0
have_rmax:
1193
0
    if (box->gmax > box->gmin)
1194
0
    {
1195
0
        for (int ig = box->gmin; ig <= box->gmax; ++ig)
1196
0
        {
1197
0
            for (int ir = box->rmin; ir <= box->rmax; ++ir)
1198
0
            {
1199
0
                const T *histp =
1200
0
                    HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin);
1201
0
                for (int ib = box->bmin; ib <= box->bmax; ++ib)
1202
0
                {
1203
0
                    if (*histp++ != 0)
1204
0
                    {
1205
0
                        box->gmin = ig;
1206
0
                        goto have_gmin;
1207
0
                    }
1208
0
                }
1209
0
            }
1210
0
        }
1211
0
    }
1212
1213
0
have_gmin:
1214
0
    if (box->gmax > box->gmin)
1215
0
    {
1216
0
        for (int ig = box->gmax; ig >= box->gmin; --ig)
1217
0
        {
1218
0
            for (int ir = box->rmin; ir <= box->rmax; ++ir)
1219
0
            {
1220
0
                const T *histp =
1221
0
                    HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin);
1222
0
                for (int ib = box->bmin; ib <= box->bmax; ++ib)
1223
0
                {
1224
0
                    if (*histp++ != 0)
1225
0
                    {
1226
0
                        box->gmax = ig;
1227
0
                        goto have_gmax;
1228
0
                    }
1229
0
                }
1230
0
            }
1231
0
        }
1232
0
    }
1233
1234
0
have_gmax:
1235
0
    if (box->bmax > box->bmin)
1236
0
    {
1237
0
        for (int ib = box->bmin; ib <= box->bmax; ++ib)
1238
0
        {
1239
0
            for (int ir = box->rmin; ir <= box->rmax; ++ir)
1240
0
            {
1241
0
                const T *histp =
1242
0
                    HISTOGRAM(histogram, nCLevels, ir, box->gmin, ib);
1243
0
                for (int ig = box->gmin; ig <= box->gmax; ++ig)
1244
0
                {
1245
0
                    if (*histp != 0)
1246
0
                    {
1247
0
                        box->bmin = ib;
1248
0
                        goto have_bmin;
1249
0
                    }
1250
0
                    histp += nCLevels;
1251
0
                }
1252
0
            }
1253
0
        }
1254
0
    }
1255
1256
0
have_bmin:
1257
0
    if (box->bmax > box->bmin)
1258
0
    {
1259
0
        for (int ib = box->bmax; ib >= box->bmin; --ib)
1260
0
        {
1261
0
            for (int ir = box->rmin; ir <= box->rmax; ++ir)
1262
0
            {
1263
0
                const T *histp =
1264
0
                    HISTOGRAM(histogram, nCLevels, ir, box->gmin, ib);
1265
0
                for (int ig = box->gmin; ig <= box->gmax; ++ig)
1266
0
                {
1267
0
                    if (*histp != 0)
1268
0
                    {
1269
0
                        box->bmax = ib;
1270
0
                        goto have_bmax;
1271
0
                    }
1272
0
                    histp += nCLevels;
1273
0
                }
1274
0
            }
1275
0
        }
1276
0
    }
1277
1278
0
have_bmax:;
1279
0
}
Unexecuted instantiation: gdalmediancut.cpp:void shrinkbox<unsigned int>(colorbox*, unsigned int const*, int)
Unexecuted instantiation: gdalmediancut.cpp:void shrinkbox<unsigned long long>(colorbox*, unsigned long long const*, int)
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
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
0
{
1306
0
    return GDALComputeMedianCutPCTInternal<GUInt32>(
1307
0
        hRed, hGreen, hBlue, pabyRedBand, pabyGreenBand, pabyBlueBand,
1308
0
        pfnIncludePixel, nColors, nBits, panHistogram, hColorTable, pfnProgress,
1309
0
        pProgressArg, panPixelCountPerColorTableEntry);
1310
0
}
1311
1312
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
0
{
1320
0
    return GDALComputeMedianCutPCTInternal<GUIntBig>(
1321
0
        hRed, hGreen, hBlue, pabyRedBand, pabyGreenBand, pabyBlueBand,
1322
0
        pfnIncludePixel, nColors, nBits, panHistogram, hColorTable, pfnProgress,
1323
0
        pProgressArg, panPixelCountPerColorTableEntry);
1324
0
}