LCOV - code coverage report
Current view: top level - alg - gdalsimplewarp.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 82 163 50.3 %
Date: 2024-11-21 22:18:42 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           2 :     bool bError = false;
     229             : 
     230             :     /* -------------------------------------------------------------------- */
     231             :     /*      If no bands provided assume we should process all bands.        */
     232             :     /* -------------------------------------------------------------------- */
     233           2 :     if (nBandCount == 0)
     234             :     {
     235           1 :         nBandCount = GDALGetRasterCount(hSrcDS);
     236           1 :         if (nBandCount == 0)
     237             :         {
     238           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     239             :                      "No raster band in source dataset");
     240           0 :             return FALSE;
     241             :         }
     242             : 
     243           1 :         panBandList = static_cast<int *>(CPLCalloc(sizeof(int), nBandCount));
     244             : 
     245           2 :         for (int iBand = 0; iBand < nBandCount; iBand++)
     246           1 :             panBandList[iBand] = iBand + 1;
     247             : 
     248           1 :         const int nResult = GDALSimpleImageWarp(
     249             :             hSrcDS, hDstDS, nBandCount, panBandList, pfnTransform,
     250             :             pTransformArg, pfnProgress, pProgressArg, papszWarpOptions);
     251           1 :         CPLFree(panBandList);
     252           1 :         return nResult;
     253             :     }
     254             : 
     255             :     /* -------------------------------------------------------------------- */
     256             :     /*      Post initial progress.                                          */
     257             :     /* -------------------------------------------------------------------- */
     258           1 :     if (pfnProgress)
     259             :     {
     260           0 :         if (!pfnProgress(0.0, "", pProgressArg))
     261           0 :             return FALSE;
     262             :     }
     263             : 
     264             :     /* -------------------------------------------------------------------- */
     265             :     /*      Load the source image band(s).                                  */
     266             :     /* -------------------------------------------------------------------- */
     267           1 :     const int nSrcXSize = GDALGetRasterXSize(hSrcDS);
     268           1 :     const int nSrcYSize = GDALGetRasterYSize(hSrcDS);
     269             :     GByte **papabySrcData =
     270           1 :         static_cast<GByte **>(CPLCalloc(nBandCount, sizeof(GByte *)));
     271             : 
     272           1 :     bool ok = true;
     273           2 :     for (int iBand = 0; iBand < nBandCount; iBand++)
     274             :     {
     275           2 :         papabySrcData[iBand] =
     276           1 :             static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nSrcXSize, nSrcYSize));
     277           1 :         if (papabySrcData[iBand] == nullptr)
     278             :         {
     279           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
     280             :                      "GDALSimpleImageWarp out of memory.");
     281           0 :             ok = false;
     282           0 :             break;
     283             :         }
     284             : 
     285           1 :         if (GDALRasterIO(GDALGetRasterBand(hSrcDS, panBandList[iBand]), GF_Read,
     286           1 :                          0, 0, nSrcXSize, nSrcYSize, papabySrcData[iBand],
     287           1 :                          nSrcXSize, nSrcYSize, GDT_Byte, 0, 0) != CE_None)
     288             :         {
     289           0 :             CPLError(CE_Failure, CPLE_FileIO,
     290             :                      "GDALSimpleImageWarp GDALRasterIO failure %s",
     291             :                      CPLGetLastErrorMsg());
     292           0 :             ok = false;
     293           0 :             break;
     294             :         }
     295             :     }
     296           1 :     if (!ok)
     297             :     {
     298           0 :         for (int i = 0; i <= nBandCount; i++)
     299             :         {
     300           0 :             VSIFree(papabySrcData[i]);
     301             :         }
     302           0 :         CPLFree(papabySrcData);
     303           0 :         return FALSE;
     304             :     }
     305             : 
     306             :     /* -------------------------------------------------------------------- */
     307             :     /*      Check for remap request(s).                                     */
     308             :     /* -------------------------------------------------------------------- */
     309           1 :     GDALSimpleWarpRemapping(nBandCount, papabySrcData, nSrcXSize, nSrcYSize,
     310             :                             papszWarpOptions);
     311             : 
     312             :     /* -------------------------------------------------------------------- */
     313             :     /*      Allocate scanline buffers for output image.                     */
     314             :     /* -------------------------------------------------------------------- */
     315           1 :     const int nDstXSize = GDALGetRasterXSize(hDstDS);
     316           1 :     const int nDstYSize = GDALGetRasterYSize(hDstDS);
     317             :     GByte **papabyDstLine =
     318           1 :         static_cast<GByte **>(CPLCalloc(nBandCount, sizeof(GByte *)));
     319             : 
     320           2 :     for (int iBand = 0; iBand < nBandCount; iBand++)
     321           1 :         papabyDstLine[iBand] = static_cast<GByte *>(CPLMalloc(nDstXSize));
     322             : 
     323             :     /* -------------------------------------------------------------------- */
     324             :     /*      Allocate x,y,z coordinate arrays for transformation ... one     */
     325             :     /*      scanlines worth of positions.                                   */
     326             :     /* -------------------------------------------------------------------- */
     327             :     double *padfX =
     328           1 :         static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
     329             :     double *padfY =
     330           1 :         static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
     331             :     double *padfZ =
     332           1 :         static_cast<double *>(CPLMalloc(sizeof(double) * nDstXSize));
     333           1 :     int *pabSuccess = static_cast<int *>(CPLMalloc(sizeof(int) * nDstXSize));
     334             : 
     335             :     /* -------------------------------------------------------------------- */
     336             :     /*      Establish the value we will use to initialize the bands.  We    */
     337             :     /*      default to -1 indicating the initial value should be read       */
     338             :     /*      and preserved from the source file, but allow this to be        */
     339             :     /*      overridden by passed                                            */
     340             :     /*      option(s).                                                      */
     341             :     /* -------------------------------------------------------------------- */
     342             :     int *const panBandInit =
     343           1 :         static_cast<int *>(CPLCalloc(sizeof(int), nBandCount));
     344           1 :     if (CSLFetchNameValue(papszWarpOptions, "INIT"))
     345             :     {
     346           0 :         char **papszTokens = CSLTokenizeStringComplex(
     347             :             CSLFetchNameValue(papszWarpOptions, "INIT"), " ,", FALSE, FALSE);
     348             : 
     349           0 :         const int nTokenCount = CSLCount(papszTokens);
     350             : 
     351           0 :         for (int iBand = 0; iBand < nBandCount; iBand++)
     352             :         {
     353           0 :             if (nTokenCount == 0)
     354           0 :                 panBandInit[iBand] = 0;
     355             :             else
     356           0 :                 panBandInit[iBand] =
     357           0 :                     atoi(papszTokens[std::min(iBand, nTokenCount - 1)]);
     358             :         }
     359             : 
     360           0 :         CSLDestroy(papszTokens);
     361             :     }
     362             : 
     363             :     /* -------------------------------------------------------------------- */
     364             :     /*      Loop over all the scanlines in the output image.                */
     365             :     /* -------------------------------------------------------------------- */
     366          21 :     for (int iDstY = 0; iDstY < nDstYSize; iDstY++)
     367             :     {
     368             :         // Clear output buffer to "transparent" value.  Should not we
     369             :         // really be reading from the destination file to support overlay?
     370          40 :         for (int iBand = 0; iBand < nBandCount; iBand++)
     371             :         {
     372          20 :             if (panBandInit[iBand] == -1)
     373             :             {
     374           0 :                 if (GDALRasterIO(GDALGetRasterBand(hDstDS, iBand + 1), GF_Read,
     375           0 :                                  0, iDstY, nDstXSize, 1, papabyDstLine[iBand],
     376           0 :                                  nDstXSize, 1, GDT_Byte, 0, 0) != CE_None)
     377             :                 {
     378           0 :                     bError = TRUE;
     379           0 :                     break;
     380             :                 }
     381             :             }
     382             :             else
     383             :             {
     384          20 :                 memset(papabyDstLine[iBand], panBandInit[iBand], nDstXSize);
     385             :             }
     386             :         }
     387             : 
     388             :         // Set point to transform.
     389         420 :         for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
     390             :         {
     391         400 :             padfX[iDstX] = iDstX + 0.5;
     392         400 :             padfY[iDstX] = iDstY + 0.5;
     393         400 :             padfZ[iDstX] = 0.0;
     394             :         }
     395             : 
     396             :         // Transform the points from destination pixel/line coordinates
     397             :         // to source pixel/line coordinates.
     398          20 :         pfnTransform(pTransformArg, TRUE, nDstXSize, padfX, padfY, padfZ,
     399             :                      pabSuccess);
     400             : 
     401             :         // Loop over the output scanline.
     402         420 :         for (int iDstX = 0; iDstX < nDstXSize; iDstX++)
     403             :         {
     404         400 :             if (!pabSuccess[iDstX])
     405           0 :                 continue;
     406             : 
     407             :             // We test against the value before casting to avoid the
     408             :             // problem of asymmetric truncation effects around zero.  That is
     409             :             // -0.5 will be 0 when cast to an int.
     410         400 :             if (padfX[iDstX] < 0.0 || padfY[iDstX] < 0.0)
     411           0 :                 continue;
     412             : 
     413         400 :             const int iSrcX = static_cast<int>(padfX[iDstX]);
     414         400 :             const int iSrcY = static_cast<int>(padfY[iDstX]);
     415             : 
     416         400 :             if (iSrcX >= nSrcXSize || iSrcY >= nSrcYSize)
     417           0 :                 continue;
     418             : 
     419         400 :             const int iSrcOffset = iSrcX + iSrcY * nSrcXSize;
     420             : 
     421         800 :             for (int iBand = 0; iBand < nBandCount; iBand++)
     422         400 :                 papabyDstLine[iBand][iDstX] = papabySrcData[iBand][iSrcOffset];
     423             :         }
     424             : 
     425             :         // Write scanline to disk.
     426          40 :         for (int iBand = 0; iBand < nBandCount; iBand++)
     427             :         {
     428          20 :             if (GDALRasterIO(GDALGetRasterBand(hDstDS, iBand + 1), GF_Write, 0,
     429          20 :                              iDstY, nDstXSize, 1, papabyDstLine[iBand],
     430          20 :                              nDstXSize, 1, GDT_Byte, 0, 0) != CE_None)
     431             :             {
     432           0 :                 bError = TRUE;
     433           0 :                 break;
     434             :             }
     435             :         }
     436             : 
     437          20 :         if (pfnProgress != nullptr)
     438             :         {
     439           0 :             if (!pfnProgress((iDstY + 1) / static_cast<double>(nDstYSize), "",
     440             :                              pProgressArg))
     441             :             {
     442           0 :                 CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     443           0 :                 bError = TRUE;
     444           0 :                 break;
     445             :             }
     446             :         }
     447             :     }
     448             : 
     449             :     /* -------------------------------------------------------------------- */
     450             :     /*      Cleanup working buffers.                                        */
     451             :     /* -------------------------------------------------------------------- */
     452           2 :     for (int iBand = 0; iBand < nBandCount; iBand++)
     453             :     {
     454           1 :         CPLFree(papabyDstLine[iBand]);
     455           1 :         CPLFree(papabySrcData[iBand]);
     456             :     }
     457             : 
     458           1 :     CPLFree(panBandInit);
     459           1 :     CPLFree(papabyDstLine);
     460           1 :     CPLFree(papabySrcData);
     461           1 :     CPLFree(padfX);
     462           1 :     CPLFree(padfY);
     463           1 :     CPLFree(padfZ);
     464           1 :     CPLFree(pabSuccess);
     465             : 
     466           1 :     return !bError;
     467             : }

Generated by: LCOV version 1.14