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

Generated by: LCOV version 1.14