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