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

Generated by: LCOV version 1.14