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