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