Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Core
4 : * Purpose: Color table implementation.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : **********************************************************************
8 : * Copyright (c) 2000, Frank Warmerdam
9 : * Copyright (c) 2009, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "gdal_priv.h"
16 :
17 : #include <cstring>
18 : #include <exception>
19 : #include <memory>
20 : #include <vector>
21 :
22 : #include "cpl_error.h"
23 : #include "gdal.h"
24 :
25 : /************************************************************************/
26 : /* GDALColorTable() */
27 : /************************************************************************/
28 :
29 : /**
30 : * \brief Construct a new color table.
31 : *
32 : * This constructor is the same as the C GDALCreateColorTable() function.
33 : *
34 : * @param eInterpIn the interpretation to be applied to GDALColorEntry
35 : * values.
36 : */
37 :
38 1268 : GDALColorTable::GDALColorTable(GDALPaletteInterp eInterpIn) : eInterp(eInterpIn)
39 : {
40 1268 : }
41 :
42 : /************************************************************************/
43 : /* GDALCreateColorTable() */
44 : /************************************************************************/
45 :
46 : /**
47 : * \brief Construct a new color table.
48 : *
49 : * This function is the same as the C++ method GDALColorTable::GDALColorTable()
50 : */
51 55 : GDALColorTableH CPL_STDCALL GDALCreateColorTable(GDALPaletteInterp eInterp)
52 :
53 : {
54 55 : return GDALColorTable::ToHandle(new GDALColorTable(eInterp));
55 : }
56 :
57 : /************************************************************************/
58 : /* ~GDALColorTable() */
59 : /************************************************************************/
60 :
61 : /**
62 : * \brief Destructor.
63 : *
64 : * This destructor is the same as the C GDALDestroyColorTable() function.
65 : */
66 :
67 : GDALColorTable::~GDALColorTable() = default;
68 :
69 : /************************************************************************/
70 : /* GDALDestroyColorTable() */
71 : /************************************************************************/
72 :
73 : /**
74 : * \brief Destroys a color table.
75 : *
76 : * This function is the same as the C++ method GDALColorTable::~GDALColorTable()
77 : */
78 72 : void CPL_STDCALL GDALDestroyColorTable(GDALColorTableH hTable)
79 :
80 : {
81 72 : delete GDALColorTable::FromHandle(hTable);
82 72 : }
83 :
84 : /************************************************************************/
85 : /* GetColorEntry() */
86 : /************************************************************************/
87 :
88 : /**
89 : * \brief Fetch a color entry from table.
90 : *
91 : * This method is the same as the C function GDALGetColorEntry().
92 : *
93 : * @param i entry offset from zero to GetColorEntryCount()-1.
94 : *
95 : * @return pointer to internal color entry, or NULL if index is out of range.
96 : */
97 :
98 2093000 : const GDALColorEntry *GDALColorTable::GetColorEntry(int i) const
99 :
100 : {
101 2093000 : if (i < 0 || i >= static_cast<int>(aoEntries.size()))
102 0 : return nullptr;
103 :
104 2093000 : return &aoEntries[i];
105 : }
106 :
107 : /************************************************************************/
108 : /* GDALGetColorEntry() */
109 : /************************************************************************/
110 :
111 : /**
112 : * \brief Fetch a color entry from table.
113 : *
114 : * This function is the same as the C++ method GDALColorTable::GetColorEntry()
115 : */
116 6557 : const GDALColorEntry *CPL_STDCALL GDALGetColorEntry(GDALColorTableH hTable,
117 : int i)
118 :
119 : {
120 6557 : VALIDATE_POINTER1(hTable, "GDALGetColorEntry", nullptr);
121 :
122 6557 : return GDALColorTable::FromHandle(hTable)->GetColorEntry(i);
123 : }
124 :
125 : /************************************************************************/
126 : /* GetColorEntryAsRGB() */
127 : /************************************************************************/
128 :
129 : /**
130 : * \brief Fetch a table entry in RGB format.
131 : *
132 : * In theory this method should support translation of color palettes in
133 : * non-RGB color spaces into RGB on the fly, but currently it only works
134 : * on RGB color tables.
135 : *
136 : * This method is the same as the C function GDALGetColorEntryAsRGB().
137 : *
138 : * @param i entry offset from zero to GetColorEntryCount()-1.
139 : *
140 : * @param poEntry the existing GDALColorEntry to be overrwritten with the RGB
141 : * values.
142 : *
143 : * @return TRUE on success, or FALSE if the conversion isn't supported.
144 : */
145 :
146 95301 : int GDALColorTable::GetColorEntryAsRGB(int i, GDALColorEntry *poEntry) const
147 :
148 : {
149 95301 : if (eInterp != GPI_RGB || i < 0 || i >= static_cast<int>(aoEntries.size()))
150 0 : return FALSE;
151 :
152 95301 : *poEntry = aoEntries[i];
153 95301 : return TRUE;
154 : }
155 :
156 : /************************************************************************/
157 : /* GDALGetColorEntryAsRGB() */
158 : /************************************************************************/
159 :
160 : /**
161 : * \brief Fetch a table entry in RGB format.
162 : *
163 : * This function is the same as the C++ method
164 : * GDALColorTable::GetColorEntryAsRGB().
165 : */
166 3393 : int CPL_STDCALL GDALGetColorEntryAsRGB(GDALColorTableH hTable, int i,
167 : GDALColorEntry *poEntry)
168 :
169 : {
170 3393 : VALIDATE_POINTER1(hTable, "GDALGetColorEntryAsRGB", 0);
171 3393 : VALIDATE_POINTER1(poEntry, "GDALGetColorEntryAsRGB", 0);
172 :
173 3393 : return GDALColorTable::FromHandle(hTable)->GetColorEntryAsRGB(i, poEntry);
174 : }
175 :
176 : /************************************************************************/
177 : /* SetColorEntry() */
178 : /************************************************************************/
179 :
180 : /**
181 : * \brief Set entry in color table.
182 : *
183 : * Note that the passed in color entry is copied, and no internal reference
184 : * to it is maintained. Also, the passed in entry must match the color
185 : * interpretation of the table to which it is being assigned.
186 : *
187 : * The table is grown as needed to hold the supplied offset.
188 : *
189 : * This function is the same as the C function GDALSetColorEntry().
190 : *
191 : * @param i entry offset from zero to GetColorEntryCount()-1.
192 : * @param poEntry value to assign to table.
193 : */
194 :
195 434334 : void GDALColorTable::SetColorEntry(int i, const GDALColorEntry *poEntry)
196 :
197 : {
198 434334 : if (i < 0)
199 0 : return;
200 :
201 : try
202 : {
203 434334 : if (i >= static_cast<int>(aoEntries.size()))
204 : {
205 49272 : GDALColorEntry oBlack = {0, 0, 0, 0};
206 49272 : aoEntries.resize(i + 1, oBlack);
207 : }
208 :
209 434334 : aoEntries[i] = *poEntry;
210 : }
211 0 : catch (std::exception &e)
212 : {
213 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
214 : }
215 : }
216 :
217 : /************************************************************************/
218 : /* GDALSetColorEntry() */
219 : /************************************************************************/
220 :
221 : /**
222 : * \brief Set entry in color table.
223 : *
224 : * This function is the same as the C++ method GDALColorTable::SetColorEntry()
225 : */
226 3964 : void CPL_STDCALL GDALSetColorEntry(GDALColorTableH hTable, int i,
227 : const GDALColorEntry *poEntry)
228 :
229 : {
230 3964 : VALIDATE_POINTER0(hTable, "GDALSetColorEntry");
231 3964 : VALIDATE_POINTER0(poEntry, "GDALSetColorEntry");
232 :
233 3964 : GDALColorTable::FromHandle(hTable)->SetColorEntry(i, poEntry);
234 : }
235 :
236 : /************************************************************************/
237 : /* Clone() */
238 : /************************************************************************/
239 :
240 : /**
241 : * \brief Make a copy of a color table.
242 : *
243 : * This method is the same as the C function GDALCloneColorTable().
244 : */
245 :
246 276 : GDALColorTable *GDALColorTable::Clone() const
247 :
248 : {
249 276 : return new GDALColorTable(*this);
250 : }
251 :
252 : /************************************************************************/
253 : /* GDALCloneColorTable() */
254 : /************************************************************************/
255 :
256 : /**
257 : * \brief Make a copy of a color table.
258 : *
259 : * This function is the same as the C++ method GDALColorTable::Clone()
260 : */
261 17 : GDALColorTableH CPL_STDCALL GDALCloneColorTable(GDALColorTableH hTable)
262 :
263 : {
264 17 : VALIDATE_POINTER1(hTable, "GDALCloneColorTable", nullptr);
265 :
266 17 : return GDALColorTable::ToHandle(
267 17 : GDALColorTable::FromHandle(hTable)->Clone());
268 : }
269 :
270 : /************************************************************************/
271 : /* GetColorEntryCount() */
272 : /************************************************************************/
273 :
274 : /**
275 : * \brief Get number of color entries in table.
276 : *
277 : * This method is the same as the function GDALGetColorEntryCount().
278 : *
279 : * @return the number of color entries.
280 : */
281 :
282 147994 : int GDALColorTable::GetColorEntryCount() const
283 :
284 : {
285 147994 : return static_cast<int>(aoEntries.size());
286 : }
287 :
288 : /************************************************************************/
289 : /* GDALGetColorEntryCount() */
290 : /************************************************************************/
291 :
292 : /**
293 : * \brief Get number of color entries in table.
294 : *
295 : * This function is the same as the C++ method
296 : * GDALColorTable::GetColorEntryCount()
297 : */
298 389 : int CPL_STDCALL GDALGetColorEntryCount(GDALColorTableH hTable)
299 :
300 : {
301 389 : VALIDATE_POINTER1(hTable, "GDALGetColorEntryCount", 0);
302 :
303 389 : return GDALColorTable::FromHandle(hTable)->GetColorEntryCount();
304 : }
305 :
306 : /************************************************************************/
307 : /* GetPaletteInterpretation() */
308 : /************************************************************************/
309 :
310 : /**
311 : * \brief Fetch palette interpretation.
312 : *
313 : * The returned value is used to interpret the values in the GDALColorEntry.
314 : *
315 : * This method is the same as the C function GDALGetPaletteInterpretation().
316 : *
317 : * @return palette interpretation enumeration value, usually GPI_RGB.
318 : */
319 :
320 18 : GDALPaletteInterp GDALColorTable::GetPaletteInterpretation() const
321 :
322 : {
323 18 : return eInterp;
324 : }
325 :
326 : /************************************************************************/
327 : /* GDALGetPaletteInterpretation() */
328 : /************************************************************************/
329 :
330 : /**
331 : * \brief Fetch palette interpretation.
332 : *
333 : * This function is the same as the C++ method
334 : * GDALColorTable::GetPaletteInterpretation()
335 : */
336 : GDALPaletteInterp CPL_STDCALL
337 9 : GDALGetPaletteInterpretation(GDALColorTableH hTable)
338 :
339 : {
340 9 : VALIDATE_POINTER1(hTable, "GDALGetPaletteInterpretation", GPI_Gray);
341 :
342 9 : return GDALColorTable::FromHandle(hTable)->GetPaletteInterpretation();
343 : }
344 :
345 : /**
346 : * \brief Create color ramp
347 : *
348 : * Automatically creates a color ramp from one color entry to
349 : * another. It can be called several times to create multiples ramps
350 : * in the same color table.
351 : *
352 : * This function is the same as the C function GDALCreateColorRamp().
353 : *
354 : * @param nStartIndex index to start the ramp on the color table [0..255]
355 : * @param psStartColor a color entry value to start the ramp
356 : * @param nEndIndex index to end the ramp on the color table [0..255]
357 : * @param psEndColor a color entry value to end the ramp
358 : * @return total number of entries, -1 to report error
359 : */
360 :
361 257 : int GDALColorTable::CreateColorRamp(int nStartIndex,
362 : const GDALColorEntry *psStartColor,
363 : int nEndIndex,
364 : const GDALColorEntry *psEndColor)
365 : {
366 : // Validate indexes.
367 257 : if (nStartIndex < 0 || nStartIndex > 255 || nEndIndex < 0 ||
368 257 : nEndIndex > 255 || nStartIndex > nEndIndex)
369 : {
370 0 : return -1;
371 : }
372 :
373 : // Validate color entries.
374 257 : if (psStartColor == nullptr || psEndColor == nullptr)
375 : {
376 0 : return -1;
377 : }
378 :
379 : // Calculate number of colors in-between + 1.
380 257 : const int nColors = nEndIndex - nStartIndex;
381 :
382 : // Set starting color.
383 257 : SetColorEntry(nStartIndex, psStartColor);
384 :
385 257 : if (nColors == 0)
386 : {
387 0 : return GetColorEntryCount(); // Only one color. No ramp to create.
388 : }
389 :
390 : // Set ending color.
391 257 : SetColorEntry(nEndIndex, psEndColor);
392 :
393 : // Calculate the slope of the linear transformation.
394 257 : const double dfColors = static_cast<double>(nColors);
395 257 : const double dfSlope1 = (psEndColor->c1 - psStartColor->c1) / dfColors;
396 257 : const double dfSlope2 = (psEndColor->c2 - psStartColor->c2) / dfColors;
397 257 : const double dfSlope3 = (psEndColor->c3 - psStartColor->c3) / dfColors;
398 257 : const double dfSlope4 = (psEndColor->c4 - psStartColor->c4) / dfColors;
399 :
400 : // Loop through the new colors.
401 257 : GDALColorEntry sColor = *psStartColor;
402 :
403 764 : for (int i = 1; i < nColors; i++)
404 : {
405 507 : sColor.c1 = static_cast<short>(i * dfSlope1 + psStartColor->c1);
406 507 : sColor.c2 = static_cast<short>(i * dfSlope2 + psStartColor->c2);
407 507 : sColor.c3 = static_cast<short>(i * dfSlope3 + psStartColor->c3);
408 507 : sColor.c4 = static_cast<short>(i * dfSlope4 + psStartColor->c4);
409 :
410 507 : SetColorEntry(nStartIndex + i, &sColor);
411 : }
412 :
413 : // Return the total number of colors.
414 257 : return GetColorEntryCount();
415 : }
416 :
417 : /************************************************************************/
418 : /* GDALCreateColorRamp() */
419 : /************************************************************************/
420 :
421 : /**
422 : * \brief Create color ramp
423 : *
424 : * This function is the same as the C++ method GDALColorTable::CreateColorRamp()
425 : */
426 1 : void CPL_STDCALL GDALCreateColorRamp(GDALColorTableH hTable, int nStartIndex,
427 : const GDALColorEntry *psStartColor,
428 : int nEndIndex,
429 : const GDALColorEntry *psEndColor)
430 : {
431 1 : VALIDATE_POINTER0(hTable, "GDALCreateColorRamp");
432 :
433 1 : GDALColorTable::FromHandle(hTable)->CreateColorRamp(
434 : nStartIndex, psStartColor, nEndIndex, psEndColor);
435 : }
436 :
437 : /************************************************************************/
438 : /* IsSame() */
439 : /************************************************************************/
440 :
441 : /**
442 : * \brief Returns if the current color table is the same as another one.
443 : *
444 : * @param poOtherCT other color table to be compared to.
445 : * @return TRUE if both color tables are identical.
446 : * @since GDAL 2.0
447 : */
448 :
449 14 : int GDALColorTable::IsSame(const GDALColorTable *poOtherCT) const
450 : {
451 42 : return aoEntries.size() == poOtherCT->aoEntries.size() &&
452 14 : (aoEntries.empty() ||
453 14 : memcmp(&aoEntries[0], &poOtherCT->aoEntries[0],
454 28 : aoEntries.size() * sizeof(GDALColorEntry)) == 0);
455 : }
456 :
457 : /************************************************************************/
458 : /* IsIdentity() */
459 : /************************************************************************/
460 :
461 : /**
462 : * \brief Returns if the current color table is the identity, that is
463 : * for each index i, colortable[i].c1 = .c2 = .c3 = i and .c4 = 255
464 : *
465 : * @since GDAL 3.4.1
466 : */
467 :
468 18 : bool GDALColorTable::IsIdentity() const
469 : {
470 290 : for (int i = 0; i < static_cast<int>(aoEntries.size()); ++i)
471 : {
472 562 : if (aoEntries[i].c1 != i || aoEntries[i].c2 != i ||
473 562 : aoEntries[i].c3 != i || aoEntries[i].c4 != 255)
474 : {
475 17 : return false;
476 : }
477 : }
478 1 : return true;
479 : }
|