/****************************************************************************** * $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 * * 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 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 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;iGetColorEntryAsRGB(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(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(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(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;iGetColorEntryAsRGB(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;iGetColorEntryAsRGB(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 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 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 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 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 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 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 /************************************************************************/ /* GDALResampleConvolutionHorizontalSSE2 */ /************************************************************************/ template 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 */ /************************************************************************/ template<> inline double GDALResampleConvolutionHorizontal( const GByte* pChunk, const double* padfWeightsAligned, int nSrcPixelCount) { return GDALResampleConvolutionHorizontalSSE2(pChunk, padfWeightsAligned, nSrcPixelCount); } template<> inline double GDALResampleConvolutionHorizontal( const GUInt16* pChunk, const double* padfWeightsAligned, int nSrcPixelCount) { return GDALResampleConvolutionHorizontalSSE2(pChunk, padfWeightsAligned, nSrcPixelCount); } /************************************************************************/ /* GDALResampleConvolutionHorizontalWithMaskSSE2 */ /************************************************************************/ template 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 */ /************************************************************************/ template<> inline void GDALResampleConvolutionHorizontalWithMask( 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( 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 */ /************************************************************************/ template 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 */ /************************************************************************/ template<> inline void GDALResampleConvolutionHorizontal_3rows( 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( 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 */ /************************************************************************/ template 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 */ /************************************************************************/ template<> inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows( 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( 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 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 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 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;iBandGetXSize() != 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;iOverviewGetXSize(); int nDstHeight = papapoOverviewBands[0][iOverview]->GetYSize(); for(iBand=1;iBandGetXSize() != 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;iOverviewGetXSize(); 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;iBandGetNoDataValue(&pabHasNoData[iBand]); } /* Second pass to do the real job ! */ double dfCurPixelCount = 0; for(iOverview=0;iOverviewGetBlockSize(&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= 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 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;iBandRasterIO( 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;iBandFlushCache(); } 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; }