Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: XPM Driver
4 : * Purpose: Implement GDAL XPM Support
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2002, Frank Warmerdam
9 : * Copyright (c) 2008-2010, 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 "gdal_pam.h"
31 : #include "cpl_string.h"
32 : #include "memdataset.h"
33 : #include "gdal_frmts.h"
34 :
35 : #include <cstdlib>
36 : #include <algorithm>
37 :
38 : static unsigned char *ParseXPM(const char *pszInput, unsigned int nFileSize,
39 : int *pnXSize, int *pnYSize,
40 : GDALColorTable **ppoRetTable);
41 :
42 : /************************************************************************/
43 : /* ==================================================================== */
44 : /* XPMDataset */
45 : /* ==================================================================== */
46 : /************************************************************************/
47 :
48 : class XPMDataset final : public GDALPamDataset
49 : {
50 : public:
51 4 : XPMDataset()
52 4 : {
53 4 : }
54 :
55 : ~XPMDataset();
56 :
57 : static GDALDataset *Open(GDALOpenInfo *);
58 : static int Identify(GDALOpenInfo *);
59 : };
60 :
61 : /************************************************************************/
62 : /* ~XPMDataset() */
63 : /************************************************************************/
64 :
65 8 : XPMDataset::~XPMDataset()
66 :
67 : {
68 4 : FlushCache(true);
69 8 : }
70 :
71 : /************************************************************************/
72 : /* Identify() */
73 : /************************************************************************/
74 :
75 52939 : int XPMDataset::Identify(GDALOpenInfo *poOpenInfo)
76 :
77 : {
78 : /* -------------------------------------------------------------------- */
79 : /* First we check to see if the file has the expected header */
80 : /* bytes. For now we expect the XPM file to start with a line */
81 : /* containing the letters XPM, and to have "static" in the */
82 : /* header. */
83 : /* -------------------------------------------------------------------- */
84 59115 : return poOpenInfo->nHeaderBytes >= 32 &&
85 6176 : strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
86 59115 : "XPM") != nullptr &&
87 26 : strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
88 52939 : "static") != nullptr;
89 : }
90 :
91 : /************************************************************************/
92 : /* Open() */
93 : /************************************************************************/
94 :
95 13 : GDALDataset *XPMDataset::Open(GDALOpenInfo *poOpenInfo)
96 :
97 : {
98 13 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
99 0 : return nullptr;
100 :
101 13 : if (poOpenInfo->eAccess == GA_Update)
102 : {
103 0 : CPLError(CE_Failure, CPLE_NotSupported,
104 : "The XPM driver does not support update access to existing"
105 : " files.");
106 0 : return nullptr;
107 : }
108 :
109 : /* -------------------------------------------------------------------- */
110 : /* Read the whole file into a memory strings. */
111 : /* -------------------------------------------------------------------- */
112 13 : VSILFILE *fp = poOpenInfo->fpL;
113 13 : poOpenInfo->fpL = nullptr;
114 :
115 13 : if (VSIFSeekL(fp, 0, SEEK_END) != 0)
116 : {
117 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
118 0 : return nullptr;
119 : }
120 13 : unsigned int nFileSize = static_cast<unsigned int>(VSIFTellL(fp));
121 :
122 : char *pszFileContents =
123 13 : reinterpret_cast<char *>(VSI_MALLOC_VERBOSE(nFileSize + 1));
124 13 : if (pszFileContents == nullptr)
125 : {
126 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
127 0 : return nullptr;
128 : }
129 13 : pszFileContents[nFileSize] = '\0';
130 :
131 26 : if (VSIFSeekL(fp, 0, SEEK_SET) != 0 ||
132 13 : VSIFReadL(pszFileContents, 1, nFileSize, fp) != nFileSize)
133 : {
134 0 : CPLFree(pszFileContents);
135 0 : CPLError(CE_Failure, CPLE_FileIO,
136 : "Failed to read all %d bytes from file %s.", nFileSize,
137 : poOpenInfo->pszFilename);
138 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
139 0 : return nullptr;
140 : }
141 :
142 13 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
143 13 : fp = nullptr;
144 :
145 : /* -------------------------------------------------------------------- */
146 : /* Convert into a binary image. */
147 : /* -------------------------------------------------------------------- */
148 13 : CPLErrorReset();
149 :
150 : int nXSize;
151 : int nYSize;
152 13 : GDALColorTable *poCT = nullptr;
153 :
154 : GByte *pabyImage =
155 13 : ParseXPM(pszFileContents, nFileSize, &nXSize, &nYSize, &poCT);
156 :
157 13 : CPLFree(pszFileContents);
158 :
159 13 : if (pabyImage == nullptr)
160 : {
161 9 : return nullptr;
162 : }
163 :
164 : /* -------------------------------------------------------------------- */
165 : /* Create a corresponding GDALDataset. */
166 : /* -------------------------------------------------------------------- */
167 4 : XPMDataset *poDS = new XPMDataset();
168 :
169 : /* -------------------------------------------------------------------- */
170 : /* Capture some information from the file that is of interest. */
171 : /* -------------------------------------------------------------------- */
172 4 : poDS->nRasterXSize = nXSize;
173 4 : poDS->nRasterYSize = nYSize;
174 :
175 : /* -------------------------------------------------------------------- */
176 : /* Create band information objects. */
177 : /* -------------------------------------------------------------------- */
178 : MEMRasterBand *poBand =
179 4 : new MEMRasterBand(poDS, 1, pabyImage, GDT_Byte, 1, nXSize, TRUE);
180 4 : poBand->SetColorTable(poCT);
181 4 : poDS->SetBand(1, poBand);
182 :
183 4 : delete poCT;
184 :
185 : /* -------------------------------------------------------------------- */
186 : /* Initialize any PAM information. */
187 : /* -------------------------------------------------------------------- */
188 4 : poDS->SetDescription(poOpenInfo->pszFilename);
189 4 : poDS->TryLoadXML();
190 :
191 : /* -------------------------------------------------------------------- */
192 : /* Support overviews. */
193 : /* -------------------------------------------------------------------- */
194 4 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
195 :
196 4 : return poDS;
197 : }
198 :
199 : /************************************************************************/
200 : /* XPMCreateCopy() */
201 : /************************************************************************/
202 :
203 29 : static GDALDataset *XPMCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
204 : int bStrict, char ** /* papszOptions */,
205 : GDALProgressFunc /* pfnProgress */,
206 : void * /* pProgressData */)
207 : {
208 : /* -------------------------------------------------------------------- */
209 : /* Some some rudimentary checks */
210 : /* -------------------------------------------------------------------- */
211 29 : const int nBands = poSrcDS->GetRasterCount();
212 29 : if (nBands != 1)
213 : {
214 5 : CPLError(CE_Failure, CPLE_NotSupported,
215 : "XPM driver only supports one band images.\n");
216 :
217 5 : return nullptr;
218 : }
219 :
220 24 : if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte && bStrict)
221 : {
222 10 : CPLError(CE_Failure, CPLE_NotSupported,
223 : "XPM driver doesn't support data type %s. "
224 : "Only eight bit bands supported.\n",
225 : GDALGetDataTypeName(
226 : poSrcDS->GetRasterBand(1)->GetRasterDataType()));
227 :
228 10 : return nullptr;
229 : }
230 :
231 : /* -------------------------------------------------------------------- */
232 : /* If there is no colortable on the source image, create a */
233 : /* greyscale one with 64 levels of grey. */
234 : /* -------------------------------------------------------------------- */
235 14 : GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
236 :
237 28 : GDALColorTable oGreyTable;
238 14 : GDALColorTable *poCT = poBand->GetColorTable();
239 :
240 14 : if (poCT == nullptr)
241 : {
242 14 : poCT = &oGreyTable;
243 :
244 3598 : for (int i = 0; i < 256; i++)
245 : {
246 : GDALColorEntry sColor;
247 :
248 3584 : sColor.c1 = (short)i;
249 3584 : sColor.c2 = (short)i;
250 3584 : sColor.c3 = (short)i;
251 3584 : sColor.c4 = 255;
252 :
253 3584 : poCT->SetColorEntry(i, &sColor);
254 : }
255 : }
256 :
257 : /* -------------------------------------------------------------------- */
258 : /* Build list of active colors, and the mapping from pixels to */
259 : /* our active colormap. */
260 : /* -------------------------------------------------------------------- */
261 14 : const char *pszColorCodes =
262 : " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@"
263 : "#$%^&*()-+=[]|:;,.<>?/";
264 :
265 : int anPixelMapping[256];
266 : GDALColorEntry asPixelColor[256];
267 14 : int nActiveColors = std::min(poCT->GetColorEntryCount(), 256);
268 :
269 : // Setup initial colortable and pixel value mapping.
270 14 : memset(anPixelMapping + 0, 0, sizeof(int) * 256);
271 3598 : for (int i = 0; i < nActiveColors; i++)
272 : {
273 3584 : poCT->GetColorEntryAsRGB(i, asPixelColor + i);
274 3584 : anPixelMapping[i] = i;
275 : }
276 :
277 : /* ==================================================================== */
278 : /* Iterate merging colors until we are under our limit (about 85). */
279 : /* ==================================================================== */
280 2380 : while (nActiveColors > static_cast<int>(strlen(pszColorCodes)))
281 : {
282 2366 : int nClosestDistance = 768;
283 2366 : int iClose1 = -1;
284 2366 : int iClose2 = -1;
285 :
286 : // Find the closest pair of colors.
287 101150 : for (int iColor1 = 0; iColor1 < nActiveColors; iColor1++)
288 : {
289 11766700 : for (int iColor2 = iColor1 + 1; iColor2 < nActiveColors; iColor2++)
290 : {
291 : int nDistance;
292 :
293 11665600 : if (asPixelColor[iColor1].c4 < 128 &&
294 0 : asPixelColor[iColor2].c4 < 128)
295 0 : nDistance = 0;
296 : else
297 11665600 : nDistance = std::abs(asPixelColor[iColor1].c1 -
298 11665600 : asPixelColor[iColor2].c1) +
299 11665600 : std::abs(asPixelColor[iColor1].c2 -
300 11665600 : asPixelColor[iColor2].c2) +
301 11665600 : std::abs(asPixelColor[iColor1].c3 -
302 11665600 : asPixelColor[iColor2].c3);
303 :
304 11665600 : if (nDistance < nClosestDistance)
305 : {
306 9786 : nClosestDistance = nDistance;
307 9786 : iClose1 = iColor1;
308 9786 : iClose2 = iColor2;
309 : }
310 : }
311 :
312 101150 : if (nClosestDistance < 8)
313 2366 : break;
314 : }
315 :
316 : // This should never happen!
317 2366 : if (iClose1 == -1)
318 0 : break;
319 :
320 : // Merge two selected colors - shift icolor2 into icolor1 and
321 : // move the last active color into icolor2's slot.
322 608062 : for (int i = 0; i < 256; i++)
323 : {
324 605696 : if (anPixelMapping[i] == iClose2)
325 2366 : anPixelMapping[i] = iClose1;
326 603330 : else if (anPixelMapping[i] == nActiveColors - 1)
327 1582 : anPixelMapping[i] = iClose2;
328 : }
329 :
330 2366 : asPixelColor[iClose2] = asPixelColor[nActiveColors - 1];
331 2366 : nActiveColors--;
332 : }
333 :
334 : /* ==================================================================== */
335 : /* Write the output image. */
336 : /* ==================================================================== */
337 14 : VSILFILE *fpPBM = VSIFOpenL(pszFilename, "wb+");
338 14 : if (fpPBM == nullptr)
339 : {
340 2 : CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create file `%s'.",
341 : pszFilename);
342 :
343 2 : return nullptr;
344 : }
345 :
346 : /* -------------------------------------------------------------------- */
347 : /* Write the header lines. */
348 : /* -------------------------------------------------------------------- */
349 12 : bool bOK = VSIFPrintfL(fpPBM, "/* XPM */\n") >= 0;
350 12 : bOK &= VSIFPrintfL(fpPBM, "static char *%s[] = {\n",
351 12 : CPLGetBasename(pszFilename)) >= 0;
352 12 : bOK &= VSIFPrintfL(fpPBM,
353 12 : "/* width height num_colors chars_per_pixel */\n") >= 0;
354 :
355 12 : const int nXSize = poSrcDS->GetRasterXSize();
356 12 : const int nYSize = poSrcDS->GetRasterYSize();
357 :
358 12 : bOK &= VSIFPrintfL(fpPBM, "\" %3d %3d %3d 1\",\n",
359 12 : nXSize, nYSize, nActiveColors) >= 0;
360 :
361 12 : bOK &= VSIFPrintfL(fpPBM, "/* colors */\n") >= 0;
362 :
363 : /* -------------------------------------------------------------------- */
364 : /* Write the color table. */
365 : /* -------------------------------------------------------------------- */
366 1056 : for (int i = 0; bOK && i < nActiveColors; i++)
367 : {
368 1044 : if (asPixelColor[i].c4 < 128)
369 0 : bOK &=
370 0 : VSIFPrintfL(fpPBM, "\"%c c None\",\n", pszColorCodes[i]) >= 0;
371 : else
372 2088 : bOK &= VSIFPrintfL(fpPBM, "\"%c c #%02x%02x%02x\",\n",
373 1044 : pszColorCodes[i], asPixelColor[i].c1,
374 1044 : asPixelColor[i].c2, asPixelColor[i].c3) >= 0;
375 : }
376 :
377 : /* -------------------------------------------------------------------- */
378 : /* Dump image. */
379 : /* -------------------------------------------------------------------- */
380 12 : GByte *pabyScanline = reinterpret_cast<GByte *>(CPLMalloc(nXSize));
381 :
382 142 : for (int iLine = 0; bOK && iLine < nYSize; iLine++)
383 : {
384 130 : if (poBand->RasterIO(GF_Read, 0, iLine, nXSize, 1,
385 : reinterpret_cast<void *>(pabyScanline), nXSize, 1,
386 130 : GDT_Byte, 0, 0, nullptr) != CE_None)
387 : {
388 0 : CPLFree(pabyScanline);
389 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpPBM));
390 0 : return nullptr;
391 : }
392 :
393 130 : bOK &= VSIFPutcL('"', fpPBM) >= 0;
394 1630 : for (int iPixel = 0; iPixel < nXSize; iPixel++)
395 1500 : bOK &=
396 1500 : VSIFPutcL(pszColorCodes[anPixelMapping[pabyScanline[iPixel]]],
397 1500 : fpPBM) >= 0;
398 130 : bOK &= VSIFPrintfL(fpPBM, "\",\n") >= 0;
399 : }
400 :
401 12 : CPLFree(pabyScanline);
402 :
403 : /* -------------------------------------------------------------------- */
404 : /* cleanup */
405 : /* -------------------------------------------------------------------- */
406 12 : bOK &= VSIFPrintfL(fpPBM, "};\n") >= 0;
407 12 : if (VSIFCloseL(fpPBM) != 0)
408 0 : bOK = false;
409 :
410 12 : if (!bOK)
411 0 : return nullptr;
412 :
413 : /* -------------------------------------------------------------------- */
414 : /* Re-open dataset, and copy any auxiliary pam information. */
415 : /* -------------------------------------------------------------------- */
416 : GDALPamDataset *poDS =
417 12 : reinterpret_cast<GDALPamDataset *>(GDALOpen(pszFilename, GA_ReadOnly));
418 :
419 12 : if (poDS)
420 2 : poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
421 :
422 12 : return poDS;
423 : }
424 :
425 : /************************************************************************/
426 : /* GDALRegister_XPM() */
427 : /************************************************************************/
428 :
429 1520 : void GDALRegister_XPM()
430 :
431 : {
432 1520 : if (GDALGetDriverByName("XPM") != nullptr)
433 301 : return;
434 :
435 1219 : GDALDriver *poDriver = new GDALDriver();
436 :
437 1219 : poDriver->SetDescription("XPM");
438 1219 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
439 1219 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "X11 PixMap Format");
440 1219 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/xpm.html");
441 1219 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "xpm");
442 1219 : poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/x-xpixmap");
443 1219 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
444 1219 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
445 :
446 1219 : poDriver->pfnOpen = XPMDataset::Open;
447 1219 : poDriver->pfnIdentify = XPMDataset::Identify;
448 1219 : poDriver->pfnCreateCopy = XPMCreateCopy;
449 :
450 1219 : GetGDALDriverManager()->RegisterDriver(poDriver);
451 : }
452 :
453 : /************************************************************************/
454 : /* ParseXPM() */
455 : /************************************************************************/
456 :
457 13 : static unsigned char *ParseXPM(const char *pszInput, unsigned int nFileSize,
458 : int *pnXSize, int *pnYSize,
459 : GDALColorTable **ppoRetTable)
460 :
461 : {
462 : /* ==================================================================== */
463 : /* Parse input into an array of strings from within the first C */
464 : /* initializer (list os comma separated strings in braces). */
465 : /* ==================================================================== */
466 13 : const char *pszNext = pszInput;
467 :
468 : // Skip till after open brace.
469 587 : while (*pszNext != '\0' && *pszNext != '{')
470 574 : pszNext++;
471 :
472 13 : if (*pszNext == '\0')
473 0 : return nullptr;
474 :
475 13 : pszNext++;
476 :
477 : // Read lines till close brace.
478 :
479 13 : char **papszXPMList = nullptr;
480 13 : int i = 0;
481 :
482 2566 : while (*pszNext != '\0' && *pszNext != '}')
483 : {
484 : // skip whole comment.
485 2559 : if (STARTS_WITH_CI(pszNext, "/*"))
486 : {
487 26 : pszNext += 2;
488 663 : while (*pszNext != '\0' && !STARTS_WITH_CI(pszNext, "*/"))
489 637 : pszNext++;
490 : }
491 :
492 : // reading string constants
493 2533 : else if (*pszNext == '"')
494 : {
495 820 : pszNext++;
496 820 : i = 0;
497 :
498 10620 : while (pszNext[i] != '\0' && pszNext[i] != '"')
499 9800 : i++;
500 :
501 820 : if (pszNext[i] == '\0')
502 : {
503 6 : CSLDestroy(papszXPMList);
504 6 : return nullptr;
505 : }
506 :
507 814 : char *pszLine = reinterpret_cast<char *>(CPLMalloc(i + 1));
508 814 : strncpy(pszLine, pszNext, i);
509 814 : pszLine[i] = '\0';
510 :
511 814 : papszXPMList = CSLAddString(papszXPMList, pszLine);
512 814 : CPLFree(pszLine);
513 814 : pszNext = pszNext + i + 1;
514 : }
515 :
516 : // just ignore everything else (whitespace, commas, newlines, etc).
517 : else
518 1713 : pszNext++;
519 : }
520 :
521 14 : if (papszXPMList == nullptr || CSLCount(papszXPMList) < 3 ||
522 7 : *pszNext != '}')
523 : {
524 3 : CSLDestroy(papszXPMList);
525 3 : return nullptr;
526 : }
527 :
528 : /* -------------------------------------------------------------------- */
529 : /* Get the image information. */
530 : /* -------------------------------------------------------------------- */
531 : int nColorCount, nCharsPerPixel;
532 :
533 8 : if (sscanf(papszXPMList[0], "%d %d %d %d", pnXSize, pnYSize, &nColorCount,
534 4 : &nCharsPerPixel) != 4 ||
535 4 : *pnXSize <= 0 || *pnYSize <= 0 || nColorCount <= 0 ||
536 12 : nColorCount > 256 ||
537 4 : static_cast<GUIntBig>(*pnXSize) * static_cast<GUIntBig>(*pnYSize) >
538 4 : nFileSize)
539 : {
540 0 : CPLError(CE_Failure, CPLE_AppDefined,
541 : "Image definition (%s) not well formed.", papszXPMList[0]);
542 0 : CSLDestroy(papszXPMList);
543 0 : return nullptr;
544 : }
545 :
546 4 : if (nCharsPerPixel != 1)
547 : {
548 0 : CPLError(CE_Failure, CPLE_AppDefined,
549 : "Only one character per pixel XPM images supported by GDAL at "
550 : "this time.");
551 0 : CSLDestroy(papszXPMList);
552 0 : return nullptr;
553 : }
554 :
555 : /* -------------------------------------------------------------------- */
556 : /* Parse out colors. */
557 : /* -------------------------------------------------------------------- */
558 : int anCharLookup[256];
559 8 : GDALColorTable oCTable;
560 :
561 1028 : for (i = 0; i < 256; i++)
562 1024 : anCharLookup[i] = -1;
563 :
564 352 : for (int iColor = 0; iColor < nColorCount; iColor++)
565 : {
566 348 : if (papszXPMList[iColor + 1] == nullptr ||
567 348 : papszXPMList[iColor + 1][0] == '\0')
568 : {
569 0 : CPLError(CE_Failure, CPLE_AppDefined,
570 : "Missing color definition for %d in XPM header.",
571 : iColor + 1);
572 0 : CSLDestroy(papszXPMList);
573 0 : return nullptr;
574 : }
575 :
576 348 : char **papszTokens = CSLTokenizeString(papszXPMList[iColor + 1] + 1);
577 :
578 348 : if (CSLCount(papszTokens) != 2 || !EQUAL(papszTokens[0], "c"))
579 : {
580 0 : CPLError(CE_Failure, CPLE_AppDefined,
581 : "Ill formed color definition (%s) in XPM header.",
582 0 : papszXPMList[iColor + 1]);
583 0 : CSLDestroy(papszXPMList);
584 0 : CSLDestroy(papszTokens);
585 0 : return nullptr;
586 : }
587 :
588 348 : anCharLookup[*(reinterpret_cast<GByte *>(papszXPMList[iColor + 1]))] =
589 : iColor;
590 :
591 : GDALColorEntry sColor;
592 : unsigned int nRed, nGreen, nBlue;
593 :
594 348 : if (EQUAL(papszTokens[1], "None"))
595 : {
596 0 : sColor.c1 = 0;
597 0 : sColor.c2 = 0;
598 0 : sColor.c3 = 0;
599 0 : sColor.c4 = 0;
600 : }
601 348 : else if (sscanf(papszTokens[1], "#%02x%02x%02x", &nRed, &nGreen,
602 348 : &nBlue) != 3)
603 : {
604 0 : CPLError(CE_Failure, CPLE_AppDefined,
605 : "Ill formed color definition (%s) in XPM header.",
606 0 : papszXPMList[iColor + 1]);
607 0 : CSLDestroy(papszXPMList);
608 0 : CSLDestroy(papszTokens);
609 0 : return nullptr;
610 : }
611 : else
612 : {
613 348 : sColor.c1 = static_cast<short>(nRed);
614 348 : sColor.c2 = static_cast<short>(nGreen);
615 348 : sColor.c3 = static_cast<short>(nBlue);
616 348 : sColor.c4 = 255;
617 : }
618 :
619 348 : oCTable.SetColorEntry(iColor, &sColor);
620 :
621 348 : CSLDestroy(papszTokens);
622 : }
623 :
624 : /* -------------------------------------------------------------------- */
625 : /* Prepare image buffer. */
626 : /* -------------------------------------------------------------------- */
627 : GByte *pabyImage =
628 4 : reinterpret_cast<GByte *>(VSI_CALLOC_VERBOSE(*pnXSize, *pnYSize));
629 4 : if (pabyImage == nullptr)
630 : {
631 0 : CSLDestroy(papszXPMList);
632 0 : return nullptr;
633 : }
634 :
635 : /* -------------------------------------------------------------------- */
636 : /* Parse image. */
637 : /* -------------------------------------------------------------------- */
638 74 : for (int iLine = 0; iLine < *pnYSize; iLine++)
639 : {
640 70 : const GByte *pabyInLine =
641 70 : reinterpret_cast<GByte *>(papszXPMList[iLine + nColorCount + 1]);
642 :
643 70 : if (pabyInLine == nullptr)
644 : {
645 0 : CPLFree(pabyImage);
646 0 : CSLDestroy(papszXPMList);
647 0 : CPLError(CE_Failure, CPLE_AppDefined,
648 : "Insufficient imagery lines in XPM image.");
649 0 : return nullptr;
650 : }
651 :
652 1370 : for (int iPixel = 0; iPixel < *pnXSize; iPixel++)
653 : {
654 1300 : if (pabyInLine[iPixel] == '\0')
655 0 : break;
656 1300 : const int nPixelValue = anCharLookup[pabyInLine[iPixel]];
657 1300 : if (nPixelValue != -1)
658 1300 : pabyImage[iLine * *pnXSize + iPixel] =
659 : static_cast<GByte>(nPixelValue);
660 : }
661 : }
662 :
663 4 : CSLDestroy(papszXPMList);
664 :
665 4 : *ppoRetTable = oCTable.Clone();
666 :
667 4 : return pabyImage;
668 : }
|