LCOV - code coverage report
Current view: top level - frmts/kea - keacopy.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 230 252 91.3 %
Date: 2024-04-27 17:22:41 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /*
       2             :  *  keacopy.cpp
       3             :  *
       4             :  *  Created by Pete Bunting on 01/08/2012.
       5             :  *  Copyright 2012 LibKEA. All rights reserved.
       6             :  *
       7             :  *  This file is part of LibKEA.
       8             :  *
       9             :  *  Permission is hereby granted, free of charge, to any person
      10             :  *  obtaining a copy of this software and associated documentation
      11             :  *  files (the "Software"), to deal in the Software without restriction,
      12             :  *  including without limitation the rights to use, copy, modify,
      13             :  *  merge, publish, distribute, sublicense, and/or sell copies of the
      14             :  *  Software, and to permit persons to whom the Software is furnished
      15             :  *  to do so, subject to the following conditions:
      16             :  *
      17             :  *  The above copyright notice and this permission notice shall be
      18             :  *  included in all copies or substantial portions of the Software.
      19             :  *
      20             :  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      21             :  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
      22             :  *  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      23             :  *  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
      24             :  *  ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
      25             :  *  CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
      26             :  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      27             :  *
      28             :  */
      29             : 
      30             : #include <cmath>
      31             : #include "gdal_priv.h"
      32             : #include "gdal_rat.h"
      33             : 
      34             : #include "keacopy.h"
      35             : 
      36             : // Support functions for CreateCopy()
      37             : 
      38             : // Copies GDAL Band to KEA Band if nOverview == -1
      39             : // Otherwise it is assumed we are writing to the specified overview
      40          89 : static bool KEACopyRasterData(GDALRasterBand *pBand,
      41             :                               kealib::KEAImageIO *pImageIO, int nBand,
      42             :                               int nOverview, int nTotalBands,
      43             :                               GDALProgressFunc pfnProgress, void *pProgressData)
      44             : {
      45             :     // get some info
      46          89 :     kealib::KEADataType eKeaType = pImageIO->getImageBandDataType(nBand);
      47             :     unsigned int nBlockSize;
      48          89 :     if (nOverview == -1)
      49          88 :         nBlockSize = pImageIO->getImageBlockSize(nBand);
      50             :     else
      51           1 :         nBlockSize = pImageIO->getOverviewBlockSize(nBand, nOverview);
      52             : 
      53          89 :     GDALDataType eGDALType = pBand->GetRasterDataType();
      54          89 :     unsigned int nXSize = pBand->GetXSize();
      55          89 :     unsigned int nYSize = pBand->GetYSize();
      56             : 
      57             :     // allocate some space
      58          89 :     int nPixelSize = GDALGetDataTypeSize(eGDALType) / 8;
      59          89 :     void *pData = VSIMalloc3(nPixelSize, nBlockSize, nBlockSize);
      60          89 :     if (pData == nullptr)
      61             :     {
      62           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unable to allocate memory");
      63           0 :         return false;
      64             :     }
      65             :     // for progress
      66          89 :     int nTotalBlocks =
      67          89 :         static_cast<int>(std::ceil((double)nXSize / (double)nBlockSize) *
      68          89 :                          std::ceil((double)nYSize / (double)nBlockSize));
      69          89 :     int nBlocksComplete = 0;
      70          89 :     double dLastFraction = -1;
      71             :     // go through the image
      72         179 :     for (unsigned int nY = 0; nY < nYSize; nY += nBlockSize)
      73             :     {
      74             :         // adjust for edge blocks
      75          90 :         unsigned int nysize = nBlockSize;
      76          90 :         unsigned int nytotalsize = nY + nBlockSize;
      77          90 :         if (nytotalsize > nYSize)
      78           1 :             nysize -= (nytotalsize - nYSize);
      79         182 :         for (unsigned int nX = 0; nX < nXSize; nX += nBlockSize)
      80             :         {
      81             :             // adjust for edge blocks
      82          92 :             unsigned int nxsize = nBlockSize;
      83          92 :             unsigned int nxtotalsize = nX + nBlockSize;
      84          92 :             if (nxtotalsize > nXSize)
      85           2 :                 nxsize -= (nxtotalsize - nXSize);
      86             : 
      87             :             // read in from GDAL
      88         184 :             if (pBand->RasterIO(GF_Read, nX, nY, nxsize, nysize, pData, nxsize,
      89             :                                 nysize, eGDALType, nPixelSize,
      90          92 :                                 nPixelSize * nBlockSize, nullptr) != CE_None)
      91             :             {
      92           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
      93             :                          "Unable to read block at %d %d\n", nX, nY);
      94           0 :                 return false;
      95             :             }
      96             :             // write out to KEA
      97          92 :             if (nOverview == -1)
      98          91 :                 pImageIO->writeImageBlock2Band(nBand, pData, nX, nY, nxsize,
      99             :                                                nysize, nBlockSize, nBlockSize,
     100             :                                                eKeaType);
     101             :             else
     102           1 :                 pImageIO->writeToOverview(nBand, nOverview, pData, nX, nY,
     103             :                                           nxsize, nysize, nBlockSize,
     104             :                                           nBlockSize, eKeaType);
     105             : 
     106             :             // progress
     107          92 :             nBlocksComplete++;
     108          92 :             if (nOverview == -1)
     109             :             {
     110          91 :                 double dFraction =
     111          91 :                     (((double)nBlocksComplete / (double)nTotalBlocks) /
     112          91 :                      (double)nTotalBands) +
     113          91 :                     ((double)(nBand - 1) * (1.0 / (double)nTotalBands));
     114          91 :                 if (dFraction != dLastFraction)
     115             :                 {
     116          91 :                     if (!pfnProgress(dFraction, nullptr, pProgressData))
     117             :                     {
     118           0 :                         CPLFree(pData);
     119           0 :                         return false;
     120             :                     }
     121          91 :                     dLastFraction = dFraction;
     122             :                 }
     123             :             }
     124             :         }
     125             :     }
     126             : 
     127          89 :     CPLFree(pData);
     128          89 :     return true;
     129             : }
     130             : 
     131             : constexpr int RAT_CHUNKSIZE = 1000;
     132             : 
     133             : // copies the raster attribute table
     134          88 : static void KEACopyRAT(GDALRasterBand *pBand, kealib::KEAImageIO *pImageIO,
     135             :                        int nBand)
     136             : {
     137          88 :     const GDALRasterAttributeTable *gdalAtt = pBand->GetDefaultRAT();
     138          88 :     if ((gdalAtt != nullptr) && (gdalAtt->GetRowCount() > 0))
     139             :     {
     140             :         // some operations depend on whether the input dataset is HFA
     141             :         int bInputHFA =
     142           6 :             pBand->GetDataset()->GetDriver() != nullptr &&
     143           3 :             EQUAL(pBand->GetDataset()->GetDriver()->GetDescription(), "HFA");
     144             : 
     145             :         kealib::KEAAttributeTable *keaAtt =
     146           3 :             pImageIO->getAttributeTable(kealib::kea_att_file, nBand);
     147             : 
     148             :         /*bool redDef = false;
     149             :         int redIdx = -1;
     150             :         bool greenDef = false;
     151             :         int greenIdx = -1;
     152             :         bool blueDef = false;
     153             :         int blueIdx = -1;
     154             :         bool alphaDef = false;
     155             :         int alphaIdx = -1;*/
     156             : 
     157           3 :         int numCols = gdalAtt->GetColumnCount();
     158             :         std::vector<kealib::KEAATTField *> *fields =
     159           3 :             new std::vector<kealib::KEAATTField *>();
     160             :         kealib::KEAATTField *field;
     161          18 :         for (int ni = 0; ni < numCols; ++ni)
     162             :         {
     163          15 :             field = new kealib::KEAATTField();
     164          15 :             field->name = gdalAtt->GetNameOfCol(ni);
     165             : 
     166          15 :             field->dataType = kealib::kea_att_string;
     167          15 :             switch (gdalAtt->GetTypeOfCol(ni))
     168             :             {
     169          12 :                 case GFT_Integer:
     170          12 :                     field->dataType = kealib::kea_att_int;
     171          12 :                     break;
     172           2 :                 case GFT_Real:
     173           2 :                     field->dataType = kealib::kea_att_float;
     174           2 :                     break;
     175           1 :                 case GFT_String:
     176           1 :                     field->dataType = kealib::kea_att_string;
     177           1 :                     break;
     178           0 :                 default:
     179             :                     // leave as "kea_att_string"
     180           0 :                     break;
     181             :             }
     182             : 
     183          15 :             if (bInputHFA && (field->name == "Histogram"))
     184             :             {
     185           0 :                 field->usage = "PixelCount";
     186           0 :                 field->dataType = kealib::kea_att_int;
     187             :             }
     188          15 :             else if (bInputHFA && (field->name == "Opacity"))
     189             :             {
     190           0 :                 field->name = "Alpha";
     191           0 :                 field->usage = "Alpha";
     192           0 :                 field->dataType = kealib::kea_att_int;
     193             :                 /*alphaDef = true;
     194             :                 alphaIdx = ni;*/
     195             :             }
     196             :             else
     197             :             {
     198          15 :                 field->usage = "Generic";
     199          15 :                 switch (gdalAtt->GetUsageOfCol(ni))
     200             :                 {
     201           1 :                     case GFU_PixelCount:
     202           1 :                         field->usage = "PixelCount";
     203           1 :                         break;
     204           1 :                     case GFU_Name:
     205           1 :                         field->usage = "Name";
     206           1 :                         break;
     207           3 :                     case GFU_Red:
     208           3 :                         field->usage = "Red";
     209           3 :                         if (bInputHFA)
     210             :                         {
     211           0 :                             field->dataType = kealib::kea_att_int;
     212             :                             /*redDef = true;
     213             :                             redIdx = ni;*/
     214             :                         }
     215           3 :                         break;
     216           3 :                     case GFU_Green:
     217           3 :                         field->usage = "Green";
     218           3 :                         if (bInputHFA)
     219             :                         {
     220           0 :                             field->dataType = kealib::kea_att_int;
     221             :                             /*greenDef = true;
     222             :                             greenIdx = ni;*/
     223             :                         }
     224           3 :                         break;
     225           3 :                     case GFU_Blue:
     226           3 :                         field->usage = "Blue";
     227           3 :                         if (bInputHFA)
     228             :                         {
     229           0 :                             field->dataType = kealib::kea_att_int;
     230             :                             /*blueDef = true;
     231             :                             blueIdx = ni;*/
     232             :                         }
     233           3 :                         break;
     234           3 :                     case GFU_Alpha:
     235           3 :                         field->usage = "Alpha";
     236           3 :                         break;
     237           1 :                     default:
     238             :                         // leave as "Generic"
     239           1 :                         break;
     240             :                 }
     241             :             }
     242             : 
     243          15 :             fields->push_back(field);
     244             :         }
     245             : 
     246             :         // This function will populate the field indexes used within
     247             :         // the KEA RAT.
     248           3 :         keaAtt->addFields(fields);
     249             : 
     250           3 :         int numRows = gdalAtt->GetRowCount();
     251           3 :         keaAtt->addRows(numRows);
     252             : 
     253           3 :         int *pnIntBuffer = new int[RAT_CHUNKSIZE];
     254           3 :         int64_t *pnInt64Buffer = new int64_t[RAT_CHUNKSIZE];
     255           3 :         double *pfDoubleBuffer = new double[RAT_CHUNKSIZE];
     256           6 :         for (int ni = 0; ni < numRows; ni += RAT_CHUNKSIZE)
     257             :         {
     258           3 :             int nLength = RAT_CHUNKSIZE;
     259           3 :             if ((ni + nLength) > numRows)
     260             :             {
     261           3 :                 nLength = numRows - ni;
     262             :             }
     263          18 :             for (int nj = 0; nj < numCols; ++nj)
     264             :             {
     265          15 :                 field = fields->at(nj);
     266             : 
     267          15 :                 switch (field->dataType)
     268             :                 {
     269          12 :                     case kealib::kea_att_int:
     270             :                         ((GDALRasterAttributeTable *)gdalAtt)
     271          12 :                             ->ValuesIO(GF_Read, nj, ni, nLength, pnIntBuffer);
     272          40 :                         for (int i = 0; i < nLength; i++)
     273             :                         {
     274          28 :                             pnInt64Buffer[i] = pnIntBuffer[i];
     275             :                         }
     276          12 :                         keaAtt->setIntFields(ni, nLength, field->idx,
     277          12 :                                              pnInt64Buffer);
     278          12 :                         break;
     279           2 :                     case kealib::kea_att_float:
     280             :                         ((GDALRasterAttributeTable *)gdalAtt)
     281           2 :                             ->ValuesIO(GF_Read, nj, ni, nLength,
     282           2 :                                        pfDoubleBuffer);
     283           2 :                         keaAtt->setFloatFields(ni, nLength, field->idx,
     284           2 :                                                pfDoubleBuffer);
     285           2 :                         break;
     286           1 :                     case kealib::kea_att_string:
     287             :                     {
     288             :                         char **papszColData =
     289           1 :                             (char **)VSIMalloc2(nLength, sizeof(char *));
     290             :                         ((GDALRasterAttributeTable *)gdalAtt)
     291           1 :                             ->ValuesIO(GF_Read, nj, ni, nLength, papszColData);
     292             : 
     293           2 :                         std::vector<std::string> aStringBuffer;
     294           2 :                         for (int i = 0; i < nLength; i++)
     295             :                         {
     296           1 :                             aStringBuffer.push_back(papszColData[i]);
     297             :                         }
     298             : 
     299           2 :                         for (int i = 0; i < nLength; i++)
     300           1 :                             CPLFree(papszColData[i]);
     301           1 :                         CPLFree(papszColData);
     302             : 
     303           1 :                         keaAtt->setStringFields(ni, nLength, field->idx,
     304           1 :                                                 &aStringBuffer);
     305             :                     }
     306           1 :                     break;
     307           0 :                     default:
     308             :                         // Ignore as data type is not known or available from a
     309             :                         // HFA/GDAL RAT."
     310           0 :                         break;
     311             :                 }
     312             :             }
     313             :         }
     314             : 
     315           3 :         delete[] pnIntBuffer;
     316           3 :         delete[] pnInt64Buffer;
     317           3 :         delete[] pfDoubleBuffer;
     318             : 
     319           3 :         delete keaAtt;
     320          15 :         for (std::vector<kealib::KEAATTField *>::iterator iterField =
     321           3 :                  fields->begin();
     322          33 :              iterField != fields->end(); ++iterField)
     323             :         {
     324          15 :             delete *iterField;
     325             :         }
     326           3 :         delete fields;
     327             :     }
     328          88 : }
     329             : 
     330             : // copies the metadata
     331             : // pass nBand == -1 to copy a dataset's metadata
     332             : // or band index to copy a band's metadata
     333         149 : static void KEACopyMetadata(GDALMajorObject *pObject,
     334             :                             kealib::KEAImageIO *pImageIO, int nBand)
     335             : {
     336         149 :     char **ppszMetadata = pObject->GetMetadata();
     337         149 :     if (ppszMetadata != nullptr)
     338             :     {
     339          86 :         int nCount = 0;
     340         252 :         while (ppszMetadata[nCount] != nullptr)
     341             :         {
     342         166 :             char *pszName = nullptr;
     343             :             const char *pszValue =
     344         166 :                 CPLParseNameValue(ppszMetadata[nCount], &pszName);
     345         166 :             if (pszValue == nullptr)
     346          12 :                 pszValue = "";
     347         166 :             if (pszName != nullptr)
     348             :             {
     349             :                 // it is LAYER_TYPE and a Band? if so handle separately
     350         154 :                 if ((nBand != -1) && EQUAL(pszName, "LAYER_TYPE"))
     351             :                 {
     352          55 :                     if (EQUAL(pszValue, "athematic"))
     353             :                     {
     354          53 :                         pImageIO->setImageBandLayerType(nBand,
     355             :                                                         kealib::kea_continuous);
     356             :                     }
     357             :                     else
     358             :                     {
     359           2 :                         pImageIO->setImageBandLayerType(nBand,
     360             :                                                         kealib::kea_thematic);
     361             :                     }
     362             :                 }
     363          99 :                 else if ((nBand != -1) &&
     364          84 :                          EQUAL(pszName, "STATISTICS_HISTOBINVALUES"))
     365             :                 {
     366             :                     // This gets copied across as part of the attributes
     367             :                     // so ignore for now.
     368             :                 }
     369             :                 else
     370             :                 {
     371             :                     // write it into the image
     372          99 :                     if (nBand != -1)
     373          84 :                         pImageIO->setImageBandMetaData(nBand, pszName,
     374             :                                                        pszValue);
     375             :                     else
     376          15 :                         pImageIO->setImageMetaData(pszName, pszValue);
     377             :                 }
     378         154 :                 CPLFree(pszName);
     379             :             }
     380         166 :             nCount++;
     381             :         }
     382             :     }
     383         149 : }
     384             : 
     385             : // copies the description over
     386          88 : static void KEACopyDescription(GDALRasterBand *pBand,
     387             :                                kealib::KEAImageIO *pImageIO, int nBand)
     388             : {
     389          88 :     const char *pszDesc = pBand->GetDescription();
     390          88 :     pImageIO->setImageBandDescription(nBand, pszDesc);
     391          88 : }
     392             : 
     393             : // copies the no data value across
     394          88 : static void KEACopyNoData(GDALRasterBand *pBand, kealib::KEAImageIO *pImageIO,
     395             :                           int nBand)
     396             : {
     397          88 :     int bSuccess = 0;
     398          88 :     double dNoData = pBand->GetNoDataValue(&bSuccess);
     399          88 :     if (bSuccess)
     400             :     {
     401          17 :         pImageIO->setNoDataValue(nBand, &dNoData, kealib::kea_64float);
     402             :     }
     403          88 : }
     404             : 
     405          88 : static bool KEACopyBand(GDALRasterBand *pBand, kealib::KEAImageIO *pImageIO,
     406             :                         int nBand, int nTotalbands,
     407             :                         GDALProgressFunc pfnProgress, void *pProgressData)
     408             : {
     409             :     // first copy the raster data over
     410          88 :     if (!KEACopyRasterData(pBand, pImageIO, nBand, -1, nTotalbands, pfnProgress,
     411             :                            pProgressData))
     412           0 :         return false;
     413             : 
     414             :     // are there any overviews?
     415          88 :     int nOverviews = pBand->GetOverviewCount();
     416          89 :     for (int nOverviewCount = 0; nOverviewCount < nOverviews; nOverviewCount++)
     417             :     {
     418           1 :         GDALRasterBand *pOverview = pBand->GetOverview(nOverviewCount);
     419           1 :         int nOverviewXSize = pOverview->GetXSize();
     420           1 :         int nOverviewYSize = pOverview->GetYSize();
     421           1 :         pImageIO->createOverview(nBand, nOverviewCount + 1, nOverviewXSize,
     422             :                                  nOverviewYSize);
     423           1 :         if (!KEACopyRasterData(pOverview, pImageIO, nBand, nOverviewCount + 1,
     424             :                                nTotalbands, pfnProgress, pProgressData))
     425           0 :             return false;
     426             :     }
     427             : 
     428             :     // now metadata
     429          88 :     KEACopyMetadata(pBand, pImageIO, nBand);
     430             : 
     431             :     // and attributes
     432          88 :     KEACopyRAT(pBand, pImageIO, nBand);
     433             : 
     434             :     // and description
     435          88 :     KEACopyDescription(pBand, pImageIO, nBand);
     436             : 
     437             :     // and no data
     438          88 :     KEACopyNoData(pBand, pImageIO, nBand);
     439             : 
     440          88 :     return true;
     441             : }
     442             : 
     443          61 : static void KEACopySpatialInfo(GDALDataset *pDataset,
     444             :                                kealib::KEAImageIO *pImageIO)
     445             : {
     446          61 :     kealib::KEAImageSpatialInfo *pSpatialInfo = pImageIO->getSpatialInfo();
     447             : 
     448             :     double padfTransform[6];
     449          61 :     if (pDataset->GetGeoTransform(padfTransform) == CE_None)
     450             :     {
     451             :         // convert back from GDAL's array format
     452          61 :         pSpatialInfo->tlX = padfTransform[0];
     453          61 :         pSpatialInfo->xRes = padfTransform[1];
     454          61 :         pSpatialInfo->xRot = padfTransform[2];
     455          61 :         pSpatialInfo->tlY = padfTransform[3];
     456          61 :         pSpatialInfo->yRot = padfTransform[4];
     457          61 :         pSpatialInfo->yRes = padfTransform[5];
     458             :     }
     459             : 
     460          61 :     const char *pszProjection = pDataset->GetProjectionRef();
     461          61 :     pSpatialInfo->wktString = pszProjection;
     462             : 
     463          61 :     pImageIO->setSpatialInfo(pSpatialInfo);
     464          61 : }
     465             : 
     466             : // copies the GCP's across
     467          61 : static void KEACopyGCPs(GDALDataset *pDataset, kealib::KEAImageIO *pImageIO)
     468             : {
     469          61 :     int nGCPs = pDataset->GetGCPCount();
     470             : 
     471          61 :     if (nGCPs > 0)
     472             :     {
     473           2 :         std::vector<kealib::KEAImageGCP *> KEAGCPs;
     474           1 :         const GDAL_GCP *pGDALGCPs = pDataset->GetGCPs();
     475             : 
     476           3 :         for (int n = 0; n < nGCPs; n++)
     477             :         {
     478           2 :             kealib::KEAImageGCP *pGCP = new kealib::KEAImageGCP;
     479           2 :             pGCP->pszId = pGDALGCPs[n].pszId;
     480           2 :             pGCP->pszInfo = pGDALGCPs[n].pszInfo;
     481           2 :             pGCP->dfGCPPixel = pGDALGCPs[n].dfGCPPixel;
     482           2 :             pGCP->dfGCPLine = pGDALGCPs[n].dfGCPLine;
     483           2 :             pGCP->dfGCPX = pGDALGCPs[n].dfGCPX;
     484           2 :             pGCP->dfGCPY = pGDALGCPs[n].dfGCPY;
     485           2 :             pGCP->dfGCPZ = pGDALGCPs[n].dfGCPZ;
     486           2 :             KEAGCPs.push_back(pGCP);
     487             :         }
     488             : 
     489           1 :         const char *pszGCPProj = pDataset->GetGCPProjection();
     490             :         try
     491             :         {
     492           1 :             pImageIO->setGCPs(&KEAGCPs, pszGCPProj);
     493             :         }
     494           0 :         catch (const kealib::KEAException &)
     495             :         {
     496             :         }
     497             : 
     498           3 :         for (std::vector<kealib::KEAImageGCP *>::iterator itr = KEAGCPs.begin();
     499           5 :              itr != KEAGCPs.end(); ++itr)
     500             :         {
     501           2 :             delete (*itr);
     502             :         }
     503             :     }
     504          61 : }
     505             : 
     506          61 : bool KEACopyFile(GDALDataset *pDataset, kealib::KEAImageIO *pImageIO,
     507             :                  GDALProgressFunc pfnProgress, void *pProgressData)
     508             : {
     509             :     // Main function - copies pDataset to pImageIO
     510             : 
     511             :     // Copy across the spatial info.
     512          61 :     KEACopySpatialInfo(pDataset, pImageIO);
     513             : 
     514             :     // dataset metadata
     515          61 :     KEACopyMetadata(pDataset, pImageIO, -1);
     516             : 
     517             :     // GCPs
     518          61 :     KEACopyGCPs(pDataset, pImageIO);
     519             : 
     520             :     // now copy all the bands over
     521          61 :     int nBands = pDataset->GetRasterCount();
     522         149 :     for (int nBand = 0; nBand < nBands; nBand++)
     523             :     {
     524          88 :         GDALRasterBand *pBand = pDataset->GetRasterBand(nBand + 1);
     525          88 :         if (!KEACopyBand(pBand, pImageIO, nBand + 1, nBands, pfnProgress,
     526             :                          pProgressData))
     527           0 :             return false;
     528             :     }
     529             : 
     530          61 :     pfnProgress(1.0, nullptr, pProgressData);
     531          61 :     return true;
     532             : }

Generated by: LCOV version 1.14