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