Coverage Report

Created: 2026-02-14 06:52

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