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