/****************************************************************************** * $Id: overview.cpp 15729 2008-11-13 20:33:46Z warmerdam $ * * Project: GDAL Core * Purpose: Helper code to implement overview support in different drivers. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 2000, Frank Warmerdam * * 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" CPL_CVSID("$Id: overview.cpp 15729 2008-11-13 20:33:46Z warmerdam $"); typedef enum { GRM_Near = 0, GRM_Average = 1, GRM_Gauss = 2, GRM_Mode = 3, GRM_Cubic = 4, } GDALResamplingMethod; /************************************************************************/ /* GDALDownsampleChunk32R() */ /************************************************************************/ static CPLErr GDALDownsampleChunk32R( int nSrcWidth, int nSrcHeight, float * pafChunk, GByte * pabyChunkNodataMask, int nChunkXOff, int nChunkXSize, int nChunkYOff, int nChunkYSize, GDALRasterBand * poOverview, const char * pszResampling, int bHasNoData, float fNoDataValue, GDALColorTable* poColorTable, GDALDataType eSrcDataType) { /* -------------------------------------------------------------------- */ /* Check the input parameters. */ /* TODO: I think that instead of passing the pszResampling string */ /* and endless string comparisons this function should take */ /* GDALResamplingMethod flag. The string to flag conversion */ /* should be done on upper level. */ /* -------------------------------------------------------------------- */ CPLErr eErr = CE_None; GDALResamplingMethod eResampling; if ( EQUALN(pszResampling, "NEAR", 4) ) eResampling = GRM_Near; else if ( EQUALN(pszResampling, "AVER", 4) ) eResampling = GRM_Average; else if ( EQUALN(pszResampling, "GAUSS", 5) ) eResampling = GRM_Gauss; else if ( EQUALN(pszResampling, "MODE", 4) ) eResampling = GRM_Mode; else if ( EQUALN(pszResampling, "CUBIC", 6) ) eResampling = GRM_Cubic; else { CPLError( CE_Failure, CPLE_AppDefined, "GDALDownsampleChunk32R: Unsupported resampling method \"%s\".", pszResampling ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Create the filter kernel and allocate scanline buffer. */ /* -------------------------------------------------------------------- */ int nDstXOff, nDstXOff2, nDstYOff, nDstYOff2, 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 + (double)nSrcHeight/(double)nOYSize); // matrix for gauss filter if(nResYFactor <= 2) { panGaussMatrix = anGaussMatrix3x3; nGaussMatrixDim=3; } else if (nResYFactor <= 4) { panGaussMatrix = anGaussMatrix5x5; nGaussMatrixDim=5; } else { panGaussMatrix = anGaussMatrix7x7; nGaussMatrixDim=7; } /* -------------------------------------------------------------------- */ /* Figure out the column to start writing to, and the first column */ /* to not write to. */ /* -------------------------------------------------------------------- */ nDstXOff = (int) (0.5 + (nChunkXOff/(double)nSrcWidth) * nOXSize); nDstXOff2 = (int) (0.5 + ((nChunkXOff+nChunkXSize)/(double)nSrcWidth) * nOXSize); if( nChunkXOff + nChunkXSize == nSrcWidth ) nDstXOff2 = nOXSize; pafDstScanline = (float *) VSIMalloc((nDstXOff2 - nDstXOff) * sizeof(float)); if( pafDstScanline == NULL ) { CPLError( CE_Failure, CPLE_OutOfMemory, "GDALDownsampleChunk32R: Out of memory for line buffer." ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* 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. */ /* -------------------------------------------------------------------- */ nDstYOff = (int) (0.5 + (nChunkYOff/(double)nSrcHeight) * nOYSize); nDstYOff2 = (int) (0.5 + ((nChunkYOff+nChunkYSize)/(double)nSrcHeight) * nOYSize); if( nChunkYOff + nChunkYSize == nSrcHeight ) nDstYOff2 = nOYSize; 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; /* ==================================================================== */ /* Loop over destination scanlines. */ /* ==================================================================== */ for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ ) { float *pafSrcScanline; GByte *pabySrcScanlineNodataMask; int nSrcYOff, nSrcYOff2 = 0, iDstPixel; if( eResampling == GRM_Near || eResampling == GRM_Average || eResampling == GRM_Mode ) { nSrcYOff = (int) (0.5 + (iDstLine/(double)nOYSize) * nSrcHeight); if ( nSrcYOff < nChunkYOff ) nSrcYOff = nChunkYOff; nSrcYOff2 = (int) (0.5 + ((iDstLine+1)/(double)nOYSize) * nSrcHeight); } else /* if (eResampling == GRM_Gauss) */ { nSrcYOff = (int) (0.5 + (iDstLine/(double)nOYSize) * nSrcHeight); nSrcYOff2 = (int) (0.5 + ((iDstLine+1)/(double)nOYSize) * nSrcHeight) + 1; if( nSrcYOff < nChunkYOff ) { nSrcYOff = nChunkYOff; nSrcYOff2++; } int iSizeY = nSrcYOff2 - nSrcYOff; nSrcYOff = nSrcYOff + iSizeY/2 - nGaussMatrixDim/2; nSrcYOff2 = nSrcYOff + nGaussMatrixDim; if(nSrcYOff < 0) nSrcYOff = 0; } if( nSrcYOff2 > nSrcHeight || iDstLine == nOYSize-1 ) nSrcYOff2 = nSrcHeight; if( nSrcYOff2 > nChunkYOff + nChunkYSize) nSrcYOff2 = nChunkYOff + nChunkYSize; 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; if ( eResampling == GRM_Near || eResampling == GRM_Average || eResampling == GRM_Mode ) { nSrcXOff = (int) (0.5 + (iDstPixel/(double)nOXSize) * nSrcWidth); if ( nSrcXOff < nChunkXOff ) nSrcXOff = nChunkXOff; nSrcXOff2 = (int) (0.5 + ((iDstPixel+1)/(double)nOXSize) * nSrcWidth); } else /* if (eResampling == GRM_Gauss) */ { nSrcXOff = (int) (0.5 + (iDstPixel/(double)nOXSize) * nSrcWidth); nSrcXOff2 = (int)(0.5 + ((iDstPixel+1)/(double)nOXSize) * nSrcWidth) + 1; int iSizeX = nSrcXOff2 - nSrcXOff; nSrcXOff = nSrcXOff + iSizeX/2 - nGaussMatrixDim/2; nSrcXOff2 = nSrcXOff + nGaussMatrixDim; if(nSrcXOff < 0) nSrcXOff = 0; } if( nSrcXOff2 > nSrcWidth || iDstPixel == nOXSize-1 ) nSrcXOff2 = nSrcWidth; if( nSrcXOff2 > nChunkXOff + nChunkXSize ) nSrcXOff2 = nChunkXOff + nChunkXSize; if ( eResampling == GRM_Near ) { pafDstScanline[iDstPixel - nDstXOff] = pafSrcScanline[nSrcXOff - nChunkXOff]; } else if ( eResampling == GRM_Average ) { if (poColorTable == NULL) { double val; double dfTotal = 0.0; int nCount = 0, iX, iY; for( iY = nSrcYOff; iY < nSrcYOff2; iY++ ) { for( iX = nSrcXOff; iX < nSrcXOff2; iX++ ) { val = pafSrcScanline[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize]; if (pabySrcScanlineNodataMask == NULL || pabySrcScanlineNodataMask[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize]) { dfTotal += val; nCount++; } } } 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 nCount = 0, iX, iY; for( iY = nSrcYOff; iY < nSrcYOff2; iY++ ) { for( iX = nSrcXOff; iX < nSrcXOff2; iX++ ) { val = pafSrcScanline[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize]; if (bHasNoData == FALSE || val != fNoDataValue) { int nVal = (int)val; if (nVal >= 0 && nVal < nEntryCount) { nTotalR += aEntries[nVal].c1; nTotalG += aEntries[nVal].c2; nTotalB += aEntries[nVal].c3; nCount++; } } } } if (bHasNoData && nCount == 0) { pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue; } else { CPLAssert( nCount > 0 ); if( nCount == 0 ) pafDstScanline[iDstPixel - nDstXOff] = 0.0; else { int nR = nTotalR / nCount, nG = nTotalG / nCount, nB = nTotalB / nCount; int i; double dfMinDist = 0; int iBestEntry = 0; for(i=0;i 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] = iMaxInd; } } else /* if ( eResampling == GRM_Gauss ) */ { if (poColorTable == NULL) { double dfTotal = 0.0, val; int nCount = 0, iX, iY; int i = 0,j = 0; const int *panLineWeight = panGaussMatrix; 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; 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 { CPLAssert( nTotalWeight > 0 ); if( nTotalWeight == 0 ) pafDstScanline[iDstPixel - nDstXOff] = 0.0; else { int nR = nTotalR / nTotalWeight, nG = nTotalG / nTotalWeight, nB = nTotalB / nTotalWeight; int i; double dfMinDist = 0; int iBestEntry = 0; for(i=0;iRasterIO( GF_Write, nDstXOff, iDstLine, nDstXOff2 - nDstXOff, 1, pafDstScanline, nDstXOff2 - nDstXOff, 1, GDT_Float32, 0, 0 ); } CPLFree( pafDstScanline ); CPLFree( aEntries ); CPLFree( pafVals ); CPLFree( panSums ); return eErr; } /************************************************************************/ /* GDALDownsampleChunkC32R() */ /************************************************************************/ static CPLErr GDALDownsampleChunkC32R( int nSrcWidth, int nSrcHeight, float * pafChunk, int nChunkYOff, int nChunkYSize, GDALRasterBand * poOverview, const char * pszResampling ) { int nDstYOff, nDstYOff2, nOXSize, nOYSize; float *pafDstScanline; CPLErr eErr = CE_None; nOXSize = poOverview->GetXSize(); nOYSize = poOverview->GetYSize(); pafDstScanline = (float *) VSIMalloc(nOXSize * sizeof(float) * 2); if( pafDstScanline == NULL ) { CPLError( CE_Failure, CPLE_OutOfMemory, "GDALDownsampleChunkC32R: Out of memory for line buffer." ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* 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. */ /* -------------------------------------------------------------------- */ nDstYOff = (int) (0.5 + (nChunkYOff/(double)nSrcHeight) * nOYSize); nDstYOff2 = (int) (0.5 + ((nChunkYOff+nChunkYSize)/(double)nSrcHeight) * nOYSize); if( nChunkYOff + nChunkYSize == nSrcHeight ) nDstYOff2 = nOYSize; /* ==================================================================== */ /* 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/(double)nOYSize) * nSrcHeight); if( nSrcYOff < nChunkYOff ) nSrcYOff = nChunkYOff; nSrcYOff2 = (int) (0.5 + ((iDstLine+1)/(double)nOYSize) * nSrcHeight); if( nSrcYOff2 > nSrcHeight || iDstLine == nOYSize-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/(double)nOXSize) * nSrcWidth); nSrcXOff2 = (int) (0.5 + ((iDstPixel+1)/(double)nOXSize) * nSrcWidth); if( nSrcXOff2 > nSrcWidth ) 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 ); } 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; } /************************************************************************/ /* GDALRegenerateOverviews() */ /************************************************************************/ /** * 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(). * * @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; int nFRXBlockSize, nFRYBlockSize; GDALDataType eType; int bHasNoData; float fNoDataValue; GDALColorTable* poColorTable = NULL; if( pfnProgress == NULL ) pfnProgress = GDALDummyProgress; if( EQUAL(pszResampling,"NONE") ) return CE_None; /* -------------------------------------------------------------------- */ /* 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."); } } /* -------------------------------------------------------------------- */ /* If we are operating on multiple overviews, and using */ /* averaging, lets do them in cascading order to reduce the */ /* amount of computation. */ /* -------------------------------------------------------------------- */ if( (EQUALN(pszResampling,"AVER",4) || EQUALN(pszResampling,"GAUSS",5)) && nOverviewCount > 1 ) return GDALRegenerateCascadingOverviews( poSrcBand, nOverviewCount, papoOvrBands, pszResampling, pfnProgress, pProgressData ); /* -------------------------------------------------------------------- */ /* Setup one horizontal swath to read from the raw buffer. */ /* -------------------------------------------------------------------- */ float *pafChunk; GByte *pabyChunkNodataMask = NULL; poSrcBand->GetBlockSize( &nFRXBlockSize, &nFRYBlockSize ); if( nFRYBlockSize < 4 || nFRYBlockSize > 256 ) nFullResYChunk = 32; else nFullResYChunk = nFRYBlockSize; if( GDALDataTypeIsComplex( poSrcBand->GetRasterDataType() ) ) eType = GDT_CFloat32; else eType = GDT_Float32; /* 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) && (poSrcBand->GetMaskFlags() & GMF_ALL_VALID) == 0); nWidth = poSrcBand->GetXSize(); pafChunk = (float *) VSIMalloc3((GDALGetDataTypeSize(eType)/8), nFullResYChunk, nWidth ); if (bUseNoDataMask) { pabyChunkNodataMask = (GByte *) (GByte*) VSIMalloc2( nFullResYChunk, nWidth ); } if( pafChunk == NULL || (bUseNoDataMask && pabyChunkNodataMask == NULL)) { CPLFree(pafChunk); CPLFree(pabyChunkNodataMask); CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory in GDALRegenerateOverviews()." ); return CE_Failure; } fNoDataValue = (float) poSrcBand->GetNoDataValue(&bHasNoData); if (!bHasNoData) { if (!EQUALN(pszResampling,"NEAR",4) && poSrcBand->GetDataset() != NULL) { const char* pszNoDataValues = poSrcBand->GetDataset()->GetMetadataItem("NODATA_VALUES"); if (pszNoDataValues) { char** papszNoDataValues = CSLTokenizeStringComplex(pszNoDataValues, " ", FALSE, FALSE); /* Make sure we have as many values as bands */ if (CSLCount(papszNoDataValues) == poSrcBand->GetDataset()->GetRasterCount() && poSrcBand->GetDataset()->GetRasterCount() != 0) { fNoDataValue = (float)atof(papszNoDataValues[poSrcBand->GetBand() - 1]); bHasNoData = TRUE; } CSLDestroy(papszNoDataValues); } } } /* -------------------------------------------------------------------- */ /* Loop over image operating on chunks. */ /* -------------------------------------------------------------------- */ int nChunkYOff = 0; CPLErr eErr = CE_None; for( nChunkYOff = 0; nChunkYOff < poSrcBand->GetYSize() && eErr == CE_None; nChunkYOff += nFullResYChunk ) { if( !pfnProgress( nChunkYOff / (double) poSrcBand->GetYSize(), NULL, pProgressData ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); return CE_Failure; } if( nFullResYChunk + nChunkYOff > poSrcBand->GetYSize() ) nFullResYChunk = poSrcBand->GetYSize() - nChunkYOff; /* read chunk */ poSrcBand->RasterIO( GF_Read, 0, nChunkYOff, nWidth, nFullResYChunk, pafChunk, nWidth, nFullResYChunk, eType, 0, 0 ); if (bUseNoDataMask) poSrcBand->GetMaskBand()->RasterIO( GF_Read, 0, nChunkYOff, nWidth, nFullResYChunk, pabyChunkNodataMask, nWidth, nFullResYChunk, GDT_Byte, 0, 0 ); /* special case to promote 1bit data to 8bit 0/255 values */ if( EQUAL(pszResampling,"AVERAGE_BIT2GRAYSCALE") ) { int i; for( i = nFullResYChunk*nWidth - 1; i >= 0; i-- ) { if( pafChunk[i] == 1.0 ) pafChunk[i] = 255.0; } } else if( EQUAL(pszResampling,"AVERAGE_BIT2GRAYSCALE_MINISWHITE") ) { int i; for( i = nFullResYChunk*nWidth - 1; i >= 0; i-- ) { if( pafChunk[i] == 1.0 ) pafChunk[i] = 0.0; else if( pafChunk[i] == 0.0 ) pafChunk[i] = 255.0; } } for( int iOverview = 0; iOverview < nOverviewCount && eErr == CE_None; iOverview++ ) { if( eType == GDT_Float32 ) eErr = GDALDownsampleChunk32R(nWidth, poSrcBand->GetYSize(), pafChunk, pabyChunkNodataMask, 0, nWidth, nChunkYOff, nFullResYChunk, papoOvrBands[iOverview], pszResampling, bHasNoData, fNoDataValue, poColorTable, poSrcBand->GetRasterDataType()); else eErr = GDALDownsampleChunkC32R(nWidth, poSrcBand->GetYSize(), pafChunk, nChunkYOff, nFullResYChunk, papoOvrBands[iOverview], pszResampling); } } VSIFree( pafChunk ); VSIFree( pabyChunkNodataMask ); /* -------------------------------------------------------------------- */ /* Renormalized overview mean / stddev if needed. */ /* -------------------------------------------------------------------- */ if( 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() */ /************************************************************************/ /** * 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 * * @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")) { CPLError(CE_Failure, CPLE_NotSupported, "GDALRegenerateOverviewsMultiBand: pszResampling='%s' not supported", pszResampling); 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(); /* 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]); if (!pabHasNoData[iBand]) { if (!EQUALN(pszResampling,"NEAR",4) && papoSrcBands[iBand]->GetDataset() != NULL) { const char* pszNoDataValues = papoSrcBands[iBand]->GetDataset()->GetMetadataItem("NODATA_VALUES"); if (pszNoDataValues) { char** papszNoDataValues = CSLTokenizeStringComplex(pszNoDataValues, " ", FALSE, FALSE); /* Make sure we have as many values as bands */ if (CSLCount(papszNoDataValues) == papoSrcBands[iBand]->GetDataset()->GetRasterCount() && papoSrcBands[iBand]->GetDataset()->GetRasterCount() != 0) { pafNoDataValue[iBand] = (float)atof(papszNoDataValues[papoSrcBands[iBand]->GetBand() - 1]); pabHasNoData[iBand] = TRUE; } CSLDestroy(papszNoDataValues); } } } } /* 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; } /* Compute the chunck size of the source such as it will match the size of */ /* a block of the overview */ int nFullResXChunk = (nDstBlockXSize * nSrcWidth) / nDstWidth; int nFullResYChunk = (nDstBlockYSize * nSrcHeight) / nDstHeight; float** papafChunk = (float**) CPLMalloc(nBands * sizeof(void*)); GByte* pabyChunkNoDataMask = NULL; for(iBand=0;iBand= 0) CPLFree(papafChunk[iBand]); CPLFree(papafChunk); CPLFree(pabHasNoData); CPLFree(pafNoDataValue); CPLError( CE_Failure, CPLE_OutOfMemory, "GDALRegenerateOverviewsMultiBand: Out of memory." ); return CE_Failure; } } if (bUseNoDataMask) { pabyChunkNoDataMask = (GByte*) VSIMalloc2(nFullResXChunk, nFullResYChunk); if( pabyChunkNoDataMask == NULL ) { for(iBand=0;iBandRasterIO( GF_Read, nChunkXOff, nChunkYOff, nXCount, nYCount, papafChunk[iBand], nXCount, nYCount, GDT_Float32, 0, 0 ); } if (bUseNoDataMask && eErr == CE_None) { GDALRasterBand* poSrcBand; if (iSrcOverview == -1) poSrcBand = papoSrcBands[0]; else poSrcBand = papapoOverviewBands[0][iSrcOverview]; eErr = poSrcBand->GetMaskBand()->RasterIO( GF_Read, nChunkXOff, nChunkYOff, nXCount, nYCount, pabyChunkNoDataMask, nXCount, nYCount, GDT_Byte, 0, 0 ); } /* Compute the resulting overview block */ for(iBand=0;iBandFlushCache(); } CPLFree(papafChunk); 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; } poSrcBand->RasterIO( GF_Read, 0, iLine, nWidth, 1, pafData, nWidth, 1, eWrkType, 0, 0 ); 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 ); 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 ); } if( !pfnProgress( 1.0, NULL, pProgressData ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); CPLFree( pafData ); return CE_Failure; } CPLFree( pafData ); } return CE_None; }