LCOV - code coverage report
Current view: top level - frmts/xpm - xpmdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 219 271 80.8 %
Date: 2024-11-21 22:18:42 Functions: 8 8 100.0 %

          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             : }

Generated by: LCOV version 1.14