mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 06:06:00 -06:00
3117 lines
124 KiB
C++
3117 lines
124 KiB
C++
/******************************************************************************
|
|
* $Id: overview.cpp 28142 2014-12-14 20:09:42Z goatbar $
|
|
*
|
|
* Project: GDAL Core
|
|
* Purpose: Helper code to implement overview support in different drivers.
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2000, Frank Warmerdam
|
|
* Copyright (c) 2007-2010, Even Rouault <even dot rouault at mines-paris dot org>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "gdal_priv.h"
|
|
#include "gdalwarper.h"
|
|
|
|
CPL_CVSID("$Id: overview.cpp 28142 2014-12-14 20:09:42Z goatbar $");
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleChunk32R_Near() */
|
|
/************************************************************************/
|
|
|
|
template <class T>
|
|
static CPLErr
|
|
GDALResampleChunk32R_NearT( double dfXRatioDstToSrc,
|
|
double dfYRatioDstToSrc,
|
|
GDALDataType eWrkDataType,
|
|
T * pChunk,
|
|
int nChunkXOff, int nChunkXSize,
|
|
int nChunkYOff,
|
|
int nDstXOff, int nDstXOff2,
|
|
int nDstYOff, int nDstYOff2,
|
|
GDALRasterBand * poOverview)
|
|
|
|
{
|
|
CPLErr eErr = CE_None;
|
|
|
|
int nDstXWidth = nDstXOff2 - nDstXOff;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Allocate scanline buffer. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
T* pDstScanline = (T *) VSIMalloc(nDstXWidth * (GDALGetDataTypeSize(eWrkDataType) / 8));
|
|
int* panSrcXOff = (int*)VSIMalloc(nDstXWidth * sizeof(int));
|
|
|
|
if( pDstScanline == NULL || panSrcXOff == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"GDALResampleChunk32R: Out of memory for line buffer." );
|
|
VSIFree(pDstScanline);
|
|
VSIFree(panSrcXOff);
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* Precompute inner loop constants. */
|
|
/* ==================================================================== */
|
|
int iDstPixel;
|
|
for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ )
|
|
{
|
|
int nSrcXOff;
|
|
|
|
nSrcXOff =
|
|
(int) (0.5 + iDstPixel * dfXRatioDstToSrc);
|
|
if ( nSrcXOff < nChunkXOff )
|
|
nSrcXOff = nChunkXOff;
|
|
|
|
panSrcXOff[iDstPixel - nDstXOff] = nSrcXOff;
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* Loop over destination scanlines. */
|
|
/* ==================================================================== */
|
|
for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ )
|
|
{
|
|
T *pSrcScanline;
|
|
int nSrcYOff;
|
|
|
|
nSrcYOff = (int) (0.5 + iDstLine * dfYRatioDstToSrc);
|
|
if ( nSrcYOff < nChunkYOff )
|
|
nSrcYOff = nChunkYOff;
|
|
|
|
pSrcScanline = pChunk + ((nSrcYOff-nChunkYOff) * nChunkXSize) - nChunkXOff;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Loop over destination pixels */
|
|
/* -------------------------------------------------------------------- */
|
|
for( iDstPixel = 0; iDstPixel < nDstXWidth; iDstPixel++ )
|
|
{
|
|
pDstScanline[iDstPixel] = pSrcScanline[panSrcXOff[iDstPixel]];
|
|
}
|
|
|
|
eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXWidth, 1,
|
|
pDstScanline, nDstXWidth, 1, eWrkDataType,
|
|
0, 0, NULL );
|
|
}
|
|
|
|
CPLFree( pDstScanline );
|
|
CPLFree( panSrcXOff );
|
|
|
|
return eErr;
|
|
}
|
|
|
|
static CPLErr
|
|
GDALResampleChunk32R_Near( double dfXRatioDstToSrc,
|
|
double dfYRatioDstToSrc,
|
|
CPL_UNUSED double dfSrcXDelta,
|
|
CPL_UNUSED double dfSrcYDelta,
|
|
GDALDataType eWrkDataType,
|
|
void * pChunk,
|
|
CPL_UNUSED GByte * pabyChunkNodataMask_unused,
|
|
int nChunkXOff, int nChunkXSize,
|
|
int nChunkYOff, CPL_UNUSED int nChunkYSize,
|
|
int nDstXOff, int nDstXOff2,
|
|
int nDstYOff, int nDstYOff2,
|
|
GDALRasterBand * poOverview,
|
|
CPL_UNUSED const char * pszResampling_unused,
|
|
CPL_UNUSED int bHasNoData_unused,
|
|
CPL_UNUSED float fNoDataValue_unused,
|
|
CPL_UNUSED GDALColorTable* poColorTable_unused,
|
|
CPL_UNUSED GDALDataType eSrcDataType)
|
|
{
|
|
if (eWrkDataType == GDT_Byte)
|
|
return GDALResampleChunk32R_NearT(dfXRatioDstToSrc,
|
|
dfYRatioDstToSrc,
|
|
eWrkDataType,
|
|
(GByte *) pChunk,
|
|
nChunkXOff, nChunkXSize,
|
|
nChunkYOff,
|
|
nDstXOff, nDstXOff2,
|
|
nDstYOff, nDstYOff2,
|
|
poOverview);
|
|
else if (eWrkDataType == GDT_UInt16)
|
|
return GDALResampleChunk32R_NearT(dfXRatioDstToSrc,
|
|
dfYRatioDstToSrc,
|
|
eWrkDataType,
|
|
(GInt16 *) pChunk,
|
|
nChunkXOff, nChunkXSize,
|
|
nChunkYOff,
|
|
nDstXOff, nDstXOff2,
|
|
nDstYOff, nDstYOff2,
|
|
poOverview);
|
|
else if (eWrkDataType == GDT_Float32)
|
|
return GDALResampleChunk32R_NearT(dfXRatioDstToSrc,
|
|
dfYRatioDstToSrc,
|
|
eWrkDataType,
|
|
(float *) pChunk,
|
|
nChunkXOff, nChunkXSize,
|
|
nChunkYOff,
|
|
nDstXOff, nDstXOff2,
|
|
nDstYOff, nDstYOff2,
|
|
poOverview);
|
|
|
|
CPLAssert(0);
|
|
return CE_Failure;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALFindBestEntry() */
|
|
/************************************************************************/
|
|
|
|
static int GDALFindBestEntry(int nEntryCount, const GDALColorEntry* aEntries,
|
|
int nR, int nG, int nB)
|
|
{
|
|
int i;
|
|
int nMinDist = 0;
|
|
int iBestEntry = 0;
|
|
for(i=0;i<nEntryCount;i++)
|
|
{
|
|
int nDist = (nR - aEntries[i].c1) * (nR - aEntries[i].c1) +
|
|
(nG - aEntries[i].c2) * (nG - aEntries[i].c2) +
|
|
(nB - aEntries[i].c3) * (nB - aEntries[i].c3);
|
|
if (i == 0 || nDist < nMinDist)
|
|
{
|
|
nMinDist = nDist;
|
|
iBestEntry = i;
|
|
}
|
|
}
|
|
return iBestEntry;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleChunk32R_Average() */
|
|
/************************************************************************/
|
|
|
|
template <class T, class Tsum>
|
|
static CPLErr
|
|
GDALResampleChunk32R_AverageT( double dfXRatioDstToSrc,
|
|
double dfYRatioDstToSrc,
|
|
GDALDataType eWrkDataType,
|
|
T* pChunk,
|
|
GByte * pabyChunkNodataMask,
|
|
int nChunkXOff, int nChunkXSize,
|
|
int nChunkYOff, int nChunkYSize,
|
|
int nDstXOff, int nDstXOff2,
|
|
int nDstYOff, int nDstYOff2,
|
|
GDALRasterBand * poOverview,
|
|
const char * pszResampling,
|
|
int bHasNoData, float fNoDataValue,
|
|
GDALColorTable* poColorTable)
|
|
{
|
|
CPLErr eErr = CE_None;
|
|
|
|
int bBit2Grayscale = EQUALN(pszResampling,"AVERAGE_BIT2GRAYSCALE",13);
|
|
if (bBit2Grayscale)
|
|
poColorTable = NULL;
|
|
|
|
int nOXSize, nOYSize;
|
|
T *pDstScanline;
|
|
|
|
T tNoDataValue = (T)fNoDataValue;
|
|
if (!bHasNoData)
|
|
tNoDataValue = 0;
|
|
|
|
nOXSize = poOverview->GetXSize();
|
|
nOYSize = poOverview->GetYSize();
|
|
|
|
int nChunkRightXOff = nChunkXOff + nChunkXSize;
|
|
int nChunkBottomYOff = nChunkYOff + nChunkYSize;
|
|
int nDstXWidth = nDstXOff2 - nDstXOff;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Allocate scanline buffer. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
pDstScanline = (T *) VSIMalloc(nDstXWidth * (GDALGetDataTypeSize(eWrkDataType) / 8));
|
|
int* panSrcXOffShifted = (int*)VSIMalloc(2 * nDstXWidth * sizeof(int));
|
|
|
|
if( pDstScanline == NULL || panSrcXOffShifted == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"GDALResampleChunk32R: Out of memory for line buffer." );
|
|
VSIFree(pDstScanline);
|
|
VSIFree(panSrcXOffShifted);
|
|
return CE_Failure;
|
|
}
|
|
|
|
int nEntryCount = 0;
|
|
GDALColorEntry* aEntries = NULL;
|
|
if (poColorTable)
|
|
{
|
|
int i;
|
|
nEntryCount = poColorTable->GetColorEntryCount();
|
|
aEntries = (GDALColorEntry* )CPLMalloc(sizeof(GDALColorEntry) * nEntryCount);
|
|
for(i=0;i<nEntryCount;i++)
|
|
{
|
|
poColorTable->GetColorEntryAsRGB(i, &aEntries[i]);
|
|
}
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* Precompute inner loop constants. */
|
|
/* ==================================================================== */
|
|
int iDstPixel;
|
|
int bSrcXSpacingIsTwo = TRUE;
|
|
for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ )
|
|
{
|
|
int nSrcXOff, nSrcXOff2;
|
|
|
|
nSrcXOff =
|
|
(int) (0.5 + iDstPixel * dfXRatioDstToSrc);
|
|
if ( nSrcXOff < nChunkXOff )
|
|
nSrcXOff = nChunkXOff;
|
|
nSrcXOff2 = (int)
|
|
(0.5 + (iDstPixel+1)* dfXRatioDstToSrc);
|
|
if( nSrcXOff2 == nSrcXOff )
|
|
nSrcXOff2 ++;
|
|
|
|
if( nSrcXOff2 > nChunkRightXOff || (dfXRatioDstToSrc > 1 && iDstPixel == nOXSize-1) )
|
|
{
|
|
if( nSrcXOff == nChunkRightXOff && nChunkRightXOff - 1 >= nChunkXOff )
|
|
nSrcXOff = nChunkRightXOff - 1;
|
|
nSrcXOff2 = nChunkRightXOff;
|
|
}
|
|
|
|
panSrcXOffShifted[2 * (iDstPixel - nDstXOff)] = nSrcXOff - nChunkXOff;
|
|
panSrcXOffShifted[2 * (iDstPixel - nDstXOff) + 1] = nSrcXOff2 - nChunkXOff;
|
|
if (nSrcXOff2 - nSrcXOff != 2)
|
|
bSrcXSpacingIsTwo = FALSE;
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* Loop over destination scanlines. */
|
|
/* ==================================================================== */
|
|
for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ )
|
|
{
|
|
int nSrcYOff, nSrcYOff2 = 0;
|
|
|
|
nSrcYOff = (int) (0.5 + iDstLine * dfYRatioDstToSrc);
|
|
if ( nSrcYOff < nChunkYOff )
|
|
nSrcYOff = nChunkYOff;
|
|
|
|
nSrcYOff2 =
|
|
(int) (0.5 + (iDstLine+1) * dfYRatioDstToSrc);
|
|
if( nSrcYOff2 == nSrcYOff )
|
|
nSrcYOff2 ++;
|
|
|
|
if( nSrcYOff2 > nChunkBottomYOff || (dfYRatioDstToSrc > 1 && iDstLine == nOYSize-1) )
|
|
{
|
|
if( nSrcYOff == nChunkBottomYOff && nChunkBottomYOff - 1 >= nChunkYOff )
|
|
nSrcYOff = nChunkBottomYOff - 1;
|
|
nSrcYOff2 = nChunkBottomYOff;
|
|
}
|
|
if( nSrcYOff2 <= nSrcYOff )
|
|
CPLDebug("GDAL", "nSrcYOff=%d nSrcYOff2=%d", nSrcYOff, nSrcYOff2);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Loop over destination pixels */
|
|
/* -------------------------------------------------------------------- */
|
|
if (poColorTable == NULL)
|
|
{
|
|
if (bSrcXSpacingIsTwo && nSrcYOff2 == nSrcYOff + 2 &&
|
|
pabyChunkNodataMask == NULL && (eWrkDataType == GDT_Byte || eWrkDataType == GDT_UInt16))
|
|
{
|
|
/* Optimized case : no nodata, overview by a factor of 2 and regular x and y src spacing */
|
|
T* pSrcScanlineShifted = pChunk + panSrcXOffShifted[0] + (nSrcYOff - nChunkYOff) * nChunkXSize;
|
|
for( iDstPixel = 0; iDstPixel < nDstXWidth; iDstPixel++ )
|
|
{
|
|
Tsum nTotal;
|
|
|
|
nTotal = pSrcScanlineShifted[0];
|
|
nTotal += pSrcScanlineShifted[1];
|
|
nTotal += pSrcScanlineShifted[nChunkXSize];
|
|
nTotal += pSrcScanlineShifted[1+nChunkXSize];
|
|
|
|
pDstScanline[iDstPixel] = (T) ((nTotal + 2) / 4);
|
|
pSrcScanlineShifted += 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nSrcYOff -= nChunkYOff;
|
|
nSrcYOff2 -= nChunkYOff;
|
|
|
|
for( iDstPixel = 0; iDstPixel < nDstXWidth; iDstPixel++ )
|
|
{
|
|
int nSrcXOff = panSrcXOffShifted[2 * iDstPixel],
|
|
nSrcXOff2 = panSrcXOffShifted[2 * iDstPixel + 1];
|
|
|
|
T val;
|
|
Tsum dfTotal = 0;
|
|
int nCount = 0, iX, iY;
|
|
|
|
for( iY = nSrcYOff; iY < nSrcYOff2; iY++ )
|
|
{
|
|
for( iX = nSrcXOff; iX < nSrcXOff2; iX++ )
|
|
{
|
|
val = pChunk[iX + iY *nChunkXSize];
|
|
if (pabyChunkNodataMask == NULL ||
|
|
pabyChunkNodataMask[iX + iY *nChunkXSize])
|
|
{
|
|
dfTotal += val;
|
|
nCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( nCount == 0 )
|
|
pDstScanline[iDstPixel] = tNoDataValue;
|
|
else if (eWrkDataType == GDT_Byte || eWrkDataType == GDT_UInt16)
|
|
pDstScanline[iDstPixel] = (T) ((dfTotal + nCount / 2) / nCount);
|
|
else
|
|
pDstScanline[iDstPixel] = (T) (dfTotal / nCount);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nSrcYOff -= nChunkYOff;
|
|
nSrcYOff2 -= nChunkYOff;
|
|
|
|
for( iDstPixel = 0; iDstPixel < nDstXWidth; iDstPixel++ )
|
|
{
|
|
int nSrcXOff = panSrcXOffShifted[2 * iDstPixel],
|
|
nSrcXOff2 = panSrcXOffShifted[2 * iDstPixel + 1];
|
|
|
|
T val;
|
|
int nTotalR = 0, nTotalG = 0, nTotalB = 0;
|
|
int nCount = 0, iX, iY;
|
|
|
|
for( iY = nSrcYOff; iY < nSrcYOff2; iY++ )
|
|
{
|
|
for( iX = nSrcXOff; iX < nSrcXOff2; iX++ )
|
|
{
|
|
val = pChunk[iX + iY *nChunkXSize];
|
|
if (bHasNoData == FALSE || val != tNoDataValue)
|
|
{
|
|
int nVal = (int)val;
|
|
if (nVal >= 0 && nVal < nEntryCount)
|
|
{
|
|
nTotalR += aEntries[nVal].c1;
|
|
nTotalG += aEntries[nVal].c2;
|
|
nTotalB += aEntries[nVal].c3;
|
|
nCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( nCount == 0 )
|
|
pDstScanline[iDstPixel] = tNoDataValue;
|
|
else
|
|
{
|
|
int nR = (nTotalR + nCount / 2) / nCount,
|
|
nG = (nTotalG + nCount / 2) / nCount,
|
|
nB = (nTotalB + nCount / 2) / nCount;
|
|
pDstScanline[iDstPixel] = (T)GDALFindBestEntry(
|
|
nEntryCount, aEntries, nR, nG, nB);
|
|
}
|
|
}
|
|
}
|
|
|
|
eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXWidth, 1,
|
|
pDstScanline, nDstXWidth, 1, eWrkDataType,
|
|
0, 0, NULL );
|
|
}
|
|
|
|
CPLFree( pDstScanline );
|
|
CPLFree( aEntries );
|
|
CPLFree( panSrcXOffShifted );
|
|
|
|
return eErr;
|
|
}
|
|
|
|
static CPLErr
|
|
GDALResampleChunk32R_Average( double dfXRatioDstToSrc, double dfYRatioDstToSrc,
|
|
CPL_UNUSED double dfSrcXDelta,
|
|
CPL_UNUSED double dfSrcYDelta,
|
|
GDALDataType eWrkDataType,
|
|
void * pChunk,
|
|
GByte * pabyChunkNodataMask,
|
|
int nChunkXOff, int nChunkXSize,
|
|
int nChunkYOff, int nChunkYSize,
|
|
int nDstXOff, int nDstXOff2,
|
|
int nDstYOff, int nDstYOff2,
|
|
GDALRasterBand * poOverview,
|
|
const char * pszResampling,
|
|
int bHasNoData, float fNoDataValue,
|
|
GDALColorTable* poColorTable,
|
|
CPL_UNUSED GDALDataType eSrcDataType)
|
|
{
|
|
if (eWrkDataType == GDT_Byte)
|
|
return GDALResampleChunk32R_AverageT<GByte, int>(dfXRatioDstToSrc, dfYRatioDstToSrc,
|
|
eWrkDataType,
|
|
(GByte *) pChunk,
|
|
pabyChunkNodataMask,
|
|
nChunkXOff, nChunkXSize,
|
|
nChunkYOff, nChunkYSize,
|
|
nDstXOff, nDstXOff2,
|
|
nDstYOff, nDstYOff2,
|
|
poOverview,
|
|
pszResampling,
|
|
bHasNoData, fNoDataValue,
|
|
poColorTable);
|
|
else if (eWrkDataType == GDT_UInt16 && dfXRatioDstToSrc * dfYRatioDstToSrc < 65536 )
|
|
return GDALResampleChunk32R_AverageT<GUInt16, GUInt32>(dfXRatioDstToSrc, dfYRatioDstToSrc,
|
|
eWrkDataType,
|
|
(GUInt16 *) pChunk,
|
|
pabyChunkNodataMask,
|
|
nChunkXOff, nChunkXSize,
|
|
nChunkYOff, nChunkYSize,
|
|
nDstXOff, nDstXOff2,
|
|
nDstYOff, nDstYOff2,
|
|
poOverview,
|
|
pszResampling,
|
|
bHasNoData, fNoDataValue,
|
|
poColorTable);
|
|
else if (eWrkDataType == GDT_Float32)
|
|
return GDALResampleChunk32R_AverageT<float, double>(dfXRatioDstToSrc, dfYRatioDstToSrc,
|
|
eWrkDataType,
|
|
(float *) pChunk,
|
|
pabyChunkNodataMask,
|
|
nChunkXOff, nChunkXSize,
|
|
nChunkYOff, nChunkYSize,
|
|
nDstXOff, nDstXOff2,
|
|
nDstYOff, nDstYOff2,
|
|
poOverview,
|
|
pszResampling,
|
|
bHasNoData, fNoDataValue,
|
|
poColorTable);
|
|
|
|
CPLAssert(0);
|
|
return CE_Failure;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleChunk32R_Gauss() */
|
|
/************************************************************************/
|
|
|
|
static CPLErr
|
|
GDALResampleChunk32R_Gauss( double dfXRatioDstToSrc, double dfYRatioDstToSrc,
|
|
CPL_UNUSED double dfSrcXDelta,
|
|
CPL_UNUSED double dfSrcYDelta,
|
|
CPL_UNUSED GDALDataType eWrkDataType,
|
|
void * pChunk,
|
|
GByte * pabyChunkNodataMask,
|
|
int nChunkXOff, int nChunkXSize,
|
|
int nChunkYOff, int nChunkYSize,
|
|
int nDstXOff, int nDstXOff2,
|
|
int nDstYOff, int nDstYOff2,
|
|
GDALRasterBand * poOverview,
|
|
CPL_UNUSED const char * pszResampling,
|
|
int bHasNoData, float fNoDataValue,
|
|
GDALColorTable* poColorTable,
|
|
CPL_UNUSED GDALDataType eSrcDataType)
|
|
|
|
{
|
|
CPLErr eErr = CE_None;
|
|
|
|
float * pafChunk = (float*) pChunk;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create the filter kernel and allocate scanline buffer. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nOXSize, nOYSize;
|
|
float *pafDstScanline;
|
|
int nGaussMatrixDim = 3;
|
|
const int *panGaussMatrix;
|
|
static const int anGaussMatrix3x3[] ={
|
|
1,2,1,
|
|
2,4,2,
|
|
1,2,1
|
|
};
|
|
static const int anGaussMatrix5x5[] = {
|
|
1,4,6,4,1,
|
|
4,16,24,16,4,
|
|
6,24,36,24,6,
|
|
4,16,24,16,4,
|
|
1,4,6,4,1};
|
|
static const int anGaussMatrix7x7[] = {
|
|
1,6,15,20,15,6,1,
|
|
6,36,90,120,90,36,6,
|
|
15,90,225,300,225,90,15,
|
|
20,120,300,400,300,120,20,
|
|
15,90,225,300,225,90,15,
|
|
6,36,90,120,90,36,6,
|
|
1,6,15,20,15,6,1};
|
|
|
|
nOXSize = poOverview->GetXSize();
|
|
nOYSize = poOverview->GetYSize();
|
|
int nResYFactor = (int) (0.5 + dfYRatioDstToSrc);
|
|
|
|
// matrix for gauss filter
|
|
if(nResYFactor <= 2 )
|
|
{
|
|
panGaussMatrix = anGaussMatrix3x3;
|
|
nGaussMatrixDim=3;
|
|
}
|
|
else if (nResYFactor <= 4)
|
|
{
|
|
panGaussMatrix = anGaussMatrix5x5;
|
|
nGaussMatrixDim=5;
|
|
}
|
|
else
|
|
{
|
|
panGaussMatrix = anGaussMatrix7x7;
|
|
nGaussMatrixDim=7;
|
|
}
|
|
|
|
pafDstScanline = (float *) VSIMalloc((nDstXOff2 - nDstXOff) * sizeof(float));
|
|
if( pafDstScanline == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"GDALResampleChunk32R: Out of memory for line buffer." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
int nEntryCount = 0;
|
|
GDALColorEntry* aEntries = NULL;
|
|
if (poColorTable)
|
|
{
|
|
int i;
|
|
nEntryCount = poColorTable->GetColorEntryCount();
|
|
aEntries = (GDALColorEntry* )CPLMalloc(sizeof(GDALColorEntry) * nEntryCount);
|
|
for(i=0;i<nEntryCount;i++)
|
|
{
|
|
poColorTable->GetColorEntryAsRGB(i, &aEntries[i]);
|
|
}
|
|
}
|
|
|
|
int nChunkRightXOff = nChunkXOff + nChunkXSize;
|
|
int nChunkBottomYOff = nChunkYOff + nChunkYSize;
|
|
|
|
/* ==================================================================== */
|
|
/* Loop over destination scanlines. */
|
|
/* ==================================================================== */
|
|
for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ )
|
|
{
|
|
float *pafSrcScanline;
|
|
GByte *pabySrcScanlineNodataMask;
|
|
int nSrcYOff, nSrcYOff2 = 0, iDstPixel;
|
|
|
|
nSrcYOff = (int) (0.5 + iDstLine * dfYRatioDstToSrc);
|
|
nSrcYOff2 = (int) (0.5 + (iDstLine+1) * dfYRatioDstToSrc) + 1;
|
|
|
|
if( nSrcYOff < nChunkYOff )
|
|
{
|
|
nSrcYOff = nChunkYOff;
|
|
nSrcYOff2++;
|
|
}
|
|
|
|
int iSizeY = nSrcYOff2 - nSrcYOff;
|
|
nSrcYOff = nSrcYOff + iSizeY/2 - nGaussMatrixDim/2;
|
|
nSrcYOff2 = nSrcYOff + nGaussMatrixDim;
|
|
int nYShiftGaussMatrix = 0;
|
|
if(nSrcYOff < 0)
|
|
{
|
|
nYShiftGaussMatrix = -nSrcYOff;
|
|
nSrcYOff = 0;
|
|
}
|
|
|
|
|
|
if( nSrcYOff2 > nChunkBottomYOff || (dfYRatioDstToSrc > 1 && iDstLine == nOYSize-1) )
|
|
nSrcYOff2 = nChunkBottomYOff;
|
|
|
|
pafSrcScanline = pafChunk + ((nSrcYOff-nChunkYOff) * nChunkXSize);
|
|
if (pabyChunkNodataMask != NULL)
|
|
pabySrcScanlineNodataMask = pabyChunkNodataMask + ((nSrcYOff-nChunkYOff) * nChunkXSize);
|
|
else
|
|
pabySrcScanlineNodataMask = NULL;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Loop over destination pixels */
|
|
/* -------------------------------------------------------------------- */
|
|
for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ )
|
|
{
|
|
int nSrcXOff, nSrcXOff2;
|
|
|
|
nSrcXOff = (int) (0.5 + iDstPixel * dfXRatioDstToSrc);
|
|
nSrcXOff2 = (int)(0.5 + (iDstPixel+1) * dfXRatioDstToSrc) + 1;
|
|
|
|
int iSizeX = nSrcXOff2 - nSrcXOff;
|
|
nSrcXOff = nSrcXOff + iSizeX/2 - nGaussMatrixDim/2;
|
|
nSrcXOff2 = nSrcXOff + nGaussMatrixDim;
|
|
int nXShiftGaussMatrix = 0;
|
|
if(nSrcXOff < 0)
|
|
{
|
|
nXShiftGaussMatrix = -nSrcXOff;
|
|
nSrcXOff = 0;
|
|
}
|
|
|
|
if( nSrcXOff2 > nChunkRightXOff || (dfXRatioDstToSrc > 1 && iDstPixel == nOXSize-1) )
|
|
nSrcXOff2 = nChunkRightXOff;
|
|
|
|
if (poColorTable == NULL)
|
|
{
|
|
double dfTotal = 0.0, val;
|
|
int nCount = 0, iX, iY;
|
|
int i = 0,j = 0;
|
|
const int *panLineWeight = panGaussMatrix +
|
|
nYShiftGaussMatrix * nGaussMatrixDim + nXShiftGaussMatrix;
|
|
|
|
for( j=0, iY = nSrcYOff; iY < nSrcYOff2;
|
|
iY++, j++, panLineWeight += nGaussMatrixDim )
|
|
{
|
|
for( i=0, iX = nSrcXOff; iX < nSrcXOff2; iX++,++i )
|
|
{
|
|
val = pafSrcScanline[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize];
|
|
if (pabySrcScanlineNodataMask == NULL ||
|
|
pabySrcScanlineNodataMask[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize])
|
|
{
|
|
int nWeight = panLineWeight[i];
|
|
dfTotal += val * nWeight;
|
|
nCount += nWeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bHasNoData && nCount == 0)
|
|
{
|
|
pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue;
|
|
}
|
|
else
|
|
{
|
|
if( nCount == 0 )
|
|
pafDstScanline[iDstPixel - nDstXOff] = 0.0;
|
|
else
|
|
pafDstScanline[iDstPixel - nDstXOff] = (float) (dfTotal / nCount);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double val;
|
|
int nTotalR = 0, nTotalG = 0, nTotalB = 0;
|
|
int nTotalWeight = 0, iX, iY;
|
|
int i = 0,j = 0;
|
|
const int *panLineWeight = panGaussMatrix +
|
|
nYShiftGaussMatrix * nGaussMatrixDim + nXShiftGaussMatrix;
|
|
|
|
for( j=0, iY = nSrcYOff; iY < nSrcYOff2;
|
|
iY++, j++, panLineWeight += nGaussMatrixDim )
|
|
{
|
|
for( i=0, iX = nSrcXOff; iX < nSrcXOff2; iX++,++i )
|
|
{
|
|
val = pafSrcScanline[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize];
|
|
if (bHasNoData == FALSE || val != fNoDataValue)
|
|
{
|
|
int nVal = (int)val;
|
|
if (nVal >= 0 && nVal < nEntryCount)
|
|
{
|
|
int nWeight = panLineWeight[i];
|
|
nTotalR += aEntries[nVal].c1 * nWeight;
|
|
nTotalG += aEntries[nVal].c2 * nWeight;
|
|
nTotalB += aEntries[nVal].c3 * nWeight;
|
|
nTotalWeight += nWeight;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bHasNoData && nTotalWeight == 0)
|
|
{
|
|
pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue;
|
|
}
|
|
else
|
|
{
|
|
if( nTotalWeight == 0 )
|
|
pafDstScanline[iDstPixel - nDstXOff] = 0.0;
|
|
else
|
|
{
|
|
int nR = (nTotalR + nTotalWeight / 2) / nTotalWeight,
|
|
nG = (nTotalG + nTotalWeight / 2) / nTotalWeight,
|
|
nB = (nTotalB + nTotalWeight / 2) / nTotalWeight;
|
|
pafDstScanline[iDstPixel - nDstXOff] =
|
|
(float) GDALFindBestEntry(
|
|
nEntryCount, aEntries, nR, nG, nB);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXOff2 - nDstXOff, 1,
|
|
pafDstScanline, nDstXOff2 - nDstXOff, 1, GDT_Float32,
|
|
0, 0, NULL );
|
|
}
|
|
|
|
CPLFree( pafDstScanline );
|
|
CPLFree( aEntries );
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleChunk32R_Mode() */
|
|
/************************************************************************/
|
|
|
|
static CPLErr
|
|
GDALResampleChunk32R_Mode( double dfXRatioDstToSrc, double dfYRatioDstToSrc,
|
|
CPL_UNUSED double dfSrcXDelta,
|
|
CPL_UNUSED double dfSrcYDelta,
|
|
CPL_UNUSED GDALDataType eWrkDataType,
|
|
void * pChunk,
|
|
GByte * pabyChunkNodataMask,
|
|
int nChunkXOff, int nChunkXSize,
|
|
int nChunkYOff, int nChunkYSize,
|
|
int nDstXOff, int nDstXOff2,
|
|
int nDstYOff, int nDstYOff2,
|
|
GDALRasterBand * poOverview,
|
|
CPL_UNUSED const char * pszResampling,
|
|
int bHasNoData, float fNoDataValue,
|
|
GDALColorTable* poColorTable,
|
|
GDALDataType eSrcDataType)
|
|
|
|
{
|
|
CPLErr eErr = CE_None;
|
|
|
|
float * pafChunk = (float*) pChunk;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create the filter kernel and allocate scanline buffer. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nOXSize, nOYSize;
|
|
float *pafDstScanline;
|
|
|
|
nOXSize = poOverview->GetXSize();
|
|
nOYSize = poOverview->GetYSize();
|
|
|
|
pafDstScanline = (float *) VSIMalloc((nDstXOff2 - nDstXOff) * sizeof(float));
|
|
if( pafDstScanline == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"GDALResampleChunk32R: Out of memory for line buffer." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
int nEntryCount = 0;
|
|
GDALColorEntry* aEntries = NULL;
|
|
if (poColorTable)
|
|
{
|
|
int i;
|
|
nEntryCount = poColorTable->GetColorEntryCount();
|
|
aEntries = (GDALColorEntry* )CPLMalloc(sizeof(GDALColorEntry) * nEntryCount);
|
|
for(i=0;i<nEntryCount;i++)
|
|
{
|
|
poColorTable->GetColorEntryAsRGB(i, &aEntries[i]);
|
|
}
|
|
}
|
|
|
|
int nMaxNumPx = 0;
|
|
float* pafVals = NULL;
|
|
int* panSums = NULL;
|
|
|
|
int nChunkRightXOff = nChunkXOff + nChunkXSize;
|
|
int nChunkBottomYOff = nChunkYOff + nChunkYSize;
|
|
|
|
/* ==================================================================== */
|
|
/* Loop over destination scanlines. */
|
|
/* ==================================================================== */
|
|
for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ )
|
|
{
|
|
float *pafSrcScanline;
|
|
GByte *pabySrcScanlineNodataMask;
|
|
int nSrcYOff, nSrcYOff2 = 0, iDstPixel;
|
|
|
|
nSrcYOff = (int) (0.5 + iDstLine * dfYRatioDstToSrc);
|
|
if ( nSrcYOff < nChunkYOff )
|
|
nSrcYOff = nChunkYOff;
|
|
|
|
nSrcYOff2 =
|
|
(int) (0.5 + (iDstLine+1) * dfYRatioDstToSrc);
|
|
if( nSrcYOff2 == nSrcYOff )
|
|
nSrcYOff2 ++;
|
|
|
|
if( nSrcYOff2 > nChunkBottomYOff || (dfYRatioDstToSrc > 1 && iDstLine == nOYSize-1) )
|
|
{
|
|
if( nSrcYOff == nChunkBottomYOff && nChunkBottomYOff - 1 >= nChunkYOff )
|
|
nSrcYOff = nChunkBottomYOff - 1;
|
|
nSrcYOff2 = nChunkBottomYOff;
|
|
}
|
|
|
|
pafSrcScanline = pafChunk + ((nSrcYOff-nChunkYOff) * nChunkXSize);
|
|
if (pabyChunkNodataMask != NULL)
|
|
pabySrcScanlineNodataMask = pabyChunkNodataMask + ((nSrcYOff-nChunkYOff) * nChunkXSize);
|
|
else
|
|
pabySrcScanlineNodataMask = NULL;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Loop over destination pixels */
|
|
/* -------------------------------------------------------------------- */
|
|
for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ )
|
|
{
|
|
int nSrcXOff, nSrcXOff2;
|
|
|
|
nSrcXOff =
|
|
(int) (0.5 + iDstPixel * dfXRatioDstToSrc);
|
|
if ( nSrcXOff < nChunkXOff )
|
|
nSrcXOff = nChunkXOff;
|
|
nSrcXOff2 = (int)
|
|
(0.5 + (iDstPixel+1) * dfXRatioDstToSrc);
|
|
if( nSrcXOff2 == nSrcXOff )
|
|
nSrcXOff2 ++;
|
|
|
|
if( nSrcXOff2 > nChunkRightXOff || (dfXRatioDstToSrc > 1 && iDstPixel == nOXSize-1) )
|
|
{
|
|
if( nSrcXOff == nChunkRightXOff && nChunkRightXOff - 1 >= nChunkXOff )
|
|
nSrcXOff = nChunkRightXOff - 1;
|
|
nSrcXOff2 = nChunkRightXOff;
|
|
}
|
|
|
|
if (eSrcDataType != GDT_Byte || nEntryCount > 256)
|
|
{
|
|
/* I'm not sure how much sense it makes to run a majority
|
|
filter on floating point data, but here it is for the sake
|
|
of compatability. It won't look right on RGB images by the
|
|
nature of the filter. */
|
|
int nNumPx = (nSrcYOff2-nSrcYOff)*(nSrcXOff2-nSrcXOff);
|
|
int iMaxInd = 0, iMaxVal = -1, iY, iX;
|
|
|
|
if (nNumPx > nMaxNumPx)
|
|
{
|
|
pafVals = (float*) CPLRealloc(pafVals, nNumPx * sizeof(float));
|
|
panSums = (int*) CPLRealloc(panSums, nNumPx * sizeof(int));
|
|
nMaxNumPx = nNumPx;
|
|
}
|
|
|
|
for( iY = nSrcYOff; iY < nSrcYOff2; ++iY )
|
|
{
|
|
int iTotYOff = (iY-nSrcYOff)*nChunkXSize-nChunkXOff;
|
|
for( iX = nSrcXOff; iX < nSrcXOff2; ++iX )
|
|
{
|
|
if (pabySrcScanlineNodataMask == NULL ||
|
|
pabySrcScanlineNodataMask[iX+iTotYOff])
|
|
{
|
|
float fVal = pafSrcScanline[iX+iTotYOff];
|
|
int i;
|
|
|
|
//Check array for existing entry
|
|
for( i = 0; i < iMaxInd; ++i )
|
|
if( pafVals[i] == fVal
|
|
&& ++panSums[i] > panSums[iMaxVal] )
|
|
{
|
|
iMaxVal = i;
|
|
break;
|
|
}
|
|
|
|
//Add to arr if entry not already there
|
|
if( i == iMaxInd )
|
|
{
|
|
pafVals[iMaxInd] = fVal;
|
|
panSums[iMaxInd] = 1;
|
|
|
|
if( iMaxVal < 0 )
|
|
iMaxVal = iMaxInd;
|
|
|
|
++iMaxInd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( iMaxVal == -1 )
|
|
pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue;
|
|
else
|
|
pafDstScanline[iDstPixel - nDstXOff] = pafVals[iMaxVal];
|
|
}
|
|
else /* if (eSrcDataType == GDT_Byte && nEntryCount < 256) */
|
|
{
|
|
/* So we go here for a paletted or non-paletted byte band */
|
|
/* The input values are then between 0 and 255 */
|
|
int anVals[256], nMaxVal = 0, iMaxInd = -1, iY, iX;
|
|
|
|
memset(anVals, 0, 256*sizeof(int));
|
|
|
|
for( iY = nSrcYOff; iY < nSrcYOff2; ++iY )
|
|
{
|
|
int iTotYOff = (iY-nSrcYOff)*nChunkXSize-nChunkXOff;
|
|
for( iX = nSrcXOff; iX < nSrcXOff2; ++iX )
|
|
{
|
|
float val = pafSrcScanline[iX+iTotYOff];
|
|
if (bHasNoData == FALSE || val != fNoDataValue)
|
|
{
|
|
int nVal = (int) val;
|
|
if ( ++anVals[nVal] > nMaxVal)
|
|
{
|
|
//Sum the density
|
|
//Is it the most common value so far?
|
|
iMaxInd = nVal;
|
|
nMaxVal = anVals[nVal];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( iMaxInd == -1 )
|
|
pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue;
|
|
else
|
|
pafDstScanline[iDstPixel - nDstXOff] = (float)iMaxInd;
|
|
}
|
|
}
|
|
|
|
eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXOff2 - nDstXOff, 1,
|
|
pafDstScanline, nDstXOff2 - nDstXOff, 1, GDT_Float32,
|
|
0, 0, NULL );
|
|
}
|
|
|
|
CPLFree( pafDstScanline );
|
|
CPLFree( aEntries );
|
|
CPLFree( pafVals );
|
|
CPLFree( panSums );
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleConvolutionHorizontal() */
|
|
/************************************************************************/
|
|
|
|
template<class T> static inline double GDALResampleConvolutionHorizontal(
|
|
const T* pChunk, const double* padfWeights, int nSrcPixelCount)
|
|
{
|
|
double dfVal1 = 0.0, dfVal2 = 0.0;
|
|
int i = 0;
|
|
for(;i+3<nSrcPixelCount;i+=4)
|
|
{
|
|
dfVal1 += pChunk[i] * padfWeights[i];
|
|
dfVal1 += pChunk[i+1] * padfWeights[i+1];
|
|
dfVal2 += pChunk[i+2] * padfWeights[i+2];
|
|
dfVal2 += pChunk[i+3] * padfWeights[i+3];
|
|
}
|
|
for(;i<nSrcPixelCount;i++)
|
|
{
|
|
dfVal1 += pChunk[i] * padfWeights[i];
|
|
}
|
|
return dfVal1 + dfVal2;
|
|
}
|
|
|
|
template<class T> static inline void GDALResampleConvolutionHorizontalWithMask(
|
|
const T* pChunk, const GByte* pabyMask,
|
|
const double* padfWeights, int nSrcPixelCount,
|
|
double& dfVal, double &dfWeightSum)
|
|
{
|
|
dfVal = 0;
|
|
dfWeightSum = 0;
|
|
int i = 0;
|
|
for(;i+3<nSrcPixelCount;i+=4)
|
|
{
|
|
double dfWeight0 = padfWeights[i] * pabyMask[i];
|
|
double dfWeight1 = padfWeights[i+1] * pabyMask[i+1];
|
|
double dfWeight2 = padfWeights[i+2] * pabyMask[i+2];
|
|
double dfWeight3 = padfWeights[i+3] * pabyMask[i+3];
|
|
dfVal += pChunk[i] * dfWeight0;
|
|
dfVal += pChunk[i+1] * dfWeight1;
|
|
dfVal += pChunk[i+2] * dfWeight2;
|
|
dfVal += pChunk[i+3] * dfWeight3;
|
|
dfWeightSum += dfWeight0 + dfWeight1 + dfWeight2 + dfWeight3;
|
|
}
|
|
for(;i<nSrcPixelCount;i++)
|
|
{
|
|
double dfWeight = padfWeights[i] * pabyMask[i];
|
|
dfVal += pChunk[i] * dfWeight;
|
|
dfWeightSum += dfWeight;
|
|
}
|
|
}
|
|
|
|
template<class T> static inline void GDALResampleConvolutionHorizontal_3rows(
|
|
const T* pChunkRow1, const T* pChunkRow2, const T* pChunkRow3,
|
|
const double* padfWeights, int nSrcPixelCount,
|
|
double& dfRes1, double& dfRes2, double& dfRes3)
|
|
{
|
|
double dfVal1 = 0.0, dfVal2 = 0.0,
|
|
dfVal3 = 0.0, dfVal4 = 0.0,
|
|
dfVal5 = 0.0, dfVal6 = 0.0;
|
|
int i = 0;
|
|
for(;i+3<nSrcPixelCount;i+=4)
|
|
{
|
|
dfVal1 += pChunkRow1[i] * padfWeights[i];
|
|
dfVal1 += pChunkRow1[i+1] * padfWeights[i+1];
|
|
dfVal2 += pChunkRow1[i+2] * padfWeights[i+2];
|
|
dfVal2 += pChunkRow1[i+3] * padfWeights[i+3];
|
|
dfVal3 += pChunkRow2[i] * padfWeights[i];
|
|
dfVal3 += pChunkRow2[i+1] * padfWeights[i+1];
|
|
dfVal4 += pChunkRow2[i+2] * padfWeights[i+2];
|
|
dfVal4 += pChunkRow2[i+3] * padfWeights[i+3];
|
|
dfVal5 += pChunkRow3[i] * padfWeights[i];
|
|
dfVal5 += pChunkRow3[i+1] * padfWeights[i+1];
|
|
dfVal6 += pChunkRow3[i+2] * padfWeights[i+2];
|
|
dfVal6 += pChunkRow3[i+3] * padfWeights[i+3];
|
|
}
|
|
for(;i<nSrcPixelCount;i++)
|
|
{
|
|
dfVal1 += pChunkRow1[i] * padfWeights[i];
|
|
dfVal3 += pChunkRow2[i] * padfWeights[i];
|
|
dfVal5 += pChunkRow3[i] * padfWeights[i];
|
|
}
|
|
dfRes1 = dfVal1 + dfVal2;
|
|
dfRes2 = dfVal3 + dfVal4;
|
|
dfRes3 = dfVal5 + dfVal6;
|
|
}
|
|
|
|
template<class T> static inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows(
|
|
const T* pChunkRow1, const T* pChunkRow2, const T* pChunkRow3,
|
|
const double* padfWeights, int nSrcPixelCount,
|
|
double& dfRes1, double& dfRes2, double& dfRes3)
|
|
{
|
|
GDALResampleConvolutionHorizontal_3rows(
|
|
pChunkRow1, pChunkRow2, pChunkRow3,
|
|
padfWeights, nSrcPixelCount,
|
|
dfRes1, dfRes2, dfRes3);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleConvolutionVertical() */
|
|
/************************************************************************/
|
|
|
|
template<class T> static inline double GDALResampleConvolutionVertical(
|
|
const T* pChunk, int nStride, const double* padfWeights, int nSrcLineCount)
|
|
{
|
|
double dfVal1 = 0.0, dfVal2 = 0.0;
|
|
int i = 0, j = 0;
|
|
for(;i+3<nSrcLineCount;i+=4, j+=4*nStride)
|
|
{
|
|
dfVal1 += pChunk[j] * padfWeights[i];
|
|
dfVal1 += pChunk[j + nStride] * padfWeights[i+1];
|
|
dfVal2 += pChunk[j + 2 * nStride] * padfWeights[i+2];
|
|
dfVal2 += pChunk[j + 3 * nStride] * padfWeights[i+3];
|
|
}
|
|
for(;i<nSrcLineCount;i++,j+=nStride)
|
|
{
|
|
dfVal1 += pChunk[j] * padfWeights[i];
|
|
}
|
|
return dfVal1 + dfVal2;
|
|
}
|
|
|
|
template<class T> static inline void GDALResampleConvolutionVertical_2cols(
|
|
const T* pChunk, int nStride, const double* padfWeights, int nSrcLineCount,
|
|
double& dfRes1, double& dfRes2)
|
|
{
|
|
double dfVal1 = 0.0, dfVal2 = 0.0,
|
|
dfVal3 = 0.0, dfVal4 = 0.0;
|
|
int i = 0, j = 0;
|
|
for(;i+3<nSrcLineCount;i+=4, j+=4*nStride)
|
|
{
|
|
dfVal1 += pChunk[j] * padfWeights[i];
|
|
dfVal3 += pChunk[j+1] * padfWeights[i];
|
|
dfVal1 += pChunk[j + nStride] * padfWeights[i+1];
|
|
dfVal3 += pChunk[j+1 + nStride] * padfWeights[i+1];
|
|
dfVal2 += pChunk[j + 2 * nStride] * padfWeights[i+2];
|
|
dfVal4 += pChunk[j+1 + 2 * nStride] * padfWeights[i+2];
|
|
dfVal2 += pChunk[j + 3 * nStride] * padfWeights[i+3];
|
|
dfVal4 += pChunk[j+1 + 3 * nStride] * padfWeights[i+3];
|
|
}
|
|
for(;i<nSrcLineCount;i++,j+=nStride)
|
|
{
|
|
dfVal1 += pChunk[j] * padfWeights[i];
|
|
dfVal3 += pChunk[j+1] * padfWeights[i];
|
|
}
|
|
dfRes1 = dfVal1 + dfVal2;
|
|
dfRes2 = dfVal3 + dfVal4;
|
|
}
|
|
/* We restrict to 64bit processors because they are guaranteed to have SSE2 */
|
|
/* Could possibly be used too on 32bit, but we would need to check at runtime */
|
|
#if defined(__x86_64) || defined(_M_X64)
|
|
#define USE_SSE2
|
|
#endif
|
|
|
|
#ifdef USE_SSE2
|
|
#include <gdalsse_priv.h>
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleConvolutionHorizontalSSE2<T> */
|
|
/************************************************************************/
|
|
|
|
template<class T> static inline double GDALResampleConvolutionHorizontalSSE2(
|
|
const T* pChunk, const double* padfWeightsAligned, int nSrcPixelCount)
|
|
{
|
|
XMMReg4Double v_acc1 = XMMReg4Double::Zero();
|
|
XMMReg4Double v_acc2 = XMMReg4Double::Zero();
|
|
int i = 0;
|
|
for(;i+7<nSrcPixelCount;i+=8)
|
|
{
|
|
// Retrieve the pixel & accumulate
|
|
XMMReg4Double v_pixels1 = XMMReg4Double::Load4Val(pChunk+i);
|
|
XMMReg4Double v_pixels2 = XMMReg4Double::Load4Val(pChunk+i+4);
|
|
XMMReg4Double v_weight1 = XMMReg4Double::Load4ValAligned(padfWeightsAligned+i);
|
|
XMMReg4Double v_weight2 = XMMReg4Double::Load4ValAligned(padfWeightsAligned+i+4);
|
|
|
|
v_acc1 += v_pixels1 * v_weight1;
|
|
v_acc2 += v_pixels2 * v_weight2;
|
|
}
|
|
|
|
v_acc1 += v_acc2;
|
|
v_acc1.AddLowAndHigh();
|
|
|
|
double dfVal = (double)v_acc1.GetLow();
|
|
for(;i<nSrcPixelCount;i++)
|
|
{
|
|
dfVal += pChunk[i] * padfWeightsAligned[i];
|
|
}
|
|
return dfVal;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleConvolutionHorizontal<GByte> */
|
|
/************************************************************************/
|
|
|
|
template<> inline double GDALResampleConvolutionHorizontal<GByte>(
|
|
const GByte* pChunk, const double* padfWeightsAligned, int nSrcPixelCount)
|
|
{
|
|
return GDALResampleConvolutionHorizontalSSE2(pChunk, padfWeightsAligned, nSrcPixelCount);
|
|
}
|
|
|
|
template<> inline double GDALResampleConvolutionHorizontal<GUInt16>(
|
|
const GUInt16* pChunk, const double* padfWeightsAligned, int nSrcPixelCount)
|
|
{
|
|
return GDALResampleConvolutionHorizontalSSE2(pChunk, padfWeightsAligned, nSrcPixelCount);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleConvolutionHorizontalWithMaskSSE2<T> */
|
|
/************************************************************************/
|
|
|
|
template<class T> static inline void GDALResampleConvolutionHorizontalWithMaskSSE2(
|
|
const T* pChunk, const GByte* pabyMask,
|
|
const double* padfWeightsAligned, int nSrcPixelCount,
|
|
double& dfVal, double &dfWeightSum)
|
|
{
|
|
int i = 0;
|
|
XMMReg4Double v_acc = XMMReg4Double::Zero(),
|
|
v_acc_weight = XMMReg4Double::Zero();
|
|
for(;i+3<nSrcPixelCount;i+=4)
|
|
{
|
|
XMMReg4Double v_pixels = XMMReg4Double::Load4Val(pChunk+i);
|
|
XMMReg4Double v_mask = XMMReg4Double::Load4Val(pabyMask+i);
|
|
XMMReg4Double v_weight = XMMReg4Double::Load4ValAligned(padfWeightsAligned+i);
|
|
v_weight *= v_mask;
|
|
v_acc += v_pixels * v_weight;
|
|
v_acc_weight += v_weight;
|
|
}
|
|
v_acc.AddLowAndHigh();
|
|
v_acc_weight.AddLowAndHigh();
|
|
dfVal = (double)v_acc.GetLow();
|
|
dfWeightSum = (double)v_acc_weight.GetLow();
|
|
for(;i<nSrcPixelCount;i++)
|
|
{
|
|
double dfWeight = padfWeightsAligned[i] * pabyMask[i];
|
|
dfVal += pChunk[i] * dfWeight;
|
|
dfWeightSum += dfWeight;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleConvolutionHorizontalWithMask<GByte> */
|
|
/************************************************************************/
|
|
|
|
template<> inline void GDALResampleConvolutionHorizontalWithMask<GByte>(
|
|
const GByte* pChunk, const GByte* pabyMask,
|
|
const double* padfWeightsAligned, int nSrcPixelCount,
|
|
double& dfVal, double &dfWeightSum)
|
|
{
|
|
GDALResampleConvolutionHorizontalWithMaskSSE2(pChunk, pabyMask,
|
|
padfWeightsAligned,
|
|
nSrcPixelCount,
|
|
dfVal, dfWeightSum);
|
|
}
|
|
|
|
template<> inline void GDALResampleConvolutionHorizontalWithMask<GUInt16>(
|
|
const GUInt16* pChunk, const GByte* pabyMask,
|
|
const double* padfWeightsAligned, int nSrcPixelCount,
|
|
double& dfVal, double &dfWeightSum)
|
|
{
|
|
GDALResampleConvolutionHorizontalWithMaskSSE2(pChunk, pabyMask,
|
|
padfWeightsAligned,
|
|
nSrcPixelCount,
|
|
dfVal, dfWeightSum);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleConvolutionHorizontal_3rows_SSE2<T> */
|
|
/************************************************************************/
|
|
|
|
template<class T> static inline void GDALResampleConvolutionHorizontal_3rows_SSE2(
|
|
const T* pChunkRow1, const T* pChunkRow2, const T* pChunkRow3,
|
|
const double* padfWeightsAligned, int nSrcPixelCount,
|
|
double& dfRes1, double& dfRes2, double& dfRes3)
|
|
{
|
|
XMMReg4Double v_acc1 = XMMReg4Double::Zero(),
|
|
v_acc2 = XMMReg4Double::Zero(),
|
|
v_acc3 = XMMReg4Double::Zero();
|
|
int i = 0;
|
|
for(;i+7<nSrcPixelCount;i+=8)
|
|
{
|
|
// Retrieve the pixel & accumulate
|
|
XMMReg4Double v_pixels1 = XMMReg4Double::Load4Val(pChunkRow1+i);
|
|
XMMReg4Double v_pixels2 = XMMReg4Double::Load4Val(pChunkRow1+i+4);
|
|
XMMReg4Double v_weight1 = XMMReg4Double::Load4ValAligned(padfWeightsAligned+i);
|
|
XMMReg4Double v_weight2 = XMMReg4Double::Load4ValAligned(padfWeightsAligned+i+4);
|
|
|
|
v_acc1 += v_pixels1 * v_weight1;
|
|
v_acc1 += v_pixels2 * v_weight2;
|
|
|
|
v_pixels1 = XMMReg4Double::Load4Val(pChunkRow2+i);
|
|
v_pixels2 = XMMReg4Double::Load4Val(pChunkRow2+i+4);
|
|
v_acc2 += v_pixels1 * v_weight1;
|
|
v_acc2 += v_pixels2 * v_weight2;
|
|
|
|
v_pixels1 = XMMReg4Double::Load4Val(pChunkRow3+i);
|
|
v_pixels2 = XMMReg4Double::Load4Val(pChunkRow3+i+4);
|
|
v_acc3 += v_pixels1 * v_weight1;
|
|
v_acc3 += v_pixels2 * v_weight2;
|
|
|
|
}
|
|
|
|
v_acc1.AddLowAndHigh();
|
|
v_acc2.AddLowAndHigh();
|
|
v_acc3.AddLowAndHigh();
|
|
|
|
dfRes1 = (double)v_acc1.GetLow();
|
|
dfRes2 = (double)v_acc2.GetLow();
|
|
dfRes3 = (double)v_acc3.GetLow();
|
|
for(;i<nSrcPixelCount;i++)
|
|
{
|
|
dfRes1 += pChunkRow1[i] * padfWeightsAligned[i];
|
|
dfRes2 += pChunkRow2[i] * padfWeightsAligned[i];
|
|
dfRes3 += pChunkRow3[i] * padfWeightsAligned[i];
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleConvolutionHorizontal_3rows<GByte> */
|
|
/************************************************************************/
|
|
|
|
template<> inline void GDALResampleConvolutionHorizontal_3rows<GByte>(
|
|
const GByte* pChunkRow1, const GByte* pChunkRow2, const GByte* pChunkRow3,
|
|
const double* padfWeightsAligned, int nSrcPixelCount,
|
|
double& dfRes1, double& dfRes2, double& dfRes3)
|
|
{
|
|
GDALResampleConvolutionHorizontal_3rows_SSE2(pChunkRow1, pChunkRow2, pChunkRow3,
|
|
padfWeightsAligned, nSrcPixelCount,
|
|
dfRes1, dfRes2, dfRes3);
|
|
}
|
|
|
|
template<> inline void GDALResampleConvolutionHorizontal_3rows<GUInt16>(
|
|
const GUInt16* pChunkRow1, const GUInt16* pChunkRow2, const GUInt16* pChunkRow3,
|
|
const double* padfWeightsAligned, int nSrcPixelCount,
|
|
double& dfRes1, double& dfRes2, double& dfRes3)
|
|
{
|
|
GDALResampleConvolutionHorizontal_3rows_SSE2(pChunkRow1, pChunkRow2, pChunkRow3,
|
|
padfWeightsAligned, nSrcPixelCount,
|
|
dfRes1, dfRes2, dfRes3);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2<T> */
|
|
/************************************************************************/
|
|
|
|
template<class T> static inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2(
|
|
const T* pChunkRow1, const T* pChunkRow2, const T* pChunkRow3,
|
|
const double* padfWeightsAligned, int nSrcPixelCount,
|
|
double& dfRes1, double& dfRes2, double& dfRes3)
|
|
{
|
|
XMMReg4Double v_acc1 = XMMReg4Double::Zero(),
|
|
v_acc2 = XMMReg4Double::Zero(),
|
|
v_acc3 = XMMReg4Double::Zero();
|
|
int i = 0;
|
|
for(;i+3<nSrcPixelCount;i+=4)
|
|
{
|
|
// Retrieve the pixel & accumulate
|
|
XMMReg4Double v_pixels1 = XMMReg4Double::Load4Val(pChunkRow1+i);
|
|
XMMReg4Double v_pixels2 = XMMReg4Double::Load4Val(pChunkRow2+i);
|
|
XMMReg4Double v_pixels3 = XMMReg4Double::Load4Val(pChunkRow3+i);
|
|
XMMReg4Double v_weight = XMMReg4Double::Load4ValAligned(padfWeightsAligned+i);
|
|
|
|
v_acc1 += v_pixels1 * v_weight;
|
|
v_acc2 += v_pixels2 * v_weight;
|
|
v_acc3 += v_pixels3 * v_weight;
|
|
}
|
|
|
|
v_acc1.AddLowAndHigh();
|
|
v_acc2.AddLowAndHigh();
|
|
v_acc3.AddLowAndHigh();
|
|
|
|
dfRes1 = (double)v_acc1.GetLow();
|
|
dfRes2 = (double)v_acc2.GetLow();
|
|
dfRes3 = (double)v_acc3.GetLow();
|
|
for(;i<nSrcPixelCount;i++)
|
|
{
|
|
dfRes1 += pChunkRow1[i] * padfWeightsAligned[i];
|
|
dfRes2 += pChunkRow2[i] * padfWeightsAligned[i];
|
|
dfRes3 += pChunkRow3[i] * padfWeightsAligned[i];
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleConvolutionHorizontalPixelCountLess8_3rows<GByte> */
|
|
/************************************************************************/
|
|
|
|
template<> inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows<GByte>(
|
|
const GByte* pChunkRow1, const GByte* pChunkRow2, const GByte* pChunkRow3,
|
|
const double* padfWeightsAligned, int nSrcPixelCount,
|
|
double& dfRes1, double& dfRes2, double& dfRes3)
|
|
{
|
|
GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2(pChunkRow1, pChunkRow2, pChunkRow3,
|
|
padfWeightsAligned, nSrcPixelCount,
|
|
dfRes1, dfRes2, dfRes3);
|
|
}
|
|
|
|
template<> inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows<GUInt16>(
|
|
const GUInt16* pChunkRow1, const GUInt16* pChunkRow2, const GUInt16* pChunkRow3,
|
|
const double* padfWeightsAligned, int nSrcPixelCount,
|
|
double& dfRes1, double& dfRes2, double& dfRes3)
|
|
{
|
|
GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2(pChunkRow1, pChunkRow2, pChunkRow3,
|
|
padfWeightsAligned, nSrcPixelCount,
|
|
dfRes1, dfRes2, dfRes3);
|
|
}
|
|
|
|
#endif /* USE_SSE2 */
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleChunk32R_Convolution() */
|
|
/************************************************************************/
|
|
|
|
template<class T> static CPLErr
|
|
GDALResampleChunk32R_ConvolutionT( double dfXRatioDstToSrc, double dfYRatioDstToSrc,
|
|
double dfSrcXDelta,
|
|
double dfSrcYDelta,
|
|
T * pChunk,
|
|
GByte * pabyChunkNodataMask,
|
|
int nChunkXOff, int nChunkXSize,
|
|
int nChunkYOff, int nChunkYSize,
|
|
int nDstXOff, int nDstXOff2,
|
|
int nDstYOff, int nDstYOff2,
|
|
GDALRasterBand * poOverview,
|
|
int bHasNoData,
|
|
float fNoDataValue,
|
|
FilterFuncType pfnFilterFunc,
|
|
FilterFunc4ValuesType pfnFilterFunc4Values,
|
|
int nKernelRadius )
|
|
|
|
{
|
|
|
|
CPLErr eErr = CE_None;
|
|
|
|
if (!bHasNoData)
|
|
fNoDataValue = 0.0f;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Allocate work buffers. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nDstXSize = nDstXOff2 - nDstXOff;
|
|
|
|
double dfXScale = 1.0 / dfXRatioDstToSrc;
|
|
double dfXScaleWeight = ( dfXScale >= 1.0 ) ? 1.0 : dfXScale;
|
|
double dfXScaledRadius = nKernelRadius / dfXScaleWeight;
|
|
double dfYScale = 1.0 / dfYRatioDstToSrc;
|
|
double dfYScaleWeight = ( dfYScale >= 1.0 ) ? 1.0 : dfYScale;
|
|
double dfYScaledRadius = nKernelRadius / dfYScaleWeight;
|
|
|
|
float* pafDstScanline = (float *) VSIMalloc(nDstXSize * sizeof(float));
|
|
|
|
/* Temporary array to store result of horizontal filter */
|
|
double* padfHorizontalFiltered = (double*) VSIMalloc(nChunkYSize * nDstXSize * sizeof(double));
|
|
|
|
/* To store convolution coefficients */
|
|
double* padfWeightsAlloc = (double*) CPLMalloc((int)(
|
|
2 + 2 * MAX(dfXScaledRadius, dfYScaledRadius) + 0.5 + 1 /* for alignment*/) * sizeof(double));
|
|
|
|
GByte* pabyChunkNodataMaskHorizontalFiltered = NULL;
|
|
if( pabyChunkNodataMask )
|
|
pabyChunkNodataMaskHorizontalFiltered = (GByte*) VSIMalloc(nChunkYSize * nDstXSize);
|
|
if( pafDstScanline == NULL || padfHorizontalFiltered == NULL ||
|
|
padfWeightsAlloc == NULL || (pabyChunkNodataMask != NULL && pabyChunkNodataMaskHorizontalFiltered == NULL) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"GDALResampleChunk32R_ConvolutionT: Out of memory for work buffers." );
|
|
VSIFree(pafDstScanline);
|
|
VSIFree(padfHorizontalFiltered);
|
|
VSIFree(padfWeightsAlloc);
|
|
VSIFree(pabyChunkNodataMaskHorizontalFiltered);
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* Make sure we are aligned on 16 bits */
|
|
double* padfWeights = padfWeightsAlloc;
|
|
if( (((size_t)padfWeights) % 16) != 0 )
|
|
padfWeights ++;
|
|
|
|
/* ==================================================================== */
|
|
/* Fist pass: horizontal filter */
|
|
/* ==================================================================== */
|
|
int nChunkRightXOff = nChunkXOff + nChunkXSize;
|
|
#ifdef USE_SSE2
|
|
int bSrcPixelCountLess8 = dfXScaledRadius < 4;
|
|
#endif
|
|
for( int iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ )
|
|
{
|
|
double dfSrcPixel = (iDstPixel+0.5)*dfXRatioDstToSrc + dfSrcXDelta;
|
|
int nSrcPixelStart = (int)floor(dfSrcPixel - dfXScaledRadius + 0.5);
|
|
int nSrcPixelStop = (int)(dfSrcPixel + dfXScaledRadius + 0.5);
|
|
if( nSrcPixelStart < nChunkXOff )
|
|
nSrcPixelStart = nChunkXOff;
|
|
if( nSrcPixelStop > nChunkRightXOff )
|
|
nSrcPixelStop = nChunkRightXOff;
|
|
#if 0
|
|
if( nSrcPixelStart < nChunkXOff && nChunkXOff > 0 )
|
|
{
|
|
printf("truncated iDstPixel = %d\n", iDstPixel);
|
|
}
|
|
if( nSrcPixelStop > nChunkRightXOff && nChunkRightXOff < nSrcWidth )
|
|
{
|
|
printf("truncated iDstPixel = %d\n", iDstPixel);
|
|
}
|
|
#endif
|
|
int nSrcPixelCount = nSrcPixelStop - nSrcPixelStart;
|
|
double dfWeightSum = 0.0;
|
|
|
|
/* Compute convolution coefficients */
|
|
int nSrcPixel = nSrcPixelStart;
|
|
double dfX = dfXScaleWeight * (nSrcPixel - dfSrcPixel + 0.5);
|
|
for( ; nSrcPixel + 3 < nSrcPixelStop; nSrcPixel+=4)
|
|
{
|
|
padfWeights[nSrcPixel - nSrcPixelStart] = dfX;
|
|
dfX += dfXScaleWeight;
|
|
padfWeights[nSrcPixel+1 - nSrcPixelStart] = dfX;
|
|
dfX += dfXScaleWeight;
|
|
padfWeights[nSrcPixel+2 - nSrcPixelStart] = dfX;
|
|
dfX += dfXScaleWeight;
|
|
padfWeights[nSrcPixel+3 - nSrcPixelStart] = dfX;
|
|
dfX += dfXScaleWeight;
|
|
dfWeightSum += pfnFilterFunc4Values(padfWeights + nSrcPixel - nSrcPixelStart);
|
|
}
|
|
for( ; nSrcPixel < nSrcPixelStop; nSrcPixel++, dfX += dfXScaleWeight)
|
|
{
|
|
double dfWeight = pfnFilterFunc(dfX);
|
|
padfWeights[nSrcPixel - nSrcPixelStart] = dfWeight;
|
|
dfWeightSum += dfWeight;
|
|
}
|
|
|
|
if( pabyChunkNodataMask == NULL )
|
|
{
|
|
if( dfWeightSum != 0 )
|
|
{
|
|
double dfInvWeightSum = 1.0 / dfWeightSum;
|
|
for(int i=0;i<nSrcPixelCount;i++)
|
|
padfWeights[i] *= dfInvWeightSum;
|
|
}
|
|
|
|
int iSrcLineOff = 0;
|
|
#ifdef USE_SSE2
|
|
if( bSrcPixelCountLess8 )
|
|
{
|
|
for( ; iSrcLineOff+2 < nChunkYSize; iSrcLineOff +=3 )
|
|
{
|
|
int j=iSrcLineOff * nChunkXSize + (nSrcPixelStart - nChunkXOff);
|
|
double dfVal1, dfVal2, dfVal3;
|
|
GDALResampleConvolutionHorizontalPixelCountLess8_3rows(
|
|
pChunk + j, pChunk + j + nChunkXSize, pChunk + j + 2 * nChunkXSize,
|
|
padfWeights, nSrcPixelCount, dfVal1, dfVal2, dfVal3);
|
|
padfHorizontalFiltered[iSrcLineOff * nDstXSize + iDstPixel - nDstXOff] = dfVal1;
|
|
padfHorizontalFiltered[(iSrcLineOff+1) * nDstXSize + iDstPixel - nDstXOff] = dfVal2;
|
|
padfHorizontalFiltered[(iSrcLineOff+2) * nDstXSize + iDstPixel - nDstXOff] = dfVal3;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
for( ; iSrcLineOff+2 < nChunkYSize; iSrcLineOff +=3 )
|
|
{
|
|
int j=iSrcLineOff * nChunkXSize + (nSrcPixelStart - nChunkXOff);
|
|
double dfVal1, dfVal2, dfVal3;
|
|
GDALResampleConvolutionHorizontal_3rows(
|
|
pChunk + j, pChunk + j + nChunkXSize, pChunk + j + 2 * nChunkXSize,
|
|
padfWeights, nSrcPixelCount, dfVal1, dfVal2, dfVal3);
|
|
padfHorizontalFiltered[iSrcLineOff * nDstXSize + iDstPixel - nDstXOff] = dfVal1;
|
|
padfHorizontalFiltered[(iSrcLineOff+1) * nDstXSize + iDstPixel - nDstXOff] = dfVal2;
|
|
padfHorizontalFiltered[(iSrcLineOff+2) * nDstXSize + iDstPixel - nDstXOff] = dfVal3;
|
|
}
|
|
}
|
|
for( ; iSrcLineOff < nChunkYSize; iSrcLineOff ++ )
|
|
{
|
|
int j=iSrcLineOff * nChunkXSize + (nSrcPixelStart - nChunkXOff);
|
|
double dfVal =
|
|
GDALResampleConvolutionHorizontal(pChunk + j,
|
|
padfWeights, nSrcPixelCount);
|
|
padfHorizontalFiltered[iSrcLineOff * nDstXSize + iDstPixel - nDstXOff] = dfVal;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( int iSrcLineOff = 0; iSrcLineOff < nChunkYSize; iSrcLineOff ++ )
|
|
{
|
|
double dfVal;
|
|
int j=iSrcLineOff * nChunkXSize + (nSrcPixelStart - nChunkXOff);
|
|
GDALResampleConvolutionHorizontalWithMask(pChunk + j, pabyChunkNodataMask + j,
|
|
padfWeights, nSrcPixelCount,
|
|
dfVal, dfWeightSum);
|
|
int nTempOffset = iSrcLineOff * nDstXSize + iDstPixel - nDstXOff;
|
|
if( dfWeightSum > 0.0 )
|
|
{
|
|
padfHorizontalFiltered[nTempOffset] = dfVal / dfWeightSum;
|
|
pabyChunkNodataMaskHorizontalFiltered[nTempOffset] = 1;
|
|
}
|
|
else
|
|
{
|
|
padfHorizontalFiltered[nTempOffset] = 0.0;
|
|
pabyChunkNodataMaskHorizontalFiltered[nTempOffset] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* Second pass: vertical filter */
|
|
/* ==================================================================== */
|
|
int nChunkBottomYOff = nChunkYOff + nChunkYSize;
|
|
for( int iDstLine = nDstYOff; iDstLine < nDstYOff2; iDstLine++ )
|
|
{
|
|
double dfSrcLine = (iDstLine+0.5)*dfYRatioDstToSrc + dfSrcYDelta;
|
|
int nSrcLineStart = (int)floor(dfSrcLine - dfYScaledRadius + 0.5);
|
|
int nSrcLineStop = (int)(dfSrcLine + dfYScaledRadius + 0.5);
|
|
if( nSrcLineStart < nChunkYOff )
|
|
nSrcLineStart = nChunkYOff;
|
|
if( nSrcLineStop > nChunkBottomYOff )
|
|
nSrcLineStop = nChunkBottomYOff;
|
|
#if 0
|
|
if( nSrcLineStart < nChunkYOff &&
|
|
nChunkYOff > 0 )
|
|
{
|
|
printf("truncated iDstLine = %d\n", iDstLine);
|
|
}
|
|
if( nSrcLineStop > nChunkBottomYOff && nChunkBottomYOff < nSrcHeight )
|
|
{
|
|
printf("truncated iDstLine = %d\n", iDstLine);
|
|
}
|
|
#endif
|
|
int nSrcLineCount = nSrcLineStop - nSrcLineStart;
|
|
double dfWeightSum = 0.0;
|
|
|
|
/* Compute convolution coefficients */
|
|
int nSrcLine = nSrcLineStart;
|
|
double dfY = dfYScaleWeight * (nSrcLine - dfSrcLine + 0.5);
|
|
for( ; nSrcLine + 3 < nSrcLineStop; nSrcLine+=4, dfY += 4 * dfYScaleWeight)
|
|
{
|
|
padfWeights[nSrcLine - nSrcLineStart] = dfY;
|
|
padfWeights[nSrcLine+1 - nSrcLineStart] = dfY + dfYScaleWeight;
|
|
padfWeights[nSrcLine+2 - nSrcLineStart] = dfY + 2 * dfYScaleWeight;
|
|
padfWeights[nSrcLine+3 - nSrcLineStart] = dfY + 3 * dfYScaleWeight;
|
|
dfWeightSum += pfnFilterFunc4Values(padfWeights + nSrcLine - nSrcLineStart);
|
|
}
|
|
for( ; nSrcLine < nSrcLineStop; nSrcLine++, dfY += dfYScaleWeight)
|
|
{
|
|
double dfWeight = pfnFilterFunc(dfY);
|
|
padfWeights[nSrcLine - nSrcLineStart] = dfWeight;
|
|
dfWeightSum += dfWeight;
|
|
}
|
|
|
|
if( pabyChunkNodataMask == NULL )
|
|
{
|
|
if( dfWeightSum != 0 )
|
|
{
|
|
double dfInvWeightSum = 1.0 / dfWeightSum;
|
|
for(int i=0;i<nSrcLineCount;i++)
|
|
padfWeights[i] *= dfInvWeightSum;
|
|
}
|
|
|
|
int iFilteredPixelOff = 0;
|
|
int j=(nSrcLineStart - nChunkYOff) * nDstXSize;
|
|
for( ; iFilteredPixelOff+1 < nDstXSize; iFilteredPixelOff += 2, j+=2 )
|
|
{
|
|
double dfVal1, dfVal2;
|
|
GDALResampleConvolutionVertical_2cols(
|
|
padfHorizontalFiltered + j, nDstXSize, padfWeights, nSrcLineCount, dfVal1, dfVal2);
|
|
pafDstScanline[iFilteredPixelOff] = (float)dfVal1;
|
|
pafDstScanline[iFilteredPixelOff+1] = (float)dfVal2;
|
|
}
|
|
if( iFilteredPixelOff < nDstXSize )
|
|
{
|
|
double dfVal = GDALResampleConvolutionVertical(
|
|
padfHorizontalFiltered + j, nDstXSize, padfWeights, nSrcLineCount);
|
|
pafDstScanline[iFilteredPixelOff] = (float)dfVal;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( int iFilteredPixelOff = 0; iFilteredPixelOff < nDstXSize; iFilteredPixelOff ++ )
|
|
{
|
|
double dfVal = 0.0;
|
|
dfWeightSum = 0.0;
|
|
for(int i=0, j=(nSrcLineStart - nChunkYOff) * nDstXSize + iFilteredPixelOff;
|
|
i<nSrcLineCount; i++, j+=nDstXSize)
|
|
{
|
|
double dfWeight = padfWeights[i] * pabyChunkNodataMaskHorizontalFiltered[j];
|
|
dfVal += padfHorizontalFiltered[j] * dfWeight;
|
|
dfWeightSum += dfWeight;
|
|
}
|
|
if( dfWeightSum > 0.0 )
|
|
{
|
|
pafDstScanline[iFilteredPixelOff] = (float)(dfVal / dfWeightSum);
|
|
}
|
|
else
|
|
{
|
|
pafDstScanline[iFilteredPixelOff] = fNoDataValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXSize, 1,
|
|
pafDstScanline, nDstXSize, 1, GDT_Float32,
|
|
0, 0, NULL );
|
|
}
|
|
|
|
VSIFree( padfWeightsAlloc );
|
|
VSIFree( padfHorizontalFiltered );
|
|
VSIFree( pafDstScanline );
|
|
VSIFree(pabyChunkNodataMaskHorizontalFiltered);
|
|
|
|
return eErr;
|
|
}
|
|
|
|
static CPLErr GDALResampleChunk32R_Convolution(
|
|
double dfXRatioDstToSrc, double dfYRatioDstToSrc,
|
|
double dfSrcXDelta,
|
|
double dfSrcYDelta,
|
|
GDALDataType eWrkDataType,
|
|
void * pChunk,
|
|
GByte * pabyChunkNodataMask,
|
|
int nChunkXOff, int nChunkXSize,
|
|
int nChunkYOff, int nChunkYSize,
|
|
int nDstXOff, int nDstXOff2,
|
|
int nDstYOff, int nDstYOff2,
|
|
GDALRasterBand * poOverview,
|
|
const char * pszResampling,
|
|
int bHasNoData, float fNoDataValue,
|
|
CPL_UNUSED GDALColorTable* poColorTable_unused,
|
|
CPL_UNUSED GDALDataType eSrcDataType)
|
|
{
|
|
GDALResampleAlg eResample;
|
|
if( EQUAL(pszResampling, "BILINEAR") )
|
|
eResample = GRA_Bilinear;
|
|
else if( EQUAL(pszResampling, "CUBIC") )
|
|
eResample = GRA_Cubic;
|
|
else if( EQUAL(pszResampling, "CUBICSPLINE") )
|
|
eResample = GRA_CubicSpline;
|
|
else if( EQUAL(pszResampling, "LANCZOS") )
|
|
eResample = GRA_Lanczos;
|
|
else
|
|
{
|
|
CPLAssert(0);
|
|
return CE_Failure;
|
|
}
|
|
int nKernelRadius = GWKGetFilterRadius(eResample);
|
|
FilterFuncType pfnFilterFunc = GWKGetFilterFunc(eResample);
|
|
FilterFunc4ValuesType pfnFilterFunc4Values = GWKGetFilterFunc4Values(eResample);
|
|
|
|
if (eWrkDataType == GDT_Byte)
|
|
return GDALResampleChunk32R_ConvolutionT(dfXRatioDstToSrc, dfYRatioDstToSrc,
|
|
dfSrcXDelta, dfSrcYDelta,
|
|
(GByte *) pChunk, pabyChunkNodataMask,
|
|
nChunkXOff, nChunkXSize,
|
|
nChunkYOff, nChunkYSize,
|
|
nDstXOff, nDstXOff2,
|
|
nDstYOff, nDstYOff2,
|
|
poOverview,
|
|
bHasNoData, fNoDataValue,
|
|
pfnFilterFunc,
|
|
pfnFilterFunc4Values,
|
|
nKernelRadius);
|
|
else if (eWrkDataType == GDT_UInt16)
|
|
return GDALResampleChunk32R_ConvolutionT(dfXRatioDstToSrc, dfYRatioDstToSrc,
|
|
dfSrcXDelta, dfSrcYDelta,
|
|
(GUInt16 *) pChunk, pabyChunkNodataMask,
|
|
nChunkXOff, nChunkXSize,
|
|
nChunkYOff, nChunkYSize,
|
|
nDstXOff, nDstXOff2,
|
|
nDstYOff, nDstYOff2,
|
|
poOverview,
|
|
bHasNoData, fNoDataValue,
|
|
pfnFilterFunc,
|
|
pfnFilterFunc4Values,
|
|
nKernelRadius);
|
|
else if (eWrkDataType == GDT_Float32)
|
|
return GDALResampleChunk32R_ConvolutionT(dfXRatioDstToSrc, dfYRatioDstToSrc,
|
|
dfSrcXDelta, dfSrcYDelta,
|
|
(float *) pChunk, pabyChunkNodataMask,
|
|
nChunkXOff, nChunkXSize,
|
|
nChunkYOff, nChunkYSize,
|
|
nDstXOff, nDstXOff2,
|
|
nDstYOff, nDstYOff2,
|
|
poOverview,
|
|
bHasNoData, fNoDataValue,
|
|
pfnFilterFunc,
|
|
pfnFilterFunc4Values,
|
|
nKernelRadius);
|
|
|
|
CPLAssert(0);
|
|
return CE_Failure;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALResampleChunkC32R() */
|
|
/************************************************************************/
|
|
|
|
static CPLErr
|
|
GDALResampleChunkC32R( int nSrcWidth, int nSrcHeight,
|
|
float * pafChunk, int nChunkYOff, int nChunkYSize,
|
|
int nDstYOff, int nDstYOff2,
|
|
GDALRasterBand * poOverview,
|
|
const char * pszResampling )
|
|
|
|
{
|
|
int nOXSize, nOYSize;
|
|
float *pafDstScanline;
|
|
CPLErr eErr = CE_None;
|
|
|
|
nOXSize = poOverview->GetXSize();
|
|
nOYSize = poOverview->GetYSize();
|
|
double dfXRatioDstToSrc = (double)nSrcWidth / nOXSize;
|
|
double dfYRatioDstToSrc = (double)nSrcHeight / nOYSize;
|
|
|
|
pafDstScanline = (float *) VSIMalloc(nOXSize * sizeof(float) * 2);
|
|
if( pafDstScanline == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"GDALResampleChunkC32R: Out of memory for line buffer." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* Loop over destination scanlines. */
|
|
/* ==================================================================== */
|
|
for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ )
|
|
{
|
|
float *pafSrcScanline;
|
|
int nSrcYOff, nSrcYOff2, iDstPixel;
|
|
|
|
nSrcYOff = (int) (0.5 + iDstLine * dfYRatioDstToSrc);
|
|
if( nSrcYOff < nChunkYOff )
|
|
nSrcYOff = nChunkYOff;
|
|
|
|
nSrcYOff2 = (int) (0.5 + (iDstLine+1) * dfYRatioDstToSrc);
|
|
if( nSrcYOff2 == nSrcYOff )
|
|
nSrcYOff2 ++;
|
|
|
|
if( nSrcYOff2 > nSrcHeight || iDstLine == nOYSize-1 )
|
|
{
|
|
if( nSrcYOff == nSrcHeight && nSrcHeight - 1 >= nChunkYOff )
|
|
nSrcYOff = nSrcHeight - 1;
|
|
nSrcYOff2 = nSrcHeight;
|
|
}
|
|
if( nSrcYOff2 > nChunkYOff + nChunkYSize )
|
|
nSrcYOff2 = nChunkYOff + nChunkYSize;
|
|
|
|
pafSrcScanline = pafChunk + ((nSrcYOff-nChunkYOff) * nSrcWidth) * 2;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Loop over destination pixels */
|
|
/* -------------------------------------------------------------------- */
|
|
for( iDstPixel = 0; iDstPixel < nOXSize; iDstPixel++ )
|
|
{
|
|
int nSrcXOff, nSrcXOff2;
|
|
|
|
nSrcXOff = (int) (0.5 + iDstPixel * dfXRatioDstToSrc);
|
|
nSrcXOff2 = (int)
|
|
(0.5 + (iDstPixel+1) * dfXRatioDstToSrc);
|
|
if( nSrcXOff2 == nSrcXOff )
|
|
nSrcXOff2 ++;
|
|
if( nSrcXOff2 > nSrcWidth || iDstPixel == nOXSize-1 )
|
|
{
|
|
if( nSrcXOff == nSrcWidth && nSrcWidth - 1 >= 0 )
|
|
nSrcXOff = nSrcWidth - 1;
|
|
nSrcXOff2 = nSrcWidth;
|
|
}
|
|
|
|
if( EQUALN(pszResampling,"NEAR",4) )
|
|
{
|
|
pafDstScanline[iDstPixel*2] = pafSrcScanline[nSrcXOff*2];
|
|
pafDstScanline[iDstPixel*2+1] = pafSrcScanline[nSrcXOff*2+1];
|
|
}
|
|
else if( EQUAL(pszResampling,"AVERAGE_MAGPHASE") )
|
|
{
|
|
double dfTotalR = 0.0, dfTotalI = 0.0, dfTotalM = 0.0;
|
|
int nCount = 0, iX, iY;
|
|
|
|
for( iY = nSrcYOff; iY < nSrcYOff2; iY++ )
|
|
{
|
|
for( iX = nSrcXOff; iX < nSrcXOff2; iX++ )
|
|
{
|
|
double dfR, dfI;
|
|
|
|
dfR = pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2];
|
|
dfI = pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2+1];
|
|
dfTotalR += dfR;
|
|
dfTotalI += dfI;
|
|
dfTotalM += sqrt( dfR*dfR + dfI*dfI );
|
|
nCount++;
|
|
}
|
|
}
|
|
|
|
CPLAssert( nCount > 0 );
|
|
if( nCount == 0 )
|
|
{
|
|
pafDstScanline[iDstPixel*2] = 0.0;
|
|
pafDstScanline[iDstPixel*2+1] = 0.0;
|
|
}
|
|
else
|
|
{
|
|
double dfM, dfDesiredM, dfRatio=1.0;
|
|
|
|
pafDstScanline[iDstPixel*2 ] = (float) (dfTotalR/nCount);
|
|
pafDstScanline[iDstPixel*2+1] = (float) (dfTotalI/nCount);
|
|
|
|
dfM = sqrt(pafDstScanline[iDstPixel*2 ]*pafDstScanline[iDstPixel*2 ]
|
|
+ pafDstScanline[iDstPixel*2+1]*pafDstScanline[iDstPixel*2+1]);
|
|
dfDesiredM = dfTotalM / nCount;
|
|
if( dfM != 0.0 )
|
|
dfRatio = dfDesiredM / dfM;
|
|
|
|
pafDstScanline[iDstPixel*2 ] *= (float) dfRatio;
|
|
pafDstScanline[iDstPixel*2+1] *= (float) dfRatio;
|
|
}
|
|
}
|
|
else if( EQUALN(pszResampling,"AVER",4) )
|
|
{
|
|
double dfTotalR = 0.0, dfTotalI = 0.0;
|
|
int nCount = 0, iX, iY;
|
|
|
|
for( iY = nSrcYOff; iY < nSrcYOff2; iY++ )
|
|
{
|
|
for( iX = nSrcXOff; iX < nSrcXOff2; iX++ )
|
|
{
|
|
dfTotalR += pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2];
|
|
dfTotalI += pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2+1];
|
|
nCount++;
|
|
}
|
|
}
|
|
|
|
CPLAssert( nCount > 0 );
|
|
if( nCount == 0 )
|
|
{
|
|
pafDstScanline[iDstPixel*2] = 0.0;
|
|
pafDstScanline[iDstPixel*2+1] = 0.0;
|
|
}
|
|
else
|
|
{
|
|
pafDstScanline[iDstPixel*2 ] = (float) (dfTotalR/nCount);
|
|
pafDstScanline[iDstPixel*2+1] = (float) (dfTotalI/nCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
eErr = poOverview->RasterIO( GF_Write, 0, iDstLine, nOXSize, 1,
|
|
pafDstScanline, nOXSize, 1, GDT_CFloat32,
|
|
0, 0, NULL );
|
|
}
|
|
|
|
CPLFree( pafDstScanline );
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRegenerateCascadingOverviews() */
|
|
/* */
|
|
/* Generate a list of overviews in order from largest to */
|
|
/* smallest, computing each from the next larger. */
|
|
/************************************************************************/
|
|
|
|
static CPLErr
|
|
GDALRegenerateCascadingOverviews(
|
|
GDALRasterBand *poSrcBand, int nOverviews, GDALRasterBand **papoOvrBands,
|
|
const char * pszResampling,
|
|
GDALProgressFunc pfnProgress, void * pProgressData )
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* First, we must put the overviews in order from largest to */
|
|
/* smallest. */
|
|
/* -------------------------------------------------------------------- */
|
|
int i, j;
|
|
|
|
for( i = 0; i < nOverviews-1; i++ )
|
|
{
|
|
for( j = 0; j < nOverviews - i - 1; j++ )
|
|
{
|
|
|
|
if( papoOvrBands[j]->GetXSize()
|
|
* (float) papoOvrBands[j]->GetYSize() <
|
|
papoOvrBands[j+1]->GetXSize()
|
|
* (float) papoOvrBands[j+1]->GetYSize() )
|
|
{
|
|
GDALRasterBand * poTempBand;
|
|
|
|
poTempBand = papoOvrBands[j];
|
|
papoOvrBands[j] = papoOvrBands[j+1];
|
|
papoOvrBands[j+1] = poTempBand;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Count total pixels so we can prepare appropriate scaled */
|
|
/* progress functions. */
|
|
/* -------------------------------------------------------------------- */
|
|
double dfTotalPixels = 0.0;
|
|
|
|
for( i = 0; i < nOverviews; i++ )
|
|
{
|
|
dfTotalPixels += papoOvrBands[i]->GetXSize()
|
|
* (double) papoOvrBands[i]->GetYSize();
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Generate all the bands. */
|
|
/* -------------------------------------------------------------------- */
|
|
double dfPixelsProcessed = 0.0;
|
|
|
|
for( i = 0; i < nOverviews; i++ )
|
|
{
|
|
void *pScaledProgressData;
|
|
double dfPixels;
|
|
GDALRasterBand *poBaseBand;
|
|
CPLErr eErr;
|
|
|
|
if( i == 0 )
|
|
poBaseBand = poSrcBand;
|
|
else
|
|
poBaseBand = papoOvrBands[i-1];
|
|
|
|
dfPixels = papoOvrBands[i]->GetXSize()
|
|
* (double) papoOvrBands[i]->GetYSize();
|
|
|
|
pScaledProgressData = GDALCreateScaledProgress(
|
|
dfPixelsProcessed / dfTotalPixels,
|
|
(dfPixelsProcessed + dfPixels) / dfTotalPixels,
|
|
pfnProgress, pProgressData );
|
|
|
|
eErr = GDALRegenerateOverviews( (GDALRasterBandH) poBaseBand,
|
|
1, (GDALRasterBandH *) papoOvrBands+i,
|
|
pszResampling,
|
|
GDALScaledProgress,
|
|
pScaledProgressData );
|
|
GDALDestroyScaledProgress( pScaledProgressData );
|
|
|
|
if( eErr != CE_None )
|
|
return eErr;
|
|
|
|
dfPixelsProcessed += dfPixels;
|
|
|
|
/* we only do the bit2grayscale promotion on the base band */
|
|
if( EQUALN(pszResampling,"AVERAGE_BIT2GRAYSCALE",13) )
|
|
pszResampling = "AVERAGE";
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGetResampleFunction() */
|
|
/************************************************************************/
|
|
|
|
GDALResampleFunction GDALGetResampleFunction(const char* pszResampling,
|
|
int* pnRadius)
|
|
{
|
|
if( pnRadius ) *pnRadius = 0;
|
|
if( EQUALN(pszResampling,"NEAR",4) )
|
|
return GDALResampleChunk32R_Near;
|
|
else if( EQUALN(pszResampling,"AVER",4) )
|
|
return GDALResampleChunk32R_Average;
|
|
else if( EQUALN(pszResampling,"GAUSS",5) )
|
|
{
|
|
if( pnRadius ) *pnRadius = 1;
|
|
return GDALResampleChunk32R_Gauss;
|
|
}
|
|
else if( EQUALN(pszResampling,"MODE",4) )
|
|
return GDALResampleChunk32R_Mode;
|
|
else if( EQUAL(pszResampling,"CUBIC") )
|
|
{
|
|
if( pnRadius ) *pnRadius = GWKGetFilterRadius(GRA_Cubic);
|
|
return GDALResampleChunk32R_Convolution;
|
|
}
|
|
else if( EQUAL(pszResampling,"CUBICSPLINE") )
|
|
{
|
|
if( pnRadius ) *pnRadius = GWKGetFilterRadius(GRA_CubicSpline);
|
|
return GDALResampleChunk32R_Convolution;
|
|
}
|
|
else if( EQUAL(pszResampling,"LANCZOS") )
|
|
{
|
|
if( pnRadius ) *pnRadius = GWKGetFilterRadius(GRA_Lanczos);
|
|
return GDALResampleChunk32R_Convolution;
|
|
}
|
|
else if( EQUAL(pszResampling,"BILINEAR") )
|
|
{
|
|
if( pnRadius ) *pnRadius = GWKGetFilterRadius(GRA_Bilinear);
|
|
return GDALResampleChunk32R_Convolution;
|
|
}
|
|
else
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"GDALGetResampleFunction: Unsupported resampling method \"%s\".",
|
|
pszResampling );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGetOvrWorkDataType() */
|
|
/************************************************************************/
|
|
|
|
GDALDataType GDALGetOvrWorkDataType(const char* pszResampling,
|
|
GDALDataType eSrcDataType)
|
|
{
|
|
if( (EQUALN(pszResampling,"NEAR",4) ||
|
|
EQUALN(pszResampling,"AVER",4) ||
|
|
EQUAL(pszResampling,"CUBIC") ||
|
|
EQUAL(pszResampling,"CUBICSPLINE") ||
|
|
EQUAL(pszResampling,"LANCZOS") ||
|
|
EQUAL(pszResampling,"BILINEAR")) &&
|
|
eSrcDataType == GDT_Byte)
|
|
return GDT_Byte;
|
|
else if( (EQUALN(pszResampling,"NEAR",4) ||
|
|
EQUALN(pszResampling,"AVER",4) ||
|
|
EQUAL(pszResampling,"CUBIC") ||
|
|
EQUAL(pszResampling,"CUBICSPLINE") ||
|
|
EQUAL(pszResampling,"LANCZOS") ||
|
|
EQUAL(pszResampling,"BILINEAR")) &&
|
|
eSrcDataType == GDT_UInt16)
|
|
return GDT_UInt16;
|
|
else
|
|
return GDT_Float32;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRegenerateOverviews() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Generate downsampled overviews.
|
|
*
|
|
* This function will generate one or more overview images from a base
|
|
* image using the requested downsampling algorithm. It's primary use
|
|
* is for generating overviews via GDALDataset::BuildOverviews(), but it
|
|
* can also be used to generate downsampled images in one file from another
|
|
* outside the overview architecture.
|
|
*
|
|
* The output bands need to exist in advance.
|
|
*
|
|
* The full set of resampling algorithms is documented in
|
|
* GDALDataset::BuildOverviews().
|
|
*
|
|
* This function will honour properly NODATA_VALUES tuples (special dataset metadata) so
|
|
* that only a given RGB triplet (in case of a RGB image) will be considered as the
|
|
* nodata value and not each value of the triplet independantly per band.
|
|
*
|
|
* @param hSrcBand the source (base level) band.
|
|
* @param nOverviewCount the number of downsampled bands being generated.
|
|
* @param pahOvrBands the list of downsampled bands to be generated.
|
|
* @param pszResampling Resampling algorithm (eg. "AVERAGE").
|
|
* @param pfnProgress progress report function.
|
|
* @param pProgressData progress function callback data.
|
|
* @return CE_None on success or CE_Failure on failure.
|
|
*/
|
|
CPLErr
|
|
GDALRegenerateOverviews( GDALRasterBandH hSrcBand,
|
|
int nOverviewCount, GDALRasterBandH *pahOvrBands,
|
|
const char * pszResampling,
|
|
GDALProgressFunc pfnProgress, void * pProgressData )
|
|
|
|
{
|
|
GDALRasterBand *poSrcBand = (GDALRasterBand *) hSrcBand;
|
|
GDALRasterBand **papoOvrBands = (GDALRasterBand **) pahOvrBands;
|
|
int nFullResYChunk, nWidth, nHeight;
|
|
int nFRXBlockSize, nFRYBlockSize;
|
|
GDALDataType eType;
|
|
int bHasNoData;
|
|
float fNoDataValue;
|
|
GDALColorTable* poColorTable = NULL;
|
|
|
|
if( pfnProgress == NULL )
|
|
pfnProgress = GDALDummyProgress;
|
|
|
|
if( EQUAL(pszResampling,"NONE") )
|
|
return CE_None;
|
|
|
|
int nKernelRadius;
|
|
GDALResampleFunction pfnResampleFn = GDALGetResampleFunction(pszResampling,
|
|
&nKernelRadius);
|
|
if (pfnResampleFn == NULL)
|
|
return CE_Failure;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check color tables... */
|
|
/* -------------------------------------------------------------------- */
|
|
if ((EQUALN(pszResampling,"AVER",4)
|
|
|| EQUALN(pszResampling,"MODE",4)
|
|
|| EQUALN(pszResampling,"GAUSS",5)) &&
|
|
poSrcBand->GetColorInterpretation() == GCI_PaletteIndex)
|
|
{
|
|
poColorTable = poSrcBand->GetColorTable();
|
|
if (poColorTable != NULL)
|
|
{
|
|
if (poColorTable->GetPaletteInterpretation() != GPI_RGB)
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"Computing overviews on palette index raster bands "
|
|
"with a palette whose color interpreation is not RGB "
|
|
"will probably lead to unexpected results.");
|
|
poColorTable = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"Computing overviews on palette index raster bands "
|
|
"without a palette will probably lead to unexpected results.");
|
|
}
|
|
}
|
|
// Not ready yet
|
|
else if( (EQUAL(pszResampling,"CUBIC") ||
|
|
EQUAL(pszResampling,"CUBICSPLINE") ||
|
|
EQUAL(pszResampling,"LANCZOS") ||
|
|
EQUAL(pszResampling,"BILINEAR") )
|
|
&& poSrcBand->GetColorInterpretation() == GCI_PaletteIndex )
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"Computing %s overviews on palette index raster bands "
|
|
"will probably lead to unexpected results.", pszResampling);
|
|
}
|
|
|
|
|
|
/* If we have a nodata mask and we are doing something more complicated */
|
|
/* than nearest neighbouring, we have to fetch to nodata mask */
|
|
|
|
GDALRasterBand* poMaskBand = NULL;
|
|
int nMaskFlags = 0;
|
|
int bUseNoDataMask = FALSE;
|
|
|
|
if( !EQUALN(pszResampling,"NEAR",4) )
|
|
{
|
|
/* Special case if we are the alpha band. We want it to be considered */
|
|
/* as the mask band to avoid alpha=0 to be taken into account in average */
|
|
/* computation */
|
|
if( poSrcBand->GetColorInterpretation() == GCI_AlphaBand )
|
|
{
|
|
poMaskBand = poSrcBand;
|
|
nMaskFlags = GMF_ALPHA | GMF_PER_DATASET;
|
|
}
|
|
else
|
|
{
|
|
poMaskBand = poSrcBand->GetMaskBand();
|
|
nMaskFlags = poSrcBand->GetMaskFlags();
|
|
}
|
|
|
|
bUseNoDataMask = ((nMaskFlags & GMF_ALL_VALID) == 0);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we are operating on multiple overviews, and using */
|
|
/* averaging, lets do them in cascading order to reduce the */
|
|
/* amount of computation. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* In case the mask made be computed from another band of the dataset, */
|
|
/* we can't use cascaded generation, as the computation of the overviews */
|
|
/* of the band used for the mask band may not have yet occured (#3033) */
|
|
if( (EQUALN(pszResampling,"AVER",4) |
|
|
EQUALN(pszResampling,"GAUSS",5) ||
|
|
EQUAL(pszResampling,"CUBIC") ||
|
|
EQUAL(pszResampling,"CUBICSPLINE") ||
|
|
EQUAL(pszResampling,"LANCZOS") ||
|
|
EQUAL(pszResampling,"BILINEAR")) && nOverviewCount > 1
|
|
&& !(bUseNoDataMask && nMaskFlags != GMF_NODATA))
|
|
return GDALRegenerateCascadingOverviews( poSrcBand,
|
|
nOverviewCount, papoOvrBands,
|
|
pszResampling,
|
|
pfnProgress,
|
|
pProgressData );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup one horizontal swath to read from the raw buffer. */
|
|
/* -------------------------------------------------------------------- */
|
|
void *pChunk;
|
|
GByte *pabyChunkNodataMask = NULL;
|
|
|
|
poSrcBand->GetBlockSize( &nFRXBlockSize, &nFRYBlockSize );
|
|
|
|
if( nFRYBlockSize < 16 || nFRYBlockSize > 256 )
|
|
nFullResYChunk = 64;
|
|
else
|
|
nFullResYChunk = nFRYBlockSize;
|
|
|
|
if( GDALDataTypeIsComplex( poSrcBand->GetRasterDataType() ) )
|
|
eType = GDT_CFloat32;
|
|
else
|
|
eType = GDALGetOvrWorkDataType(pszResampling, poSrcBand->GetRasterDataType());
|
|
|
|
nWidth = poSrcBand->GetXSize();
|
|
nHeight = poSrcBand->GetYSize();
|
|
|
|
int nMaxOvrFactor = 1;
|
|
for( int iOverview = 0; iOverview < nOverviewCount; iOverview ++ )
|
|
{
|
|
int nDstWidth = papoOvrBands[iOverview]->GetXSize();
|
|
int nDstHeight = papoOvrBands[iOverview]->GetYSize();
|
|
nMaxOvrFactor = MAX( nMaxOvrFactor, (int)((double)nWidth / nDstWidth + 0.5) );
|
|
nMaxOvrFactor = MAX( nMaxOvrFactor, (int)((double)nHeight / nDstHeight + 0.5) );
|
|
}
|
|
int nMaxChunkYSizeQueried = nFullResYChunk + 2 * nKernelRadius * nMaxOvrFactor;
|
|
|
|
pChunk =
|
|
VSIMalloc3((GDALGetDataTypeSize(eType)/8), nMaxChunkYSizeQueried, nWidth );
|
|
if (bUseNoDataMask)
|
|
{
|
|
pabyChunkNodataMask = (GByte *)
|
|
(GByte*) VSIMalloc2( nMaxChunkYSizeQueried, nWidth );
|
|
}
|
|
|
|
if( pChunk == NULL || (bUseNoDataMask && pabyChunkNodataMask == NULL))
|
|
{
|
|
CPLFree(pChunk);
|
|
CPLFree(pabyChunkNodataMask);
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"Out of memory in GDALRegenerateOverviews()." );
|
|
|
|
return CE_Failure;
|
|
}
|
|
|
|
fNoDataValue = (float) poSrcBand->GetNoDataValue(&bHasNoData);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Loop over image operating on chunks. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nChunkYOff = 0;
|
|
CPLErr eErr = CE_None;
|
|
|
|
for( nChunkYOff = 0;
|
|
nChunkYOff < nHeight && eErr == CE_None;
|
|
nChunkYOff += nFullResYChunk )
|
|
{
|
|
if( !pfnProgress( nChunkYOff / (double) nHeight,
|
|
NULL, pProgressData ) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
|
|
eErr = CE_Failure;
|
|
}
|
|
|
|
if( nFullResYChunk + nChunkYOff > nHeight )
|
|
nFullResYChunk = nHeight - nChunkYOff;
|
|
|
|
int nChunkYOffQueried = nChunkYOff - nKernelRadius * nMaxOvrFactor;
|
|
int nChunkYSizeQueried = nFullResYChunk + 2 * nKernelRadius * nMaxOvrFactor;
|
|
if( nChunkYOffQueried < 0 )
|
|
{
|
|
nChunkYSizeQueried += nChunkYOffQueried;
|
|
nChunkYOffQueried = 0;
|
|
}
|
|
if( nChunkYOffQueried + nChunkYSizeQueried > nHeight )
|
|
nChunkYSizeQueried = nHeight - nChunkYOffQueried;
|
|
|
|
/* read chunk */
|
|
if (eErr == CE_None)
|
|
eErr = poSrcBand->RasterIO( GF_Read, 0, nChunkYOffQueried, nWidth, nChunkYSizeQueried,
|
|
pChunk, nWidth, nChunkYSizeQueried, eType,
|
|
0, 0, NULL );
|
|
if (eErr == CE_None && bUseNoDataMask)
|
|
eErr = poMaskBand->RasterIO( GF_Read, 0, nChunkYOffQueried, nWidth, nChunkYSizeQueried,
|
|
pabyChunkNodataMask, nWidth, nChunkYSizeQueried, GDT_Byte,
|
|
0, 0, NULL );
|
|
|
|
/* special case to promote 1bit data to 8bit 0/255 values */
|
|
if( EQUAL(pszResampling,"AVERAGE_BIT2GRAYSCALE") )
|
|
{
|
|
int i;
|
|
|
|
if (eType == GDT_Float32)
|
|
{
|
|
float* pafChunk = (float*)pChunk;
|
|
for( i = nChunkYSizeQueried*nWidth - 1; i >= 0; i-- )
|
|
{
|
|
if( pafChunk[i] == 1.0 )
|
|
pafChunk[i] = 255.0;
|
|
}
|
|
}
|
|
else if (eType == GDT_Byte)
|
|
{
|
|
GByte* pabyChunk = (GByte*)pChunk;
|
|
for( i = nChunkYSizeQueried*nWidth - 1; i >= 0; i-- )
|
|
{
|
|
if( pabyChunk[i] == 1 )
|
|
pabyChunk[i] = 255;
|
|
}
|
|
}
|
|
else if (eType == GDT_UInt16)
|
|
{
|
|
GUInt16* pasChunk = (GUInt16*)pChunk;
|
|
for( i = nChunkYSizeQueried*nWidth - 1; i >= 0; i-- )
|
|
{
|
|
if( pasChunk[i] == 1 )
|
|
pasChunk[i] = 255;
|
|
}
|
|
}
|
|
else {
|
|
CPLAssert(0);
|
|
}
|
|
}
|
|
else if( EQUAL(pszResampling,"AVERAGE_BIT2GRAYSCALE_MINISWHITE") )
|
|
{
|
|
int i;
|
|
|
|
if (eType == GDT_Float32)
|
|
{
|
|
float* pafChunk = (float*)pChunk;
|
|
for( i = nChunkYSizeQueried*nWidth - 1; i >= 0; i-- )
|
|
{
|
|
if( pafChunk[i] == 1.0 )
|
|
pafChunk[i] = 0.0;
|
|
else if( pafChunk[i] == 0.0 )
|
|
pafChunk[i] = 255.0;
|
|
}
|
|
}
|
|
else if (eType == GDT_Byte)
|
|
{
|
|
GByte* pabyChunk = (GByte*)pChunk;
|
|
for( i = nChunkYSizeQueried*nWidth - 1; i >= 0; i-- )
|
|
{
|
|
if( pabyChunk[i] == 1 )
|
|
pabyChunk[i] = 0;
|
|
else if( pabyChunk[i] == 0 )
|
|
pabyChunk[i] = 255;
|
|
}
|
|
}
|
|
else if (eType == GDT_UInt16)
|
|
{
|
|
GUInt16* pasChunk = (GUInt16*)pChunk;
|
|
for( i = nChunkYSizeQueried*nWidth - 1; i >= 0; i-- )
|
|
{
|
|
if( pasChunk[i] == 1 )
|
|
pasChunk[i] = 0;
|
|
else if( pasChunk[i] == 0 )
|
|
pasChunk[i] = 255;
|
|
}
|
|
}
|
|
else {
|
|
CPLAssert(0);
|
|
}
|
|
}
|
|
|
|
for( int iOverview = 0; iOverview < nOverviewCount && eErr == CE_None; iOverview++ )
|
|
{
|
|
int nDstWidth = papoOvrBands[iOverview]->GetXSize();
|
|
int nDstHeight = papoOvrBands[iOverview]->GetYSize();
|
|
|
|
double dfXRatioDstToSrc = (double)nWidth / nDstWidth;
|
|
double dfYRatioDstToSrc = (double)nHeight / nDstHeight;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Figure out the line to start writing to, and the first line */
|
|
/* to not write to. In theory this approach should ensure that */
|
|
/* every output line will be written if all input chunks are */
|
|
/* processed. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nDstYOff = (int) (0.5 + nChunkYOff/dfYRatioDstToSrc);
|
|
int nDstYOff2 = (int)
|
|
(0.5 + (nChunkYOff+nFullResYChunk)/dfYRatioDstToSrc);
|
|
|
|
if( nChunkYOff + nFullResYChunk == nHeight )
|
|
nDstYOff2 = nDstHeight;
|
|
//CPLDebug("GDAL", "nDstYOff=%d, nDstYOff2=%d", nDstYOff, nDstYOff2);
|
|
|
|
if( eType == GDT_Byte || eType == GDT_UInt16 || eType == GDT_Float32 )
|
|
eErr = pfnResampleFn(dfXRatioDstToSrc, dfYRatioDstToSrc,
|
|
0.0, 0.0,
|
|
eType,
|
|
pChunk,
|
|
pabyChunkNodataMask,
|
|
0, nWidth,
|
|
nChunkYOffQueried, nChunkYSizeQueried,
|
|
0, nDstWidth,
|
|
nDstYOff, nDstYOff2,
|
|
papoOvrBands[iOverview], pszResampling,
|
|
bHasNoData, fNoDataValue, poColorTable,
|
|
poSrcBand->GetRasterDataType());
|
|
else
|
|
eErr = GDALResampleChunkC32R(nWidth, nHeight,
|
|
(float*)pChunk,
|
|
nChunkYOffQueried, nChunkYSizeQueried,
|
|
nDstYOff, nDstYOff2,
|
|
papoOvrBands[iOverview], pszResampling);
|
|
}
|
|
}
|
|
|
|
VSIFree( pChunk );
|
|
VSIFree( pabyChunkNodataMask );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Renormalized overview mean / stddev if needed. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( eErr == CE_None && EQUAL(pszResampling,"AVERAGE_MP") )
|
|
{
|
|
GDALOverviewMagnitudeCorrection( (GDALRasterBandH) poSrcBand,
|
|
nOverviewCount,
|
|
(GDALRasterBandH *) papoOvrBands,
|
|
GDALDummyProgress, NULL );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* It can be important to flush out data to overviews. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( int iOverview = 0;
|
|
eErr == CE_None && iOverview < nOverviewCount;
|
|
iOverview++ )
|
|
{
|
|
eErr = papoOvrBands[iOverview]->FlushCache();
|
|
}
|
|
|
|
if (eErr == CE_None)
|
|
pfnProgress( 1.0, NULL, pProgressData );
|
|
|
|
return eErr;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* GDALRegenerateOverviewsMultiBand() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Variant of GDALRegenerateOverviews, specialy dedicated for generating
|
|
* compressed pixel-interleaved overviews (JPEG-IN-TIFF for example)
|
|
*
|
|
* This function will generate one or more overview images from a base
|
|
* image using the requested downsampling algorithm. It's primary use
|
|
* is for generating overviews via GDALDataset::BuildOverviews(), but it
|
|
* can also be used to generate downsampled images in one file from another
|
|
* outside the overview architecture.
|
|
*
|
|
* The output bands need to exist in advance and share the same characteristics
|
|
* (type, dimensions)
|
|
*
|
|
* The resampling algorithms supported for the moment are "NEAREST", "AVERAGE"
|
|
* and "GAUSS"
|
|
*
|
|
* The pseudo-algorithm used by the function is :
|
|
* for each overview
|
|
* iterate on lines of the source by a step of deltay
|
|
* iterate on columns of the source by a step of deltax
|
|
* read the source data of size deltax * deltay for all the bands
|
|
* generate the corresponding overview block for all the bands
|
|
*
|
|
* This function will honour properly NODATA_VALUES tuples (special dataset metadata) so
|
|
* that only a given RGB triplet (in case of a RGB image) will be considered as the
|
|
* nodata value and not each value of the triplet independantly per band.
|
|
*
|
|
* @param nBands the number of bands, size of papoSrcBands and size of
|
|
* first dimension of papapoOverviewBands
|
|
* @param papoSrcBands the list of source bands to downsample
|
|
* @param nOverviews the number of downsampled overview levels being generated.
|
|
* @param papapoOverviewBands bidimension array of bands. First dimension is indexed
|
|
* by nBands. Second dimension is indexed by nOverviews.
|
|
* @param pszResampling Resampling algorithm ("NEAREST", "AVERAGE" or "GAUSS").
|
|
* @param pfnProgress progress report function.
|
|
* @param pProgressData progress function callback data.
|
|
* @return CE_None on success or CE_Failure on failure.
|
|
*/
|
|
|
|
CPLErr
|
|
GDALRegenerateOverviewsMultiBand(int nBands, GDALRasterBand** papoSrcBands,
|
|
int nOverviews,
|
|
GDALRasterBand*** papapoOverviewBands,
|
|
const char * pszResampling,
|
|
GDALProgressFunc pfnProgress, void * pProgressData )
|
|
{
|
|
CPLErr eErr = CE_None;
|
|
int iOverview, iBand;
|
|
|
|
if( pfnProgress == NULL )
|
|
pfnProgress = GDALDummyProgress;
|
|
|
|
if( EQUAL(pszResampling,"NONE") )
|
|
return CE_None;
|
|
|
|
/* Sanity checks */
|
|
if (!EQUALN(pszResampling, "NEAR", 4) &&
|
|
!EQUAL(pszResampling, "AVERAGE") &&
|
|
!EQUAL(pszResampling, "GAUSS") &&
|
|
!EQUAL(pszResampling, "CUBIC") &&
|
|
!EQUAL(pszResampling,"CUBICSPLINE") &&
|
|
!EQUAL(pszResampling,"LANCZOS") &&
|
|
!EQUAL(pszResampling,"BILINEAR"))
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"GDALRegenerateOverviewsMultiBand: pszResampling='%s' not supported", pszResampling);
|
|
return CE_Failure;
|
|
}
|
|
|
|
int nKernelRadius;
|
|
GDALResampleFunction pfnResampleFn = GDALGetResampleFunction(pszResampling,
|
|
&nKernelRadius);
|
|
if (pfnResampleFn == NULL)
|
|
return CE_Failure;
|
|
|
|
int nSrcWidth = papoSrcBands[0]->GetXSize();
|
|
int nSrcHeight = papoSrcBands[0]->GetYSize();
|
|
GDALDataType eDataType = papoSrcBands[0]->GetRasterDataType();
|
|
for(iBand=1;iBand<nBands;iBand++)
|
|
{
|
|
if (papoSrcBands[iBand]->GetXSize() != nSrcWidth ||
|
|
papoSrcBands[iBand]->GetYSize() != nSrcHeight)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"GDALRegenerateOverviewsMultiBand: all the source bands must have the same dimensions");
|
|
return CE_Failure;
|
|
}
|
|
if (papoSrcBands[iBand]->GetRasterDataType() != eDataType)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"GDALRegenerateOverviewsMultiBand: all the source bands must have the same data type");
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
for(iOverview=0;iOverview<nOverviews;iOverview++)
|
|
{
|
|
int nDstWidth = papapoOverviewBands[0][iOverview]->GetXSize();
|
|
int nDstHeight = papapoOverviewBands[0][iOverview]->GetYSize();
|
|
for(iBand=1;iBand<nBands;iBand++)
|
|
{
|
|
if (papapoOverviewBands[iBand][iOverview]->GetXSize() != nDstWidth ||
|
|
papapoOverviewBands[iBand][iOverview]->GetYSize() != nDstHeight)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"GDALRegenerateOverviewsMultiBand: all the overviews bands of the same level must have the same dimensions");
|
|
return CE_Failure;
|
|
}
|
|
if (papapoOverviewBands[iBand][iOverview]->GetRasterDataType() != eDataType)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"GDALRegenerateOverviewsMultiBand: all the overviews bands must have the same data type as the source bands");
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* First pass to compute the total number of pixels to read */
|
|
double dfTotalPixelCount = 0;
|
|
for(iOverview=0;iOverview<nOverviews;iOverview++)
|
|
{
|
|
nSrcWidth = papoSrcBands[0]->GetXSize();
|
|
nSrcHeight = papoSrcBands[0]->GetYSize();
|
|
|
|
int nDstWidth = papapoOverviewBands[0][iOverview]->GetXSize();
|
|
/* Try to use previous level of overview as the source to compute */
|
|
/* the next level */
|
|
if (iOverview > 0 && papapoOverviewBands[0][iOverview - 1]->GetXSize() > nDstWidth)
|
|
{
|
|
nSrcWidth = papapoOverviewBands[0][iOverview - 1]->GetXSize();
|
|
nSrcHeight = papapoOverviewBands[0][iOverview - 1]->GetYSize();
|
|
}
|
|
|
|
dfTotalPixelCount += (double)nSrcWidth * nSrcHeight;
|
|
}
|
|
|
|
nSrcWidth = papoSrcBands[0]->GetXSize();
|
|
nSrcHeight = papoSrcBands[0]->GetYSize();
|
|
|
|
GDALDataType eWrkDataType = GDALGetOvrWorkDataType(pszResampling, eDataType);
|
|
|
|
/* If we have a nodata mask and we are doing something more complicated */
|
|
/* than nearest neighbouring, we have to fetch to nodata mask */
|
|
int bUseNoDataMask = (!EQUALN(pszResampling,"NEAR",4) &&
|
|
(papoSrcBands[0]->GetMaskFlags() & GMF_ALL_VALID) == 0);
|
|
|
|
int* pabHasNoData = (int*)CPLMalloc(nBands * sizeof(int));
|
|
float* pafNoDataValue = (float*)CPLMalloc(nBands * sizeof(float));
|
|
|
|
for(iBand=0;iBand<nBands;iBand++)
|
|
{
|
|
pabHasNoData[iBand] = FALSE;
|
|
pafNoDataValue[iBand] = (float) papoSrcBands[iBand]->GetNoDataValue(&pabHasNoData[iBand]);
|
|
}
|
|
|
|
/* Second pass to do the real job ! */
|
|
double dfCurPixelCount = 0;
|
|
for(iOverview=0;iOverview<nOverviews && eErr == CE_None;iOverview++)
|
|
{
|
|
int iSrcOverview = -1; /* -1 means the source bands */
|
|
|
|
int nDstBlockXSize, nDstBlockYSize;
|
|
int nDstWidth, nDstHeight;
|
|
papapoOverviewBands[0][iOverview]->GetBlockSize(&nDstBlockXSize, &nDstBlockYSize);
|
|
nDstWidth = papapoOverviewBands[0][iOverview]->GetXSize();
|
|
nDstHeight = papapoOverviewBands[0][iOverview]->GetYSize();
|
|
|
|
/* Try to use previous level of overview as the source to compute */
|
|
/* the next level */
|
|
if (iOverview > 0 && papapoOverviewBands[0][iOverview - 1]->GetXSize() > nDstWidth)
|
|
{
|
|
nSrcWidth = papapoOverviewBands[0][iOverview - 1]->GetXSize();
|
|
nSrcHeight = papapoOverviewBands[0][iOverview - 1]->GetYSize();
|
|
iSrcOverview = iOverview - 1;
|
|
}
|
|
|
|
double dfXRatioDstToSrc = (double)nSrcWidth / nDstWidth;
|
|
double dfYRatioDstToSrc = (double)nSrcHeight / nDstHeight;
|
|
|
|
/* Compute the maximum chunck size of the source such as it will match the size of */
|
|
/* a block of the overview */
|
|
int nFullResXChunk = 1 + (int)(nDstBlockXSize * dfXRatioDstToSrc);
|
|
int nFullResYChunk = 1 + (int)(nDstBlockYSize * dfYRatioDstToSrc);
|
|
|
|
int nOvrFactor = MAX( (int)(0.5 + dfXRatioDstToSrc),
|
|
(int)(0.5 + dfYRatioDstToSrc) );
|
|
if( nOvrFactor == 0 ) nOvrFactor = 1;
|
|
int nFullResXChunkQueried = nFullResXChunk + 2 * nKernelRadius * nOvrFactor;
|
|
int nFullResYChunkQueried = nFullResYChunk + 2 * nKernelRadius * nOvrFactor;
|
|
|
|
void** papaChunk = (void**) CPLMalloc(nBands * sizeof(void*));
|
|
GByte* pabyChunkNoDataMask = NULL;
|
|
for(iBand=0;iBand<nBands;iBand++)
|
|
{
|
|
papaChunk[iBand] = VSIMalloc3(nFullResXChunkQueried, nFullResYChunkQueried, GDALGetDataTypeSize(eWrkDataType) / 8);
|
|
if( papaChunk[iBand] == NULL )
|
|
{
|
|
while ( --iBand >= 0)
|
|
CPLFree(papaChunk[iBand]);
|
|
CPLFree(papaChunk);
|
|
CPLFree(pabHasNoData);
|
|
CPLFree(pafNoDataValue);
|
|
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"GDALRegenerateOverviewsMultiBand: Out of memory." );
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
if (bUseNoDataMask)
|
|
{
|
|
pabyChunkNoDataMask = (GByte*) VSIMalloc2(nFullResXChunkQueried, nFullResYChunkQueried);
|
|
if( pabyChunkNoDataMask == NULL )
|
|
{
|
|
for(iBand=0;iBand<nBands;iBand++)
|
|
{
|
|
CPLFree(papaChunk[iBand]);
|
|
}
|
|
CPLFree(papaChunk);
|
|
CPLFree(pabHasNoData);
|
|
CPLFree(pafNoDataValue);
|
|
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"GDALRegenerateOverviewsMultiBand: Out of memory." );
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
int nDstYOff;
|
|
/* Iterate on destination overview, block by block */
|
|
for( nDstYOff = 0; nDstYOff < nDstHeight && eErr == CE_None; nDstYOff += nDstBlockYSize )
|
|
{
|
|
int nDstYCount;
|
|
if (nDstYOff + nDstBlockYSize <= nDstHeight)
|
|
nDstYCount = nDstBlockYSize;
|
|
else
|
|
nDstYCount = nDstHeight - nDstYOff;
|
|
|
|
int nChunkYOff = (int) (0.5 + nDstYOff * dfYRatioDstToSrc);
|
|
int nChunkYOff2 = (int) (0.5 + (nDstYOff + nDstYCount) * dfYRatioDstToSrc);
|
|
if( nChunkYOff2 > nSrcHeight || nDstYOff + nDstYCount == nDstHeight)
|
|
nChunkYOff2 = nSrcHeight;
|
|
int nYCount = nChunkYOff2 - nChunkYOff;
|
|
CPLAssert(nYCount <= nFullResYChunk);
|
|
|
|
int nChunkYOffQueried = nChunkYOff - nKernelRadius * nOvrFactor;
|
|
int nChunkYSizeQueried = nYCount + 2 * nKernelRadius * nOvrFactor;
|
|
if( nChunkYOffQueried < 0 )
|
|
{
|
|
nChunkYSizeQueried += nChunkYOffQueried;
|
|
nChunkYOffQueried = 0;
|
|
}
|
|
if( nChunkYSizeQueried + nChunkYOffQueried > nSrcHeight )
|
|
nChunkYSizeQueried = nSrcHeight - nChunkYOffQueried;
|
|
CPLAssert(nChunkYSizeQueried <= nFullResYChunkQueried);
|
|
|
|
if( !pfnProgress( dfCurPixelCount / dfTotalPixelCount,
|
|
NULL, pProgressData ) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
|
|
eErr = CE_Failure;
|
|
}
|
|
|
|
int nDstXOff;
|
|
/* Iterate on destination overview, block by block */
|
|
for( nDstXOff = 0; nDstXOff < nDstWidth && eErr == CE_None; nDstXOff += nDstBlockXSize )
|
|
{
|
|
int nDstXCount;
|
|
if (nDstXOff + nDstBlockXSize <= nDstWidth)
|
|
nDstXCount = nDstBlockXSize;
|
|
else
|
|
nDstXCount = nDstWidth - nDstXOff;
|
|
|
|
int nChunkXOff = (int) (0.5 + nDstXOff * dfXRatioDstToSrc);
|
|
int nChunkXOff2 = (int) (0.5 + (nDstXOff + nDstXCount) * dfXRatioDstToSrc);
|
|
if( nChunkXOff2 > nSrcWidth || nDstXOff + nDstXCount == nDstWidth)
|
|
nChunkXOff2 = nSrcWidth;
|
|
int nXCount = nChunkXOff2 - nChunkXOff;
|
|
CPLAssert(nXCount <= nFullResXChunk);
|
|
|
|
int nChunkXOffQueried = nChunkXOff - nKernelRadius * nOvrFactor;
|
|
int nChunkXSizeQueried = nXCount + 2 * nKernelRadius * nOvrFactor;
|
|
if( nChunkXOffQueried < 0 )
|
|
{
|
|
nChunkXSizeQueried += nChunkXOffQueried;
|
|
nChunkXOffQueried = 0;
|
|
}
|
|
if( nChunkXSizeQueried + nChunkXOffQueried > nSrcWidth )
|
|
nChunkXSizeQueried = nSrcWidth - nChunkXOffQueried;
|
|
CPLAssert(nChunkXSizeQueried <= nFullResXChunkQueried);
|
|
/*CPLDebug("GDAL", "Reading (%dx%d -> %dx%d) for output (%dx%d -> %dx%d)",
|
|
nChunkXOff, nChunkYOff, nXCount, nYCount,
|
|
nDstXOff, nDstYOff, nDstXCount, nDstYCount);*/
|
|
|
|
/* Read the source buffers for all the bands */
|
|
for(iBand=0;iBand<nBands && eErr == CE_None;iBand++)
|
|
{
|
|
GDALRasterBand* poSrcBand;
|
|
if (iSrcOverview == -1)
|
|
poSrcBand = papoSrcBands[iBand];
|
|
else
|
|
poSrcBand = papapoOverviewBands[iBand][iSrcOverview];
|
|
eErr = poSrcBand->RasterIO( GF_Read,
|
|
nChunkXOffQueried, nChunkYOffQueried,
|
|
nChunkXSizeQueried, nChunkYSizeQueried,
|
|
papaChunk[iBand],
|
|
nChunkXSizeQueried, nChunkYSizeQueried,
|
|
eWrkDataType, 0, 0, NULL );
|
|
}
|
|
|
|
if (bUseNoDataMask && eErr == CE_None)
|
|
{
|
|
GDALRasterBand* poSrcBand;
|
|
if (iSrcOverview == -1)
|
|
poSrcBand = papoSrcBands[0];
|
|
else
|
|
poSrcBand = papapoOverviewBands[0][iSrcOverview];
|
|
eErr = poSrcBand->GetMaskBand()->RasterIO( GF_Read,
|
|
nChunkXOffQueried, nChunkYOffQueried,
|
|
nChunkXSizeQueried, nChunkYSizeQueried,
|
|
pabyChunkNoDataMask,
|
|
nChunkXSizeQueried, nChunkYSizeQueried,
|
|
GDT_Byte, 0, 0, NULL );
|
|
}
|
|
|
|
/* Compute the resulting overview block */
|
|
for(iBand=0;iBand<nBands && eErr == CE_None;iBand++)
|
|
{
|
|
eErr = pfnResampleFn( dfXRatioDstToSrc, dfYRatioDstToSrc,
|
|
0.0, 0.0,
|
|
eWrkDataType,
|
|
papaChunk[iBand],
|
|
pabyChunkNoDataMask,
|
|
nChunkXOffQueried, nChunkXSizeQueried,
|
|
nChunkYOffQueried, nChunkYSizeQueried,
|
|
nDstXOff, nDstXOff + nDstXCount,
|
|
nDstYOff, nDstYOff + nDstYCount,
|
|
papapoOverviewBands[iBand][iOverview],
|
|
pszResampling,
|
|
pabHasNoData[iBand],
|
|
pafNoDataValue[iBand],
|
|
/*poColorTable*/ NULL,
|
|
eDataType);
|
|
}
|
|
}
|
|
|
|
dfCurPixelCount += (double)nYCount * nSrcWidth;
|
|
}
|
|
|
|
/* Flush the data to overviews */
|
|
for(iBand=0;iBand<nBands;iBand++)
|
|
{
|
|
CPLFree(papaChunk[iBand]);
|
|
papapoOverviewBands[iBand][iOverview]->FlushCache();
|
|
}
|
|
CPLFree(papaChunk);
|
|
CPLFree(pabyChunkNoDataMask);
|
|
|
|
}
|
|
|
|
CPLFree(pabHasNoData);
|
|
CPLFree(pafNoDataValue);
|
|
|
|
if (eErr == CE_None)
|
|
pfnProgress( 1.0, NULL, pProgressData );
|
|
|
|
return eErr;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* GDALComputeBandStats() */
|
|
/************************************************************************/
|
|
|
|
CPLErr CPL_STDCALL
|
|
GDALComputeBandStats( GDALRasterBandH hSrcBand,
|
|
int nSampleStep,
|
|
double *pdfMean, double *pdfStdDev,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData )
|
|
|
|
{
|
|
VALIDATE_POINTER1( hSrcBand, "GDALComputeBandStats", CE_Failure );
|
|
|
|
GDALRasterBand *poSrcBand = (GDALRasterBand *) hSrcBand;
|
|
int iLine, nWidth, nHeight;
|
|
GDALDataType eType = poSrcBand->GetRasterDataType();
|
|
GDALDataType eWrkType;
|
|
int bComplex;
|
|
float *pafData;
|
|
double dfSum=0.0, dfSum2=0.0;
|
|
int nSamples = 0;
|
|
|
|
if( pfnProgress == NULL )
|
|
pfnProgress = GDALDummyProgress;
|
|
|
|
nWidth = poSrcBand->GetXSize();
|
|
nHeight = poSrcBand->GetYSize();
|
|
|
|
if( nSampleStep >= nHeight || nSampleStep < 1 )
|
|
nSampleStep = 1;
|
|
|
|
bComplex = GDALDataTypeIsComplex(eType);
|
|
if( bComplex )
|
|
{
|
|
pafData = (float *) VSIMalloc(nWidth * 2 * sizeof(float));
|
|
eWrkType = GDT_CFloat32;
|
|
}
|
|
else
|
|
{
|
|
pafData = (float *) VSIMalloc(nWidth * sizeof(float));
|
|
eWrkType = GDT_Float32;
|
|
}
|
|
|
|
if( pafData == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"GDALComputeBandStats: Out of memory for buffer." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Loop over all sample lines. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( iLine = 0; iLine < nHeight; iLine += nSampleStep )
|
|
{
|
|
int iPixel;
|
|
|
|
if( !pfnProgress( iLine / (double) nHeight,
|
|
NULL, pProgressData ) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
|
|
CPLFree( pafData );
|
|
return CE_Failure;
|
|
}
|
|
|
|
CPLErr eErr = poSrcBand->RasterIO( GF_Read, 0, iLine, nWidth, 1,
|
|
pafData, nWidth, 1, eWrkType,
|
|
0, 0, NULL );
|
|
if ( eErr != CE_None )
|
|
{
|
|
CPLFree( pafData );
|
|
return eErr;
|
|
}
|
|
|
|
for( iPixel = 0; iPixel < nWidth; iPixel++ )
|
|
{
|
|
float fValue;
|
|
|
|
if( bComplex )
|
|
{
|
|
// Compute the magnitude of the complex value.
|
|
|
|
fValue = (float)
|
|
sqrt(pafData[iPixel*2 ] * pafData[iPixel*2 ]
|
|
+ pafData[iPixel*2+1] * pafData[iPixel*2+1]);
|
|
}
|
|
else
|
|
{
|
|
fValue = pafData[iPixel];
|
|
}
|
|
|
|
dfSum += fValue;
|
|
dfSum2 += fValue * fValue;
|
|
}
|
|
|
|
nSamples += nWidth;
|
|
}
|
|
|
|
if( !pfnProgress( 1.0, NULL, pProgressData ) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
|
|
CPLFree( pafData );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Produce the result values. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pdfMean != NULL )
|
|
*pdfMean = dfSum / nSamples;
|
|
|
|
if( pdfStdDev != NULL )
|
|
{
|
|
double dfMean = dfSum / nSamples;
|
|
|
|
*pdfStdDev = sqrt((dfSum2 / nSamples) - (dfMean * dfMean));
|
|
}
|
|
|
|
CPLFree( pafData );
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALOverviewMagnitudeCorrection() */
|
|
/* */
|
|
/* Correct the mean and standard deviation of the overviews of */
|
|
/* the given band to match the base layer approximately. */
|
|
/************************************************************************/
|
|
|
|
CPLErr
|
|
GDALOverviewMagnitudeCorrection( GDALRasterBandH hBaseBand,
|
|
int nOverviewCount,
|
|
GDALRasterBandH *pahOverviews,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData )
|
|
|
|
{
|
|
VALIDATE_POINTER1( hBaseBand, "GDALOverviewMagnitudeCorrection", CE_Failure );
|
|
|
|
CPLErr eErr;
|
|
double dfOrigMean, dfOrigStdDev;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Compute mean/stddev for source raster. */
|
|
/* -------------------------------------------------------------------- */
|
|
eErr = GDALComputeBandStats( hBaseBand, 2, &dfOrigMean, &dfOrigStdDev,
|
|
pfnProgress, pProgressData );
|
|
|
|
if( eErr != CE_None )
|
|
return eErr;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Loop on overview bands. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iOverview;
|
|
|
|
for( iOverview = 0; iOverview < nOverviewCount; iOverview++ )
|
|
{
|
|
GDALRasterBand *poOverview = (GDALRasterBand *)pahOverviews[iOverview];
|
|
double dfOverviewMean, dfOverviewStdDev;
|
|
double dfGain;
|
|
|
|
eErr = GDALComputeBandStats( pahOverviews[iOverview], 1,
|
|
&dfOverviewMean, &dfOverviewStdDev,
|
|
pfnProgress, pProgressData );
|
|
|
|
if( eErr != CE_None )
|
|
return eErr;
|
|
|
|
if( dfOrigStdDev < 0.0001 )
|
|
dfGain = 1.0;
|
|
else
|
|
dfGain = dfOrigStdDev / dfOverviewStdDev;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Apply gain and offset. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALDataType eWrkType, eType = poOverview->GetRasterDataType();
|
|
int iLine, nWidth, nHeight, bComplex;
|
|
float *pafData;
|
|
|
|
nWidth = poOverview->GetXSize();
|
|
nHeight = poOverview->GetYSize();
|
|
|
|
bComplex = GDALDataTypeIsComplex(eType);
|
|
if( bComplex )
|
|
{
|
|
pafData = (float *) VSIMalloc2(nWidth, 2 * sizeof(float));
|
|
eWrkType = GDT_CFloat32;
|
|
}
|
|
else
|
|
{
|
|
pafData = (float *) VSIMalloc2(nWidth, sizeof(float));
|
|
eWrkType = GDT_Float32;
|
|
}
|
|
|
|
if( pafData == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"GDALOverviewMagnitudeCorrection: Out of memory for buffer." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
for( iLine = 0; iLine < nHeight; iLine++ )
|
|
{
|
|
int iPixel;
|
|
|
|
if( !pfnProgress( iLine / (double) nHeight,
|
|
NULL, pProgressData ) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
|
|
CPLFree( pafData );
|
|
return CE_Failure;
|
|
}
|
|
|
|
poOverview->RasterIO( GF_Read, 0, iLine, nWidth, 1,
|
|
pafData, nWidth, 1, eWrkType,
|
|
0, 0, NULL );
|
|
|
|
for( iPixel = 0; iPixel < nWidth; iPixel++ )
|
|
{
|
|
if( bComplex )
|
|
{
|
|
pafData[iPixel*2] *= (float) dfGain;
|
|
pafData[iPixel*2+1] *= (float) dfGain;
|
|
}
|
|
else
|
|
{
|
|
pafData[iPixel] = (float)
|
|
((pafData[iPixel]-dfOverviewMean)*dfGain + dfOrigMean);
|
|
|
|
}
|
|
}
|
|
|
|
poOverview->RasterIO( GF_Write, 0, iLine, nWidth, 1,
|
|
pafData, nWidth, 1, eWrkType,
|
|
0, 0, NULL );
|
|
}
|
|
|
|
if( !pfnProgress( 1.0, NULL, pProgressData ) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
|
|
CPLFree( pafData );
|
|
return CE_Failure;
|
|
}
|
|
|
|
CPLFree( pafData );
|
|
}
|
|
|
|
return CE_None;
|
|
}
|