Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: MiraMonRaster driver
4 : * Purpose: Implements MMRPalettes class: handles access to a DBF file
5 : * containing color information, which is then converted into
6 : * either a color table or an attribute table, depending on the
7 : * context.
8 : * Author: Abel Pau
9 : *
10 : ******************************************************************************
11 : * Copyright (c) 2025, Xavier Pons
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 :
16 : #include "miramon_rel.h"
17 : #include "miramon_palettes.h"
18 :
19 : #include "../miramon_common/mm_gdal_functions.h" // For MMCheck_REL_FILE()
20 :
21 63 : MMRPalettes::MMRPalettes(MMRRel &fRel, const CPLString &osBandSectionIn)
22 63 : : m_pfRel(&fRel), m_osBandSection(osBandSectionIn)
23 : {
24 : // Is a constant color, and which colors is it?
25 63 : CPLString os_Color_Const;
26 63 : if (!m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, m_osBandSection,
27 : "Color_Const", os_Color_Const))
28 31 : os_Color_Const = ""; // Coverity scan CID 1620831
29 :
30 63 : if (EQUAL(os_Color_Const, "1"))
31 : {
32 2 : m_bIsConstantColor = true;
33 2 : if (CE_None != UpdateConstantColor())
34 0 : return; // The constant color indicated is wrong
35 : }
36 :
37 63 : CPLString os_Color_Paleta;
38 :
39 63 : if (!m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, m_osBandSection,
40 121 : "Color_Paleta", os_Color_Paleta) ||
41 58 : EQUAL(os_Color_Paleta, "<Automatic>"))
42 : {
43 36 : m_bIsValid = true;
44 36 : ColorScaling = ColorTreatment::DIRECT_ASSIGNATION;
45 36 : return;
46 : }
47 :
48 : // Treatment of the color variable
49 27 : CPLString os_Color_TractamentVariable;
50 27 : if (!m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, m_osBandSection,
51 : "Color_TractamentVariable",
52 50 : os_Color_TractamentVariable) ||
53 23 : os_Color_TractamentVariable.empty())
54 : {
55 8 : CPLString os_TractamentVariable;
56 4 : if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA,
57 : "TractamentVariable",
58 : os_TractamentVariable))
59 2 : os_TractamentVariable = ""; // Coverity scan CID 1620831
60 :
61 4 : if (EQUAL(os_TractamentVariable, "Categoric"))
62 2 : SetIsCategorical(true);
63 : else
64 2 : SetIsCategorical(false);
65 : }
66 : else
67 : {
68 23 : if (EQUAL(os_Color_TractamentVariable, "Categoric"))
69 18 : SetIsCategorical(true);
70 : else
71 5 : SetIsCategorical(false);
72 : }
73 :
74 27 : UpdateColorInfo();
75 :
76 27 : CPLString osExtension = CPLGetExtensionSafe(os_Color_Paleta);
77 27 : if (osExtension.tolower() == "dbf")
78 : {
79 24 : if (CE_None != GetPaletteColors_DBF(os_Color_Paleta))
80 3 : return;
81 :
82 21 : m_bIsValid = true;
83 : }
84 4 : else if (osExtension.tolower() == "pal" || osExtension.tolower() == "p25" ||
85 1 : osExtension.tolower() == "p65")
86 : {
87 3 : if (CE_None != GetPaletteColors_PAL_P25_P65(os_Color_Paleta))
88 0 : return;
89 :
90 3 : m_bIsValid = true;
91 : }
92 : else
93 0 : return;
94 :
95 24 : m_nRealNPaletteColors = m_nNPaletteColors;
96 24 : if (HasNodata())
97 : {
98 6 : if (m_nNPaletteColors < 1)
99 0 : return;
100 6 : m_nNPaletteColors--;
101 : }
102 : else
103 : {
104 : // If palette doesn't have nodata let's set some index
105 18 : m_nNoDataPaletteIndex = m_nRealNPaletteColors;
106 : }
107 : }
108 :
109 63 : MMRPalettes::~MMRPalettes()
110 : {
111 63 : }
112 :
113 1213 : void MMRPalettes::AssignColorFromDBF(struct MM_DATA_BASE_XP &oColorTable,
114 : char *pzsRecord, char *pszField,
115 : MM_EXT_DBF_N_FIELDS &nRIndex,
116 : MM_EXT_DBF_N_FIELDS &nGIndex,
117 : MM_EXT_DBF_N_FIELDS &nBIndex,
118 : int nIPaletteIndex)
119 : {
120 : // RED
121 1213 : memcpy(pszField, pzsRecord + oColorTable.pField[nRIndex].AccumulatedBytes,
122 1213 : oColorTable.pField[nRIndex].BytesPerField);
123 1213 : pszField[oColorTable.pField[nRIndex].BytesPerField] = '\0';
124 1213 : m_aadfPaletteColors[0][nIPaletteIndex] = CPLAtof(pszField);
125 :
126 : // GREEN
127 1213 : memcpy(pszField, pzsRecord + oColorTable.pField[nGIndex].AccumulatedBytes,
128 1213 : oColorTable.pField[nGIndex].BytesPerField);
129 1213 : pszField[oColorTable.pField[nGIndex].BytesPerField] = '\0';
130 1213 : m_aadfPaletteColors[1][nIPaletteIndex] = CPLAtof(pszField);
131 :
132 : // BLUE
133 1213 : memcpy(pszField, pzsRecord + oColorTable.pField[nBIndex].AccumulatedBytes,
134 1213 : oColorTable.pField[nBIndex].BytesPerField);
135 1213 : pszField[oColorTable.pField[nBIndex].BytesPerField] = '\0';
136 1213 : m_aadfPaletteColors[2][nIPaletteIndex] = CPLAtof(pszField);
137 :
138 : // ALPHA
139 1213 : if (m_aadfPaletteColors[0][nIPaletteIndex] == -1 &&
140 1218 : m_aadfPaletteColors[1][nIPaletteIndex] == -1 &&
141 5 : m_aadfPaletteColors[2][nIPaletteIndex] == -1)
142 : {
143 : // Transparent (white or whatever color)
144 5 : m_aadfPaletteColors[0][nIPaletteIndex] = m_sNoDataColorRGB.c1;
145 5 : m_aadfPaletteColors[1][nIPaletteIndex] = m_sNoDataColorRGB.c2;
146 5 : m_aadfPaletteColors[2][nIPaletteIndex] = m_sNoDataColorRGB.c3;
147 5 : m_aadfPaletteColors[3][nIPaletteIndex] = m_sNoDataColorRGB.c4;
148 : }
149 : else
150 1208 : m_aadfPaletteColors[3][nIPaletteIndex] = 255;
151 1213 : }
152 :
153 23 : CPLErr MMRPalettes::GetPaletteColors_DBF_Indexes(
154 : struct MM_DATA_BASE_XP &oColorTable, MM_EXT_DBF_N_FIELDS &nClauSimbol,
155 : MM_EXT_DBF_N_FIELDS &nRIndex, MM_EXT_DBF_N_FIELDS &nGIndex,
156 : MM_EXT_DBF_N_FIELDS &nBIndex)
157 : {
158 23 : nClauSimbol = oColorTable.nFields;
159 23 : nRIndex = oColorTable.nFields;
160 23 : nGIndex = oColorTable.nFields;
161 23 : nBIndex = oColorTable.nFields;
162 :
163 121 : for (MM_EXT_DBF_N_FIELDS nIField = 0; nIField < oColorTable.nFields;
164 : nIField++)
165 : {
166 98 : if (EQUAL(oColorTable.pField[nIField].FieldName, "CLAUSIMBOL"))
167 22 : nClauSimbol = nIField;
168 76 : else if (EQUAL(oColorTable.pField[nIField].FieldName, "R_COLOR"))
169 23 : nRIndex = nIField;
170 53 : else if (EQUAL(oColorTable.pField[nIField].FieldName, "G_COLOR"))
171 23 : nGIndex = nIField;
172 30 : else if (EQUAL(oColorTable.pField[nIField].FieldName, "B_COLOR"))
173 23 : nBIndex = nIField;
174 : }
175 :
176 23 : if (nClauSimbol == oColorTable.nFields || nRIndex == oColorTable.nFields ||
177 22 : nGIndex == oColorTable.nFields || nBIndex == oColorTable.nFields)
178 1 : return CE_Failure;
179 :
180 22 : return CE_None;
181 : }
182 :
183 : // Updates nNPaletteColors
184 24 : CPLErr MMRPalettes::GetPaletteColors_DBF(const CPLString &os_Color_Paleta_DBF)
185 :
186 : {
187 : // Getting the full path name of the DBF
188 48 : CPLString osAux = CPLGetPathSafe(m_pfRel->GetRELNameChar());
189 : CPLString osColorTableFileName =
190 48 : CPLFormFilenameSafe(osAux.c_str(), os_Color_Paleta_DBF.c_str(), "");
191 :
192 : // Reading the DBF file
193 : struct MM_DATA_BASE_XP oColorTable;
194 24 : memset(&oColorTable, 0, sizeof(oColorTable));
195 :
196 24 : if (MM_ReadExtendedDBFHeaderFromFile(osColorTableFileName.c_str(),
197 : &oColorTable,
198 48 : m_pfRel->GetRELNameChar()))
199 : {
200 1 : CPLError(CE_Failure, CPLE_AssertionFailed,
201 : "Invalid color table:"
202 : "\"%s\".",
203 : osColorTableFileName.c_str());
204 :
205 1 : return CE_Failure;
206 : }
207 :
208 : // Getting indices of fields that determine the colors.
209 : MM_EXT_DBF_N_FIELDS nClauSimbol, nRIndex, nGIndex, nBIndex;
210 23 : if (CE_Failure == GetPaletteColors_DBF_Indexes(oColorTable, nClauSimbol,
211 : nRIndex, nGIndex, nBIndex))
212 : {
213 1 : CPLError(CE_Failure, CPLE_AssertionFailed,
214 : "Invalid color table:"
215 : "\"%s\".",
216 : osColorTableFileName.c_str());
217 :
218 1 : VSIFCloseL(oColorTable.pfDataBase);
219 1 : MM_ReleaseMainFields(&oColorTable);
220 1 : return CE_Failure;
221 : }
222 :
223 : // Checking the structure to be correct
224 22 : if (oColorTable.pField[nClauSimbol].BytesPerField == 0 ||
225 22 : oColorTable.pField[nRIndex].BytesPerField == 0 ||
226 22 : oColorTable.pField[nGIndex].BytesPerField == 0 ||
227 22 : oColorTable.pField[nBIndex].BytesPerField == 0 ||
228 22 : oColorTable.pField[nClauSimbol].FieldType != 'N' ||
229 21 : oColorTable.pField[nRIndex].FieldType != 'N' ||
230 21 : oColorTable.pField[nGIndex].FieldType != 'N' ||
231 21 : oColorTable.pField[nBIndex].FieldType != 'N')
232 : {
233 1 : CPLError(CE_Failure, CPLE_AssertionFailed,
234 : "Invalid color table:"
235 : "\"%s\".",
236 : osColorTableFileName.c_str());
237 :
238 1 : VSIFCloseL(oColorTable.pfDataBase);
239 1 : MM_ReleaseMainFields(&oColorTable);
240 1 : return CE_Failure;
241 : }
242 :
243 : // Guessing or reading the number of colors of the palette.
244 21 : MM_ACCUMULATED_BYTES_TYPE_DBF nBufferSize = oColorTable.BytesPerRecord + 1;
245 21 : char *pzsRecord = static_cast<char *>(VSI_CALLOC_VERBOSE(1, nBufferSize));
246 21 : if (!pzsRecord)
247 : {
248 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
249 : "Out of memory allocating working buffer");
250 0 : VSIFCloseL(oColorTable.pfDataBase);
251 0 : MM_ReleaseMainFields(&oColorTable);
252 : }
253 21 : char *pszField = static_cast<char *>(VSI_CALLOC_VERBOSE(1, nBufferSize));
254 :
255 21 : if (!pszField)
256 : {
257 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
258 : "Out of memory allocating working buffer");
259 0 : VSIFree(pzsRecord);
260 0 : VSIFCloseL(oColorTable.pfDataBase);
261 0 : MM_ReleaseMainFields(&oColorTable);
262 : }
263 :
264 21 : m_nNPaletteColors = static_cast<int>(oColorTable.nRecords); // Safe cast
265 :
266 : // Checking the size of the palette.
267 21 : if (m_nNPaletteColors < 0 || m_nNPaletteColors > 65536)
268 : {
269 0 : m_nNPaletteColors = 0;
270 0 : CPLError(CE_Failure, CPLE_AppDefined,
271 : "Invalid number of colors: %d "
272 : "in color table \"%s\".",
273 : m_nNPaletteColors, osColorTableFileName.c_str());
274 :
275 0 : VSIFree(pszField);
276 0 : VSIFree(pzsRecord);
277 0 : VSIFCloseL(oColorTable.pfDataBase);
278 0 : MM_ReleaseMainFields(&oColorTable);
279 0 : return CE_Failure;
280 : }
281 :
282 : // Getting the memory to allocate the color values
283 105 : for (int iColumn = 0; iColumn < 4; iColumn++)
284 : {
285 : try
286 : {
287 84 : m_aadfPaletteColors[iColumn].resize(m_nNPaletteColors, 0);
288 : }
289 0 : catch (std::bad_alloc &e)
290 : {
291 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
292 0 : VSIFree(pszField);
293 0 : VSIFree(pzsRecord);
294 0 : VSIFCloseL(oColorTable.pfDataBase);
295 0 : MM_ReleaseMainFields(&oColorTable);
296 0 : return CE_Failure;
297 : }
298 : }
299 :
300 21 : VSIFSeekL(oColorTable.pfDataBase, oColorTable.FirstRecordOffset, SEEK_SET);
301 : /*
302 : Each record's CLAUSIMBOL field doesn't match a pixel value present in the raster,
303 : and it's used only for discovering nodata value (blanc value).
304 : The list of values is used to map every value in a color using:
305 : - Direct assignation: mode used in categorical modes but possible in continuous.
306 : - Linear scaling
307 : - Logarithmic scaling
308 : */
309 1234 : for (int nIPaletteColors = 0; nIPaletteColors < m_nNPaletteColors;
310 : nIPaletteColors++)
311 : {
312 2426 : if (oColorTable.BytesPerRecord !=
313 1213 : VSIFReadL(pzsRecord, sizeof(unsigned char),
314 1213 : oColorTable.BytesPerRecord, oColorTable.pfDataBase))
315 : {
316 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid color table: \"%s\"",
317 : osColorTableFileName.c_str());
318 0 : VSIFree(pszField);
319 0 : VSIFree(pzsRecord);
320 0 : VSIFCloseL(oColorTable.pfDataBase);
321 0 : MM_ReleaseMainFields(&oColorTable);
322 0 : return CE_Failure;
323 : }
324 :
325 : // Nodata identification
326 1213 : memcpy(pszField,
327 1213 : pzsRecord + oColorTable.pField[nClauSimbol].AccumulatedBytes,
328 1213 : oColorTable.pField[nClauSimbol].BytesPerField);
329 1213 : pszField[oColorTable.pField[nClauSimbol].BytesPerField] = '\0';
330 2426 : CPLString osField = pszField;
331 1213 : osField.replaceAll(" ", "");
332 1213 : if (osField.empty()) // Nodata value
333 : {
334 6 : m_bHasNodata = true;
335 6 : m_nNoDataPaletteIndex = nIPaletteColors;
336 : }
337 :
338 1213 : AssignColorFromDBF(oColorTable, pzsRecord, pszField, nRIndex, nGIndex,
339 : nBIndex, nIPaletteColors);
340 : }
341 :
342 21 : VSIFree(pszField);
343 21 : VSIFree(pzsRecord);
344 21 : VSIFCloseL(oColorTable.pfDataBase);
345 21 : MM_ReleaseMainFields(&oColorTable);
346 :
347 21 : return CE_None;
348 : }
349 :
350 : // Colors in a PAL, P25 or P65 format files
351 : // Updates nNPaletteColors
352 : CPLErr
353 3 : MMRPalettes::GetPaletteColors_PAL_P25_P65(const CPLString &os_Color_Paleta_DBF)
354 :
355 : {
356 6 : CPLString osAux = CPLGetPathSafe(m_pfRel->GetRELNameChar());
357 : CPLString osColorTableFileName =
358 6 : CPLFormFilenameSafe(osAux.c_str(), os_Color_Paleta_DBF.c_str(), "");
359 :
360 : // This kind of palette has not NoData color.
361 : //bHasNodata = false;
362 :
363 6 : CPLString osExtension = CPLGetExtensionSafe(os_Color_Paleta_DBF);
364 3 : int nNReadPaletteColors = 0;
365 3 : m_nNPaletteColors = 0;
366 :
367 3 : if (osExtension.tolower() == "pal")
368 1 : m_nNPaletteColors = 64;
369 2 : else if (osExtension.tolower() == "p25")
370 1 : m_nNPaletteColors = 256;
371 1 : else if (osExtension.tolower() == "p65")
372 1 : m_nNPaletteColors = 65536;
373 : else
374 0 : return CE_None;
375 :
376 15 : for (int iColumn = 0; iColumn < 4; iColumn++)
377 : {
378 : try
379 : {
380 12 : m_aadfPaletteColors[iColumn].resize(m_nNPaletteColors, 0);
381 : }
382 0 : catch (std::bad_alloc &e)
383 : {
384 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
385 0 : return CE_Failure;
386 : }
387 : }
388 :
389 3 : VSILFILE *fpColorTable = VSIFOpenL(osColorTableFileName, "rt");
390 3 : if (!fpColorTable)
391 : {
392 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid color table: \"%s\"",
393 : osColorTableFileName.c_str());
394 0 : return CE_Failure;
395 : }
396 :
397 3 : nNReadPaletteColors = 0;
398 : const char *pszLine;
399 51 : while ((pszLine = CPLReadLineL(fpColorTable)) != nullptr)
400 : {
401 : // Ignore empty lines
402 48 : if (pszLine[0] == '\0')
403 0 : continue;
404 :
405 48 : const CPLStringList aosTokens(CSLTokenizeString2(pszLine, " \t", 0));
406 48 : if (aosTokens.size() != 4)
407 : {
408 0 : VSIFCloseL(fpColorTable);
409 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid color table: \"%s\"",
410 : osColorTableFileName.c_str());
411 0 : return CE_Failure;
412 : }
413 :
414 : // Index of the color
415 : // papszTokens[0] is ignored;
416 :
417 : // RED
418 48 : m_aadfPaletteColors[0][nNReadPaletteColors] = CPLAtof(aosTokens[1]);
419 :
420 : // GREEN
421 48 : m_aadfPaletteColors[1][nNReadPaletteColors] = CPLAtof(aosTokens[2]);
422 :
423 : // BLUE
424 48 : m_aadfPaletteColors[2][nNReadPaletteColors] = CPLAtof(aosTokens[3]);
425 :
426 : // ALPHA
427 48 : m_aadfPaletteColors[3][nNReadPaletteColors] = 255.0;
428 48 : nNReadPaletteColors++;
429 : }
430 :
431 : // Filling the rest of colors.
432 3 : for (int nIColorIndex = nNReadPaletteColors;
433 65811 : nIColorIndex < m_nNPaletteColors; nIColorIndex++)
434 : {
435 65808 : m_aadfPaletteColors[0][nNReadPaletteColors] = m_sDefaultColorRGB.c1;
436 65808 : m_aadfPaletteColors[1][nNReadPaletteColors] = m_sDefaultColorRGB.c2;
437 65808 : m_aadfPaletteColors[2][nNReadPaletteColors] = m_sDefaultColorRGB.c3;
438 65808 : m_aadfPaletteColors[3][nNReadPaletteColors] = m_sDefaultColorRGB.c4;
439 65808 : nNReadPaletteColors++;
440 : }
441 :
442 3 : if (nNReadPaletteColors != m_nNPaletteColors)
443 : {
444 0 : VSIFCloseL(fpColorTable);
445 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid color table: \"%s\"",
446 : osColorTableFileName.c_str());
447 0 : return CE_Failure;
448 : }
449 :
450 3 : VSIFCloseL(fpColorTable);
451 :
452 3 : return CE_None;
453 : }
454 :
455 27 : void MMRPalettes::UpdateColorInfo()
456 : {
457 54 : CPLString os_Color_EscalatColor;
458 27 : if (m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, m_osBandSection,
459 : "Color_EscalatColor",
460 32 : os_Color_EscalatColor) &&
461 5 : !os_Color_EscalatColor.empty())
462 : {
463 5 : if (os_Color_EscalatColor.compare("AssigDirecta") == 0)
464 2 : ColorScaling = ColorTreatment::DIRECT_ASSIGNATION;
465 3 : else if (os_Color_EscalatColor.compare("DespOrigen") == 0)
466 0 : ColorScaling = ColorTreatment::ORIGIN_DISPLACEMENT;
467 3 : else if (os_Color_EscalatColor.compare("lineal") == 0)
468 3 : ColorScaling = ColorTreatment::LINEAR_SCALING;
469 0 : else if (os_Color_EscalatColor.compare("log_10") == 0)
470 0 : ColorScaling = ColorTreatment::LOG_10_SCALING;
471 0 : else if (os_Color_EscalatColor.compare("IntervalsUsuari") == 0)
472 0 : ColorScaling = ColorTreatment::USER_INTERVALS;
473 : }
474 : else
475 : {
476 22 : if (IsCategorical())
477 20 : ColorScaling = ColorTreatment::DIRECT_ASSIGNATION;
478 : else
479 2 : ColorScaling = ColorTreatment::LINEAR_SCALING;
480 : }
481 27 : }
482 :
483 2 : CPLErr MMRPalettes::UpdateConstantColor()
484 : {
485 : // Example: Color_Smb=(255,0,255)
486 4 : CPLString os_Color_Smb;
487 2 : if (!m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, m_osBandSection,
488 : "Color_Smb", os_Color_Smb))
489 0 : return CE_None;
490 :
491 2 : os_Color_Smb.replaceAll(" ", "");
492 4 : if (!os_Color_Smb.empty() && os_Color_Smb.size() >= 7 &&
493 4 : os_Color_Smb[0] == '(' && os_Color_Smb[os_Color_Smb.size() - 1] == ')')
494 : {
495 2 : os_Color_Smb.replaceAll("(", "");
496 2 : os_Color_Smb.replaceAll(")", "");
497 2 : const CPLStringList aosTokens(CSLTokenizeString2(os_Color_Smb, ",", 0));
498 2 : if (CSLCount(aosTokens) != 3)
499 : {
500 0 : CPLError(CE_Failure, CPLE_AppDefined,
501 : "Invalid constant color: \"%s\"",
502 0 : m_pfRel->GetRELNameChar());
503 0 : return CE_Failure;
504 : }
505 :
506 : int nIColor0;
507 2 : if (1 != sscanf(aosTokens[0], "%d", &nIColor0))
508 : {
509 0 : CPLError(CE_Failure, CPLE_AppDefined,
510 : "Invalid constant color: \"%s\"",
511 0 : m_pfRel->GetRELNameChar());
512 0 : return CE_Failure;
513 : }
514 :
515 : int nIColor1;
516 2 : if (1 != sscanf(aosTokens[1], "%d", &nIColor1))
517 : {
518 0 : CPLError(CE_Failure, CPLE_AppDefined,
519 : "Invalid constant color: \"%s\"",
520 0 : m_pfRel->GetRELNameChar());
521 0 : return CE_Failure;
522 : }
523 :
524 : int nIColor2;
525 2 : if (1 != sscanf(aosTokens[2], "%d", &nIColor2))
526 : {
527 0 : CPLError(CE_Failure, CPLE_AppDefined,
528 : "Invalid constant color: \"%s\"",
529 0 : m_pfRel->GetRELNameChar());
530 0 : return CE_Failure;
531 : }
532 :
533 2 : SetConstantColorRGB(static_cast<short>(nIColor0),
534 : static_cast<short>(nIColor1),
535 : static_cast<short>(nIColor2));
536 : }
537 2 : return CE_None;
538 : }
|