LCOV - code coverage report
Current view: top level - alg - gdalsimplewarp.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 76 149 51.0 %
Date: 2025-05-20 14:45:53 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Mapinfo Image Warper
       4             :  * Purpose:  Simple (source in memory) warp algorithm.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2002, i3 - information integration and imaging, Fort Collin, CO
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "gdal_alg.h"
      15             : 
      16             : #include <cstdlib>
      17             : #include <cstring>
      18             : 
      19             : #include <algorithm>
      20             : 
      21             : #include "cpl_conv.h"
      22             : #include "cpl_error.h"
      23             : #include "cpl_progress.h"
      24             : #include "cpl_string.h"
      25             : #include "cpl_vsi.h"
      26             : #include "gdal.h"
      27             : #include "gdal_priv.h"
      28             : 
      29             : /************************************************************************/
      30             : /*                      GDALSimpleWarpRemapping()                       */
      31             : /*                                                                      */
      32             : /*      This function implements any raster remapping requested in      */
      33             : /*      the options list.  The remappings are applied to the source     */
      34             : /*      data before warping.  Two kinds are support ... REMAP           */
      35             : /*      commands which remap selected pixel values for any band and     */
      36             : /*      REMAP_MULTI which only remap pixels matching the input in       */
      37             : /*      all bands at once (i.e. to remap an RGB value to another).      */
      38             : /************************************************************************/
      39             : 
      40           1 : static void GDALSimpleWarpRemapping(int nBandCount, GByte **papabySrcData,
      41             :                                     int nSrcXSize, int nSrcYSize,
      42             :                                     char **papszWarpOptions)
      43             : 
      44             : {
      45             : 
      46             :     /* ==================================================================== */
      47             :     /*      Process any and all single value REMAP commands.                */
      48             :     /* ==================================================================== */
      49           1 :     char **papszRemaps = CSLFetchNameValueMultiple(papszWarpOptions, "REMAP");
      50             : 
      51           1 :     const int nRemaps = CSLCount(papszRemaps);
      52           1 :     for (int iRemap = 0; iRemap < nRemaps; iRemap++)
      53             :     {
      54             : 
      55             :         /* --------------------------------------------------------------------
      56             :          */
      57             :         /*      What are the pixel values to map from and to? */
      58             :         /* --------------------------------------------------------------------
      59             :          */
      60           0 :         char **papszTokens = CSLTokenizeString(papszRemaps[iRemap]);
      61             : 
      62           0 :         if (CSLCount(papszTokens) != 2)
      63             :         {
      64           0 :             CPLError(CE_Warning, CPLE_AppDefined,
      65             :                      "Ill formed REMAP `%s' ignored in "
      66             :                      "GDALSimpleWarpRemapping()",
      67           0 :                      papszRemaps[iRemap]);
      68           0 :             CSLDestroy(papszTokens);
      69           0 :             continue;
      70             :         }
      71             : 
      72           0 :         const int nFromValue = atoi(papszTokens[0]);
      73           0 :         const int nToValue = atoi(papszTokens[1]);
      74             :         // TODO(schwehr): Why is it ok to narrow ints to byte without checking?
      75           0 :         const GByte byToValue = static_cast<GByte>(nToValue);
      76             : 
      77           0 :         CSLDestroy(papszTokens);
      78             : 
      79             :         /* --------------------------------------------------------------------
      80             :          */
      81             :         /*      Pass over each band searching for matches. */
      82             :         /* --------------------------------------------------------------------
      83             :          */
      84           0 :         for (int iBand = 0; iBand < nBandCount; iBand++)
      85             :         {
      86           0 :             GByte *pabyData = papabySrcData[iBand];
      87           0 :             int nPixelCount = nSrcXSize * nSrcYSize;
      88             : 
      89           0 :             while (nPixelCount != 0)
      90             :             {
      91           0 :                 if (*pabyData == nFromValue)
      92           0 :                     *pabyData = byToValue;
      93             : 
      94           0 :                 pabyData++;
      95           0 :                 nPixelCount--;
      96             :             }
      97             :         }
      98             :     }
      99             : 
     100           1 :     CSLDestroy(papszRemaps);
     101             : 
     102             :     /* ==================================================================== */
     103             :     /*      Process any and all REMAP_MULTI commands.                       */
     104             :     /* ==================================================================== */
     105           1 :     papszRemaps = CSLFetchNameValueMultiple(papszWarpOptions, "REMAP_MULTI");
     106             : 
     107           1 :     const int nRemapsMulti = CSLCount(papszRemaps);
     108           1 :     for (int iRemap = 0; iRemap < nRemapsMulti; iRemap++)
     109             :     {
     110             :         /* --------------------------------------------------------------------
     111             :          */
     112             :         /*      What are the pixel values to map from and to? */
     113             :         /* --------------------------------------------------------------------
     114             :          */
     115           0 :         char **papszTokens = CSLTokenizeString(papszRemaps[iRemap]);
     116             : 
     117           0 :         const int nTokens = CSLCount(papszTokens);
     118           0 :         if (nTokens % 2 == 1 || nTokens == 0 || nTokens > nBandCount * 2)
     119             :         {
     120           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     121             :                      "Ill formed REMAP_MULTI `%s' ignored in "
     122             :                      "GDALSimpleWarpRemapping()",
     123           0 :                      papszRemaps[iRemap]);
     124           0 :             CSLDestroy(papszTokens);
     125           0 :             continue;
     126             :         }
     127             : 
     128           0 :         const int nMapBandCount = nTokens / 2;
     129             : 
     130             :         int *panFromValue =
     131           0 :             static_cast<int *>(CPLMalloc(sizeof(int) * nMapBandCount));
     132             :         int *panToValue =
     133           0 :             static_cast<int *>(CPLMalloc(sizeof(int) * nMapBandCount));
     134             : 
     135           0 :         for (int iBand = 0; iBand < nMapBandCount; iBand++)
     136             :         {
     137           0 :             panFromValue[iBand] = atoi(papszTokens[iBand]);
     138           0 :             panToValue[iBand] = atoi(papszTokens[iBand + nMapBandCount]);
     139             :         }
     140             : 
     141           0 :         CSLDestroy(papszTokens);
     142             : 
     143             :         /* --------------------------------------------------------------------
     144             :          */
     145             :         /*      Search for matching values to replace. */
     146             :         /* --------------------------------------------------------------------
     147             :          */
     148           0 :         const int nPixelCount = nSrcXSize * nSrcYSize;
     149             : 
     150           0 :         for (int iPixel = 0; iPixel < nPixelCount; iPixel++)
     151             :         {
     152           0 :             bool bMatch = true;
     153             : 
     154             :             // Always check band 0.
     155           0 :             for (int iBand = 0; bMatch && iBand < std::max(1, nMapBandCount);
     156             :                  iBand++)
     157             :             {
     158           0 :                 if (papabySrcData[iBand][iPixel] != panFromValue[iBand])
     159           0 :                     bMatch = false;
     160             :             }
     161             : 
     162           0 :             if (!bMatch)
     163           0 :                 continue;
     164             : 
     165           0 :             for (int iBand = 0; iBand < nMapBandCount; iBand++)
     166           0 :                 papabySrcData[iBand][iPixel] =
     167           0 :                     static_cast<GByte>(panToValue[iBand]);
     168             :         }
     169             : 
     170           0 :         CPLFree(panFromValue);
     171           0 :         CPLFree(panToValue);
     172             :     }
     173             : 
     174           1 :     CSLDestroy(papszRemaps);
     175           1 : }
     176             : 
     177             : /************************************************************************/
     178             : /*                        GDALSimpleImageWarp()                         */
     179             : /************************************************************************/
     180             : 
     181             : /**
     182             :  * Perform simple image warp.
     183             :  *
     184             :  * Copies an image from a source dataset to a destination dataset applying
     185             :  * an application defined transformation.   This algorithm is called simple
     186             :  * because it lacks many options such as resampling kernels (other than
     187             :  * nearest neighbour), support for data types other than 8bit, and the
     188             :  * ability to warp images without holding the entire source and destination
     189             :  * image in memory.
     190             :  *
     191             :  * The following option(s) may be passed in papszWarpOptions.
     192             :  * <ul>
     193             :  * <li> "INIT=v[,v...]": This option indicates that the output dataset should
     194             :  * be initialized to the indicated value in any area valid data is not written.
     195             :  * Distinct values may be listed for each band separated by columns.
     196             :  * </ul>
     197             :  *
     198             :  * For more advanced warping capabilities, consider using GDALWarp().
     199             :  *
     200             :  * @param hSrcDS the source image dataset.
     201             :  * @param hDstDS the destination image dataset.
     202             :  * @param nBandCount the number of bands to be warped.  If zero, all bands
     203             :  * will be processed.
     204             :  * @param panBandList the list of bands to translate.
     205             :  * @param pfnTransform the transformation function to call.  See
     206             :  * GDALTransformerFunc().
     207             :  * @param pTransformArg the callback handle to pass to pfnTransform.
     208             :  * @param pfnProgress the function used to report progress.  See
     209             :  * GDALProgressFunc().
     210             :  * @param pProgressArg the callback handle to pass to pfnProgress.
     211             :  * @param papszWarpOptions additional options controlling the warp.
     212             :  *
     213             :  * @return TRUE if the operation completes, or FALSE if an error occurs.
     214             :  * @see GDALWarp()
     215             :  */
     216             : 
     217           2 : int CPL_STDCALL GDALSimpleImageWarp(GDALDatasetH hSrcDS, GDALDatasetH hDstDS,
     218             :                                     int nBandCount, int *panBandList,
     219             :                                     GDALTransformerFunc pfnTransform,
     220             :                                     void *pTransformArg,
     221             :                                     GDALProgressFunc pfnProgress,
     222             :                                     void *pProgressArg, char **papszWarpOptions)
     223             : 
     224             : {
     225           2 :     VALIDATE_POINTER1(hSrcDS, "GDALSimpleImageWarp", 0);
     226           2 :     VALIDATE_POINTER1(hDstDS, "GDALSimpleImageWarp", 0);
     227             : 
     228             :     /* -------------------------------------------------------------------- */
     229             :     /*      If no bands provided assume we should process all bands.        */
     230             :     /* -------------------------------------------------------------------- */
     231           2 :     if (nBandCount == 0)
     232             :     {
     233           1 :         nBandCount = GDALGetRasterCount(hSrcDS);
     234           1 :         if (nBandCount == 0)
     235             :         {
     236           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     237             :                      "No raster band in source dataset");
     238           0 :             return FALSE;
     239             :         }
     240             : 
     241           1 :         panBandList = static_cast<int *>(CPLCalloc(sizeof(int), nBandCount));
     242             : 
     243           2 :         for (int iBand = 0; iBand < nBandCount; iBand++)
     244           1 :             panBandList[iBand] = iBand + 1;
     245             : 
     246           1 :         const int nResult = GDALSimpleImageWarp(
     247             :             hSrcDS, hDstDS, nBandCount, panBandList, pfnTransform,
     248             :             pTransformArg, pfnProgress, pProgressArg, papszWarpOptions);
     249           1 :         CPLFree(panBandList);
     250           1 :         return nResult;
     251             :     }
     252             : 
     253             :     /* -------------------------------------------------------------------- */
     254             :     /*      Post initial progress.                                          */
     255             :     /* -------------------------------------------------------------------- */
     256           1 :     if (pfnProgress)
     257             :     {
     258           0 :         if (!pfnProgress(0.0, "", pProgressArg))
     259           0 :             return FALSE;
     260             :     }
     261             : 
     262             :     /* -------------------------------------------------------------------- */
     263             :     /*      Load the source image band(s).                                  */
     264             :     /* -------------------------------------------------------------------- */
     265           1 :     const int nSrcXSize = GDALGetRasterXSize(hSrcDS);
     266           1 :     const int nSrcYSize = GDALGetRasterYSize(hSrcDS);
     267             :     std::vector<std::unique_ptr<GByte, VSIFreeReleaser>> apabySrcDataUniquePtr(
     268           2 :         nBandCount);
     269             : 
     270           2 :     for (int iBand = 0; iBand < nBandCount; iBand++)
     271             :     {
     272           1 :         apabySrcDataUniquePtr[iBand].reset(
     273           1 :             static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nSrcXSize, nSrcYSize)));
     274           1 :         if (apabySrcDataUniquePtr[iBand] == nullptr)
     275             :         {
     276           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
     277             :                      "GDALSimpleImageWarp out of memory.");
     278           0 :             return FALSE;
     279             :         }
     280             : 
     281           1 :         if (GDALRasterIO(GDALGetRasterBand(hSrcDS, panBandList[iBand]), GF_Read,
     282             :                          0, 0, nSrcXSize, nSrcYSize,
     283           1 :                          apabySrcDataUniquePtr[iBand].get(), nSrcXSize,
     284           1 :                          nSrcYSize, GDT_Byte, 0, 0) != CE_None)
     285             :         {
     286           0 :             CPLError(CE_Failure, CPLE_FileIO,
     287             :                      "GDALSimpleImageWarp GDALRasterIO failure %s",
     288             :                      CPLGetLastErrorMsg());
     289           0 :             return FALSE;
     290             :         }
     291             :     }
     292             : 
     293             :     /* -------------------------------------------------------------------- */
     294             :     /*      Check for remap request(s).                                     */
     295             :     /* -------------------------------------------------------------------- */
     296           2 :     std::vector<GByte *> apabySrcData;
     297           2 :     for (auto &ptr : apabySrcDataUniquePtr)
     298           1 :         apabySrcData.push_back(ptr.get());
     299           1 :     GDALSimpleWarpRemapping(nBandCount, apabySrcData.data(), nSrcXSize,
     300             :                             nSrcYSize, papszWarpOptions);
     301             : 
     302             :     /* -------------------------------------------------------------------- */
     303             :     /*      Allocate scanline buffers for output image.                     */
     304             :     /* -------------------------------------------------------------------- */
     305           1 :     const int nDstXSize = GDALGetRasterXSize(hDstDS);
     306           1 :     const int nDstYSize = GDALGetRasterYSize(hDstDS);
     307             :     std::vector<std::unique_ptr<GByte, VSIFreeReleaser>> apabyDstLine(
     308           2 :         nBandCount);
     309             : 
     310           2 :     for (int iBand = 0; iBand < nBandCount; iBand++)
     311             :     {
     312           1 :         apabyDstLine[iBand].reset(
     313           1 :             static_cast<GByte *>(VSI_MALLOC_VERBOSE(nDstXSize)));
     314           1 :         if (!apabyDstLine[iBand])
     315           0 :             return FALSE;
     316             :     }
     317             : 
     318             :     /* -------------------------------------------------------------------- */
     319             :     /*      Allocate x,y,z coordinate arrays for transformation ... one     */
     320             :     /*      scanlines worth of positions.                                   */
     321             :     /* -------------------------------------------------------------------- */
     322             :     std::unique_ptr<double, VSIFreeReleaser> padfX(
     323           2 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(sizeof(double), nDstXSize)));
     324             :     std::unique_ptr<double, VSIFreeReleaser> padfY(
     325           2 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(sizeof(double), nDstXSize)));
     326             :     std::unique_ptr<double, VSIFreeReleaser> padfZ(
     327           2 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(sizeof(double), nDstXSize)));
     328             :     std::unique_ptr<int, VSIFreeReleaser> pabSuccess(
     329           2 :         static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nDstXSize)));
     330           1 :     if (!padfX || !padfY || !padfZ || !pabSuccess)
     331           0 :         return FALSE;
     332             : 
     333             :     /* -------------------------------------------------------------------- */
     334             :     /*      Establish the value we will use to initialize the bands.  We    */
     335             :     /*      default to -1 indicating the initial value should be read       */
     336             :     /*      and preserved from the source file, but allow this to be        */
     337             :     /*      overridden by passed                                            */
     338             :     /*      option(s).                                                      */
     339             :     /* -------------------------------------------------------------------- */
     340           2 :     std::vector<int> anBandInit(nBandCount);
     341           1 :     if (CSLFetchNameValue(papszWarpOptions, "INIT"))
     342             :     {
     343             :         const CPLStringList aosTokens(CSLTokenizeStringComplex(
     344           0 :             CSLFetchNameValue(papszWarpOptions, "INIT"), " ,", FALSE, FALSE));
     345             : 
     346           0 :         const int nTokenCount = aosTokens.size();
     347             : 
     348           0 :         for (int iBand = 0; iBand < nBandCount; iBand++)
     349             :         {
     350           0 :             if (nTokenCount == 0)
     351           0 :                 anBandInit[iBand] = 0;
     352             :             else
     353           0 :                 anBandInit[iBand] =
     354           0 :                     atoi(aosTokens[std::min(iBand, nTokenCount - 1)]);
     355             :         }
     356             :     }
     357             : 
     358             :     /* -------------------------------------------------------------------- */
     359             :     /*      Loop over all the scanlines in the output image.                */
     360             :     /* -------------------------------------------------------------------- */
     361          21 :     for (int iDstY = 0; iDstY < nDstYSize; iDstY++)
     362             :     {
     363             :         // Clear output buffer to "transparent" value.  Should not we
     364             :         // really be reading from the destination file to support overlay?
     365          40 :         for (int iBand = 0; iBand < nBandCount; iBand++)
     366             :         {
     367          20 :             if (anBandInit[iBand] == -1)
     368             :             {
     369           0 :                 if (GDALRasterIO(GDALGetRasterBand(hDstDS, iBand + 1), GF_Read,
     370             :                                  0, iDstY, nDstXSize, 1,
     371           0 :                                  apabyDstLine[iBand].get(), nDstXSize, 1,
     372           0 :                                  GDT_Byte, 0, 0) != CE_None)
     373             :                 {
     374           0 :                     return FALSE;
     375             :                 }
     376             :             }
     377             :             else
     378             :             {
     379          20 :                 memset(apabyDstLine[iBand].get(), anBandInit[iBand], nDstXSize);
     380             :             }
     381             :         }
     382             : 
     383             :         // Set point to transform.
     384         420 :         for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
     385             :         {
     386         400 :             (padfX.get())[iDstX] = iDstX + 0.5;
     387         400 :             (padfY.get())[iDstX] = iDstY + 0.5;
     388         400 :             (padfZ.get())[iDstX] = 0.0;
     389             :         }
     390             : 
     391             :         // Transform the points from destination pixel/line coordinates
     392             :         // to source pixel/line coordinates.
     393          20 :         pfnTransform(pTransformArg, TRUE, nDstXSize, padfX.get(), padfY.get(),
     394             :                      padfZ.get(), pabSuccess.get());
     395             : 
     396             :         // Loop over the output scanline.
     397         420 :         for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
     398             :         {
     399         400 :             if (!(pabSuccess.get())[iDstX])
     400           0 :                 continue;
     401             : 
     402             :             // We test against the value before casting to avoid the
     403             :             // problem of asymmetric truncation effects around zero.  That is
     404             :             // -0.5 will be 0 when cast to an int.
     405         400 :             if ((padfX.get())[iDstX] < 0.0 || (padfY.get())[iDstX] < 0.0)
     406           0 :                 continue;
     407             : 
     408         400 :             const int iSrcX = static_cast<int>((padfX.get())[iDstX]);
     409         400 :             const int iSrcY = static_cast<int>((padfY.get())[iDstX]);
     410             : 
     411         400 :             if (iSrcX >= nSrcXSize || iSrcY >= nSrcYSize)
     412           0 :                 continue;
     413             : 
     414         400 :             const int iSrcOffset = iSrcX + iSrcY * nSrcXSize;
     415             : 
     416         800 :             for (int iBand = 0; iBand < nBandCount; iBand++)
     417         400 :                 (apabyDstLine[iBand].get())[iDstX] =
     418         400 :                     apabySrcData[iBand][iSrcOffset];
     419             :         }
     420             : 
     421             :         // Write scanline to disk.
     422          40 :         for (int iBand = 0; iBand < nBandCount; iBand++)
     423             :         {
     424          20 :             if (GDALRasterIO(GDALGetRasterBand(hDstDS, iBand + 1), GF_Write, 0,
     425          20 :                              iDstY, nDstXSize, 1, apabyDstLine[iBand].get(),
     426          20 :                              nDstXSize, 1, GDT_Byte, 0, 0) != CE_None)
     427             :             {
     428           0 :                 return FALSE;
     429             :             }
     430             :         }
     431             : 
     432          20 :         if (pfnProgress != nullptr)
     433             :         {
     434           0 :             if (!pfnProgress((iDstY + 1) / static_cast<double>(nDstYSize), "",
     435             :                              pProgressArg))
     436             :             {
     437           0 :                 CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     438           0 :                 return FALSE;
     439             :             }
     440             :         }
     441             :     }
     442             : 
     443           1 :     return TRUE;
     444             : }

Generated by: LCOV version 1.14