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