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: 2025-02-20 10:14:44 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       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             : }

Generated by: LCOV version 1.14