QgsRasterBlock* QgsSingleBandColorDataRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback ) { Q_UNUSED( bandNo ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { return outputBlock; } QgsRasterBlock *inputBlock = mInput->block( mBand, extent, width, height, feedback ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } bool hasTransparency = usesTransparency(); if ( !hasTransparency ) { // Nothing to do, just retype if necessary inputBlock->convert( Qgis::ARGB32_Premultiplied ); delete outputBlock; return inputBlock; } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; return outputBlock; } // make sure input is also premultiplied! inputBlock->convert( Qgis::ARGB32_Premultiplied ); QRgb* inputBits = ( QRgb* )inputBlock->bits(); QRgb* outputBits = ( QRgb* )outputBlock->bits(); for ( qgssize i = 0; i < ( qgssize )width*height; i++ ) { QRgb c = inputBits[i]; outputBits[i] = qRgba( mOpacity * qRed( c ), mOpacity * qGreen( c ), mOpacity * qBlue( c ), mOpacity * qAlpha( c ) ); } delete inputBlock; return outputBlock; }
QgsRasterBlock* QgsSingleBandColorDataRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height ) { Q_UNUSED( bandNo ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { return outputBlock; } QgsRasterBlock *inputBlock = mInput->block( mBand, extent, width, height ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } bool hasTransparency = usesTransparency(); if ( !hasTransparency ) { // Nothing to do, just retype if necessary inputBlock->convert( QgsRasterBlock::ARGB32_Premultiplied ); delete outputBlock; return inputBlock; } if ( !outputBlock->reset( QgsRasterBlock::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; return outputBlock; } for ( size_t i = 0; i < ( size_t )width*height; i++ ) { QRgb pixelColor; double alpha = 255.0; QRgb c = inputBlock->color( i ); alpha = qAlpha( c ); pixelColor = qRgba( mOpacity * qRed( c ), mOpacity * qGreen( c ), mOpacity * qBlue( c ), mOpacity * alpha ); outputBlock->setColor( i, pixelColor ); } delete inputBlock; return outputBlock; }
QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback ) { QgsDebugMsgLevel( QString( "bandNo = %1 width = %2 height = %3" ).arg( bandNo ).arg( width ).arg( height ), 4 ); QgsDebugMsgLevel( QString( "boundingBox = %1" ).arg( boundingBox.toString() ), 4 ); QgsRasterBlock *block = new QgsRasterBlock( dataType( bandNo ), width, height ); if ( sourceHasNoDataValue( bandNo ) && useSourceNoDataValue( bandNo ) ) { block->setNoDataValue( sourceNoDataValue( bandNo ) ); } if ( block->isEmpty() ) { QgsDebugMsg( "Couldn't create raster block" ); return block; } // Read necessary extent only QgsRectangle tmpExtent = extent().intersect( &boundingBox ); if ( tmpExtent.isEmpty() ) { QgsDebugMsg( "Extent outside provider extent" ); block->setIsNoData(); return block; } double xRes = boundingBox.width() / width; double yRes = boundingBox.height() / height; double tmpXRes, tmpYRes; double providerXRes = 0; double providerYRes = 0; if ( capabilities() & Size ) { providerXRes = extent().width() / xSize(); providerYRes = extent().height() / ySize(); tmpXRes = std::max( providerXRes, xRes ); tmpYRes = std::max( providerYRes, yRes ); if ( qgsDoubleNear( tmpXRes, xRes ) ) tmpXRes = xRes; if ( qgsDoubleNear( tmpYRes, yRes ) ) tmpYRes = yRes; } else { tmpXRes = xRes; tmpYRes = yRes; } if ( tmpExtent != boundingBox || tmpXRes > xRes || tmpYRes > yRes ) { // Read smaller extent or lower resolution if ( !extent().contains( boundingBox ) ) { QRect subRect = QgsRasterBlock::subRect( boundingBox, width, height, extent() ); block->setIsNoDataExcept( subRect ); } // Calculate row/col limits (before tmpExtent is aligned) int fromRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMaximum() ) / yRes ); int toRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1; int fromCol = std::round( ( tmpExtent.xMinimum() - boundingBox.xMinimum() ) / xRes ); int toCol = std::round( ( tmpExtent.xMaximum() - boundingBox.xMinimum() ) / xRes ) - 1; QgsDebugMsgLevel( QString( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ), 4 ); if ( fromRow < 0 || fromRow >= height || toRow < 0 || toRow >= height || fromCol < 0 || fromCol >= width || toCol < 0 || toCol >= width ) { // Should not happen QgsDebugMsg( "Row or column limits out of range" ); return block; } // If lower source resolution is used, the extent must beS aligned to original // resolution to avoid possible shift due to resampling if ( tmpXRes > xRes ) { int col = std::floor( ( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes ); col = std::ceil( ( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes ); } if ( tmpYRes > yRes ) { int row = std::floor( ( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes ); tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes ); row = std::ceil( ( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes ); tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes ); } int tmpWidth = std::round( tmpExtent.width() / tmpXRes ); int tmpHeight = std::round( tmpExtent.height() / tmpYRes ); tmpXRes = tmpExtent.width() / tmpWidth; tmpYRes = tmpExtent.height() / tmpHeight; QgsDebugMsgLevel( QString( "Reading smaller block tmpWidth = %1 height = %2" ).arg( tmpWidth ).arg( tmpHeight ), 4 ); QgsDebugMsgLevel( QString( "tmpExtent = %1" ).arg( tmpExtent.toString() ), 4 ); QgsRasterBlock *tmpBlock = new QgsRasterBlock( dataType( bandNo ), tmpWidth, tmpHeight ); if ( sourceHasNoDataValue( bandNo ) && useSourceNoDataValue( bandNo ) ) { tmpBlock->setNoDataValue( sourceNoDataValue( bandNo ) ); } readBlock( bandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->bits(), feedback ); int pixelSize = dataTypeSize( bandNo ); double xMin = boundingBox.xMinimum(); double yMax = boundingBox.yMaximum(); double tmpXMin = tmpExtent.xMinimum(); double tmpYMax = tmpExtent.yMaximum(); for ( int row = fromRow; row <= toRow; row++ ) { double y = yMax - ( row + 0.5 ) * yRes; int tmpRow = std::floor( ( tmpYMax - y ) / tmpYRes ); for ( int col = fromCol; col <= toCol; col++ ) { double x = xMin + ( col + 0.5 ) * xRes; int tmpCol = std::floor( ( x - tmpXMin ) / tmpXRes ); if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth ) { QgsDebugMsg( "Source row or column limits out of range" ); block->setIsNoData(); // so that the problem becomes obvious and fixed delete tmpBlock; return block; } qgssize tmpIndex = static_cast< qgssize >( tmpRow ) * static_cast< qgssize >( tmpWidth ) + tmpCol; qgssize index = row * static_cast< qgssize >( width ) + col; char *tmpBits = tmpBlock->bits( tmpIndex ); char *bits = block->bits( index ); if ( !tmpBits ) { QgsDebugMsg( QString( "Cannot get input block data tmpRow = %1 tmpCol = %2 tmpIndex = %3." ).arg( tmpRow ).arg( tmpCol ).arg( tmpIndex ) ); continue; } if ( !bits ) { QgsDebugMsg( "Cannot set output block data." ); continue; } memcpy( bits, tmpBits, pixelSize ); } } delete tmpBlock; } else { readBlock( bandNo, boundingBox, width, height, block->bits(), feedback ); } // apply scale and offset block->applyScaleOffset( bandScale( bandNo ), bandOffset( bandNo ) ); // apply user no data values block->applyNoDataValues( userNoDataValues( bandNo ) ); return block; }
QgsRasterBlock* QgsSingleBandGrayRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height ) { Q_UNUSED( bandNo ); QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { return outputBlock; } QgsRasterBlock *inputBlock = mInput->block( mGrayBand, extent, width, height ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } QgsRasterBlock *alphaBlock = 0; if ( mAlphaBand > 0 && mGrayBand != mAlphaBand ) { alphaBlock = mInput->block( mAlphaBand, extent, width, height ); if ( !alphaBlock || alphaBlock->isEmpty() ) { // TODO: better to render without alpha delete inputBlock; delete alphaBlock; return outputBlock; } } else if ( mAlphaBand > 0 ) { alphaBlock = inputBlock; } if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; delete alphaBlock; return outputBlock; } QRgb myDefaultColor = NODATA_COLOR; for ( qgssize i = 0; i < ( qgssize )width*height; i++ ) { if ( inputBlock->isNoData( i ) ) { outputBlock->setColor( i, myDefaultColor ); continue; } double grayVal = inputBlock->value( i ); double currentAlpha = mOpacity; if ( mRasterTransparency ) { currentAlpha = mRasterTransparency->alphaValue( grayVal, mOpacity * 255 ) / 255.0; } if ( mAlphaBand > 0 ) { currentAlpha *= alphaBlock->value( i ) / 255.0; } if ( mContrastEnhancement ) { if ( !mContrastEnhancement->isValueInDisplayableRange( grayVal ) ) { outputBlock->setColor( i, myDefaultColor ); continue; } grayVal = mContrastEnhancement->enhanceContrast( grayVal ); } if ( mGradient == WhiteToBlack ) { grayVal = 255 - grayVal; } if ( qgsDoubleNear( currentAlpha, 1.0 ) ) { outputBlock->setColor( i, qRgba( grayVal, grayVal, grayVal, 255 ) ); } else { outputBlock->setColor( i, qRgba( currentAlpha * grayVal, currentAlpha * grayVal, currentAlpha * grayVal, currentAlpha * 255 ) ); } } delete inputBlock; if ( mAlphaBand > 0 && mGrayBand != mAlphaBand ) { delete alphaBlock; } return outputBlock; }
QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback ) { Q_UNUSED( bandNo ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { QgsDebugMsg( "No input raster!" ); return outputBlock; } QgsRasterBlock *inputBlock = mInput->block( mBand, extent, width, height, feedback ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } QgsRasterBlock *alphaBlock = nullptr; if ( mAlphaBand > 0 && mBand != mAlphaBand ) { alphaBlock = mInput->block( mAlphaBand, extent, width, height, feedback ); if ( !alphaBlock || alphaBlock->isEmpty() ) { // TODO: better to render without alpha delete inputBlock; delete alphaBlock; return outputBlock; } } else if ( mAlphaBand > 0 ) { alphaBlock = inputBlock; } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; delete alphaBlock; return outputBlock; } double cellXSize = extent.width() / double( width ); double cellYSize = extent.height() / double( height ); double zenithRad = qMax( 0.0, 90 - mLightAngle ) * M_PI / 180.0; double azimuthRad = -1 * mLightAzimuth * M_PI / 180.0; double cosZenithRad = cos( zenithRad ); double sinZenithRad = sin( zenithRad ); // Multi direction hillshade: http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf double angle0Rad = ( -1 * mLightAzimuth - 45 - 45 * 0.5 ) * M_PI / 180.0; double angle1Rad = ( -1 * mLightAzimuth - 45 * 0.5 ) * M_PI / 180.0; double angle2Rad = ( -1 * mLightAzimuth + 45 * 0.5 ) * M_PI / 180.0; double angle3Rad = ( -1 * mLightAzimuth + 45 + 45 * 0.5 ) * M_PI / 180.0; QRgb myDefaultColor = NODATA_COLOR; for ( qgssize i = 0; i < ( qgssize )height; i++ ) { for ( qgssize j = 0; j < ( qgssize )width; j++ ) { if ( inputBlock->isNoData( i, j ) ) { outputBlock->setColor( i, j, myDefaultColor ); continue; } qgssize iUp, iDown, jLeft, jRight; if ( i == 0 ) { iUp = i; iDown = i + 1; } else if ( i < ( qgssize )height - 1 ) { iUp = i - 1; iDown = i + 1; } else { iUp = i - 1; iDown = i; } if ( j == 0 ) { jLeft = j; jRight = j + 1; } else if ( j < ( qgssize )width - 1 ) { jLeft = j - 1; jRight = j + 1; } else { jLeft = j - 1; jRight = j; } double x11; double x21; double x31; double x12; double x22; // Working cell double x32; double x13; double x23; double x33; // This is center cell. It is not nodata. Use this in place of nodata neighbors x22 = inputBlock->value( i, j ); x11 = inputBlock->isNoData( iUp, jLeft ) ? x22 : inputBlock->value( iUp, jLeft ); x21 = inputBlock->isNoData( i, jLeft ) ? x22 : inputBlock->value( i, jLeft ); x31 = inputBlock->isNoData( iDown, jLeft ) ? x22 : inputBlock->value( iDown, jLeft ); x12 = inputBlock->isNoData( iUp, j ) ? x22 : inputBlock->value( iUp, j ); // x22 x32 = inputBlock->isNoData( iDown, j ) ? x22 : inputBlock->value( iDown, j ); x13 = inputBlock->isNoData( iUp, jRight ) ? x22 : inputBlock->value( iUp, jRight ); x23 = inputBlock->isNoData( i, jRight ) ? x22 : inputBlock->value( i, jRight ); x33 = inputBlock->isNoData( iDown, jRight ) ? x22 : inputBlock->value( iDown, jRight ); double derX = calcFirstDerX( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellXSize ); double derY = calcFirstDerY( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellYSize ); double slopeRad = atan( mZFactor * sqrt( derX * derX + derY * derY ) ); double aspectRad = atan2( derX, -derY ); double grayValue; if ( !mMultiDirectional ) { // Standard single direction hillshade grayValue = qBound( 0.0, 255.0 * ( cosZenithRad * cos( slopeRad ) + sinZenithRad * sin( slopeRad ) * cos( azimuthRad - aspectRad ) ) , 255.0 ); } else { // Weighted multi direction as in http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf double weight0 = sin( aspectRad - angle0Rad ); double weight1 = sin( aspectRad - angle1Rad ); double weight2 = sin( aspectRad - angle2Rad ); double weight3 = sin( aspectRad - angle3Rad ); weight0 = weight0 * weight0; weight1 = weight1 * weight1; weight2 = weight2 * weight2; weight3 = weight3 * weight3; double cosSlope = cosZenithRad * cos( slopeRad ); double sinSlope = sinZenithRad * sin( slopeRad ); double color0 = cosSlope + sinSlope * cos( angle0Rad - aspectRad ) ; double color1 = cosSlope + sinSlope * cos( angle1Rad - aspectRad ) ; double color2 = cosSlope + sinSlope * cos( angle2Rad - aspectRad ) ; double color3 = cosSlope + sinSlope * cos( angle3Rad - aspectRad ) ; grayValue = qBound( 0.0, 255 * ( weight0 * color0 + weight1 * color1 + weight2 * color2 + weight3 * color3 ) * 0.5, 255.0 ); } double currentAlpha = mOpacity; if ( mRasterTransparency ) { currentAlpha = mRasterTransparency->alphaValue( x22, mOpacity * 255 ) / 255.0; } if ( mAlphaBand > 0 ) { currentAlpha *= alphaBlock->value( i ) / 255.0; } if ( qgsDoubleNear( currentAlpha, 1.0 ) ) { outputBlock->setColor( i, j, qRgba( grayValue, grayValue, grayValue, 255 ) ); } else { outputBlock->setColor( i, j, qRgba( currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * grayValue, currentAlpha * 255 ) ); } } } delete inputBlock; if ( mAlphaBand > 0 && mBand != mAlphaBand ) { delete alphaBlock; } return outputBlock; }
QgsRasterBlock* QgsSingleBandPseudoColorRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback ) { Q_UNUSED( bandNo ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput || !mShader ) { return outputBlock; } QgsRasterBlock *inputBlock = mInput->block( mBand, extent, width, height, feedback ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } //rendering is faster without considering user-defined transparency bool hasTransparency = usesTransparency(); QgsRasterBlock *alphaBlock = nullptr; if ( mAlphaBand > 0 && mAlphaBand != mBand ) { alphaBlock = mInput->block( mAlphaBand, extent, width, height, feedback ); if ( !alphaBlock || alphaBlock->isEmpty() ) { delete inputBlock; delete alphaBlock; return outputBlock; } } else if ( mAlphaBand == mBand ) { alphaBlock = inputBlock; } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; delete alphaBlock; return outputBlock; } QRgb myDefaultColor = NODATA_COLOR; for ( qgssize i = 0; i < ( qgssize )width*height; i++ ) { if ( inputBlock->isNoData( i ) ) { outputBlock->setColor( i, myDefaultColor ); continue; } double val = inputBlock->value( i ); int red, green, blue, alpha; if ( !mShader->shade( val, &red, &green, &blue, &alpha ) ) { outputBlock->setColor( i, myDefaultColor ); continue; } if ( alpha < 255 ) { // Working with premultiplied colors, so multiply values by alpha red *= ( alpha / 255.0 ); blue *= ( alpha / 255.0 ); green *= ( alpha / 255.0 ); } if ( !hasTransparency ) { outputBlock->setColor( i, qRgba( red, green, blue, alpha ) ); } else { //opacity double currentOpacity = mOpacity; if ( mRasterTransparency ) { currentOpacity = mRasterTransparency->alphaValue( val, mOpacity * 255 ) / 255.0; } if ( mAlphaBand > 0 ) { currentOpacity *= alphaBlock->value( i ) / 255.0; } outputBlock->setColor( i, qRgba( currentOpacity * red, currentOpacity * green, currentOpacity * blue, currentOpacity * alpha ) ); } } delete inputBlock; if ( mAlphaBand > 0 && mBand != mAlphaBand ) { delete alphaBlock; } return outputBlock; }
QgsRasterBlock* QgsRasterResampleFilter::block2( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback *feedback ) { Q_UNUSED( bandNo ); QgsDebugMsg( QString( "width = %1 height = %2 extent = %3" ).arg( width ).arg( height ).arg( extent.toString() ) ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) return outputBlock; double oversampling = 1.0; // approximate global oversampling factor if ( mZoomedInResampler || mZoomedOutResampler ) { QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider*>( mInput->srcInput() ); if ( provider && ( provider->capabilities() & QgsRasterDataProvider::Size ) ) { double xRes = extent.width() / width; double providerXRes = provider->extent().width() / provider->xSize(); double pixelRatio = xRes / providerXRes; oversampling = ( pixelRatio > mMaxOversampling ) ? mMaxOversampling : pixelRatio; QgsDebugMsg( QString( "xRes = %1 providerXRes = %2 pixelRatio = %3 oversampling = %4" ).arg( xRes ).arg( providerXRes ).arg( pixelRatio ).arg( oversampling ) ); } else { // We don't know exact data source resolution (WMS) so we expect that // server data have higher resolution (which is not always true) and use // mMaxOversampling oversampling = mMaxOversampling; } } QgsDebugMsg( QString( "oversampling %1" ).arg( oversampling ) ); int bandNumber = 1; // Do no oversampling if no resampler for zoomed in / zoomed out (nearest neighbour) // We do mZoomedInResampler if oversampling == 1 (otherwise for example reprojected // zoom in rasters are never resampled because projector limits resolution. if ((( oversampling < 1.0 || qgsDoubleNear( oversampling, 1.0 ) ) && !mZoomedInResampler ) || ( oversampling > 1.0 && !mZoomedOutResampler ) ) { QgsDebugMsg( "No oversampling." ); delete outputBlock; return mInput->block2( bandNumber, extent, width, height, feedback ); } //effective oversampling factors are different to global one because of rounding double oversamplingX = (( double )width * oversampling ) / width; double oversamplingY = (( double )height * oversampling ) / height; // TODO: we must also increase the extent to get correct result on borders of parts int resWidth = width * oversamplingX; int resHeight = height * oversamplingY; QgsRasterBlock *inputBlock = mInput->block2( bandNumber, extent, resWidth, resHeight, feedback ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; return outputBlock; } //resample image QImage img = inputBlock->image(); QImage dstImg = QImage( width, height, QImage::Format_ARGB32_Premultiplied ); if ( mZoomedInResampler && ( oversamplingX < 1.0 || qgsDoubleNear( oversampling, 1.0 ) ) ) { QgsDebugMsg( "zoomed in resampling" ); mZoomedInResampler->resample( img, dstImg ); } else if ( mZoomedOutResampler && oversamplingX > 1.0 ) { QgsDebugMsg( "zoomed out resampling" ); mZoomedOutResampler->resample( img, dstImg ); } else { // Should not happen QgsDebugMsg( "Unexpected resampling" ); dstImg = img.scaled( width, height ); } outputBlock->setImage( &dstImg ); delete inputBlock; return outputBlock; // No resampling }
QgsRasterBlock * QgsPalettedRasterRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height ) { QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { return outputBlock; } QgsRasterBlock *inputBlock = mInput->block( bandNo, extent, width, height ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } double currentOpacity = mOpacity; //rendering is faster without considering user-defined transparency bool hasTransparency = usesTransparency(); QgsRasterBlock *alphaBlock = 0; if ( mAlphaBand > 0 && mAlphaBand != mBand ) { alphaBlock = mInput->block( mAlphaBand, extent, width, height ); if ( !alphaBlock || alphaBlock->isEmpty() ) { delete inputBlock; delete alphaBlock; return outputBlock; } } else if ( mAlphaBand == mBand ) { alphaBlock = inputBlock; } if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; delete alphaBlock; return outputBlock; } QRgb myDefaultColor = NODATA_COLOR; //use direct data access instead of QgsRasterBlock::setValue //because of performance unsigned int* outputData = ( unsigned int* )( outputBlock->bits() ); qgssize rasterSize = ( qgssize )width * height; for ( qgssize i = 0; i < rasterSize; ++i ) { if ( inputBlock->isNoData( i ) ) { outputData[i] = myDefaultColor; continue; } int val = ( int ) inputBlock->value( i ); if ( !hasTransparency ) { outputData[i] = mColors[val]; } else { currentOpacity = mOpacity; if ( mRasterTransparency ) { currentOpacity = mRasterTransparency->alphaValue( val, mOpacity * 255 ) / 255.0; } if ( mAlphaBand > 0 ) { currentOpacity *= alphaBlock->value( i ) / 255.0; } QColor currentColor = QColor( mColors[val] ); outputData[i] = qRgba( currentOpacity * currentColor.red(), currentOpacity * currentColor.green(), currentOpacity * currentColor.blue(), currentOpacity * 255 ); } } delete inputBlock; if ( mAlphaBand > 0 && mBand != mAlphaBand ) { delete alphaBlock; } return outputBlock; }
QgsRasterBlock * QgsRasterResampleFilter::block( int bandNo, QgsRectangle const & extent, int width, int height ) { Q_UNUSED( bandNo ); QgsDebugMsg( "Entered" ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) return outputBlock; double oversampling = 1.0; // approximate global oversampling factor if ( mZoomedInResampler || mZoomedOutResampler ) { QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider*>( mInput->srcInput() ); // Do not oversample if data source does not have fixed resolution (WMS) if ( provider && ( provider->capabilities() & QgsRasterDataProvider::ExactResolution ) ) { double xRes = extent.width() / width; double providerXRes = provider->extent().width() / provider->xSize(); double pixelRatio = xRes / providerXRes; oversampling = ( pixelRatio > mMaxOversampling ) ? mMaxOversampling : pixelRatio; QgsDebugMsg( QString( "xRes = %1 providerXRes = %2 pixelRatio = %3 oversampling = %4" ).arg( xRes ).arg( providerXRes ).arg( pixelRatio ).arg( oversampling ) ); } } //set oversampling back to 1.0 if no resampler for zoomed in / zoomed out (nearest neighbour) if (( oversampling < 1.0 && !mZoomedInResampler ) || ( oversampling > 1.0 && !mZoomedOutResampler ) ) { oversampling = 1.0; } QgsDebugMsg( QString( "oversampling %1" ).arg( oversampling ) ); //effective oversampling factors are different to global one because of rounding double oversamplingX = (( double )width * oversampling ) / width; double oversamplingY = (( double )height * oversampling ) / height; // TODO: we must also increase the extent to get correct result on borders of parts int resWidth = width * oversamplingX; int resHeight = height * oversamplingY; // At moment we know that we read rendered image int bandNumber = 1; //void *rasterData = mInput->block( bandNumber, extent, resWidth, resHeight ); QgsRasterBlock *inputBlock = mInput->block( bandNumber, extent, resWidth, resHeight ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } if ( doubleNear( oversamplingX, 1.0 ) || doubleNear( oversamplingY, 1.0 ) ) { QgsDebugMsg( "No oversampling." ); delete outputBlock; return inputBlock; } if ( !outputBlock->reset( QgsRasterBlock::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; return outputBlock; } //resample image QImage img = inputBlock->image(); QImage dstImg = QImage( width, height, QImage::Format_ARGB32_Premultiplied ); if ( mZoomedInResampler && oversamplingX < 1.0 ) { QgsDebugMsg( "zoomed in resampling" ); mZoomedInResampler->resample( img, dstImg ); } else if ( mZoomedOutResampler && oversamplingX > 1.0 ) { QgsDebugMsg( "zoomed out resampling" ); mZoomedOutResampler->resample( img, dstImg ); } else { // Should not happen QgsDebugMsg( "Unexpected resampling" ); dstImg = img.scaled( width, height ); } outputBlock->setImage( &dstImg ); delete inputBlock; return outputBlock; // No resampling }
QgsRasterBlock * QgsRasterProjector::block( int bandNo, QgsRectangle const & extent, int width, int height ) { QgsDebugMsg( QString( "extent:\n%1" ).arg( extent.toString() ) ); QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) ); if ( !mInput ) { QgsDebugMsg( "Input not set" ); return new QgsRasterBlock(); } if ( ! mSrcCRS.isValid() || ! mDestCRS.isValid() || mSrcCRS == mDestCRS ) { QgsDebugMsg( "No projection necessary" ); return mInput->block( bandNo, extent, width, height ); } mDestExtent = extent; mDestRows = height; mDestCols = width; calc(); QgsDebugMsg( QString( "srcExtent:\n%1" ).arg( srcExtent().toString() ) ); QgsDebugMsg( QString( "srcCols = %1 srcRows = %2" ).arg( srcCols() ).arg( srcRows() ) ); // If we zoom out too much, projector srcRows / srcCols maybe 0, which can cause problems in providers if ( srcRows() <= 0 || srcCols() <= 0 ) { QgsDebugMsg( "Zero srcRows or srcCols" ); return new QgsRasterBlock(); } QgsRasterBlock *inputBlock = mInput->block( bandNo, srcExtent(), srcCols(), srcRows() ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return new QgsRasterBlock(); } qgssize pixelSize = QgsRasterBlock::typeSize( mInput->dataType( bandNo ) ); QgsRasterBlock *outputBlock; if ( inputBlock->hasNoDataValue() ) { outputBlock = new QgsRasterBlock( inputBlock->dataType(), width, height, inputBlock->noDataValue() ); } else { outputBlock = new QgsRasterBlock( inputBlock->dataType(), width, height ); } if ( !outputBlock->isValid() ) { QgsDebugMsg( "Cannot create block" ); delete inputBlock; return outputBlock; } // set output to no data, it should be fast outputBlock->setIsNoData(); // No data: because isNoData()/setIsNoData() is slow with respect to simple memcpy, // we use if only if necessary: // 1) no data value exists (numerical) -> memcpy, not necessary isNoData()/setIsNoData() // 2) no data value does not exist but it may contain no data (numerical no data bitmap) // -> must use isNoData()/setIsNoData() // 3) no data are not used (no no data value, no no data bitmap) -> simple memcpy // 4) image - simple memcpy // To copy no data values stored in bitmaps we have to use isNoData()/setIsNoData(), // we cannot fill output block with no data because we use memcpy for data, not setValue(). bool doNoData = !QgsRasterBlock::typeIsNumeric( inputBlock->dataType() ) && inputBlock->hasNoData() && !inputBlock->hasNoDataValue(); const QgsCoordinateTransform* inverseCt = nullptr; if ( !mApproximate ) { inverseCt = QgsCoordinateTransformCache::instance()->transform( mDestCRS.authid(), mSrcCRS.authid(), mDestDatumTransform, mSrcDatumTransform ); } outputBlock->setIsNoData(); int srcRow, srcCol; for ( int i = 0; i < height; ++i ) { for ( int j = 0; j < width; ++j ) { bool inside = srcRowCol( i, j, &srcRow, &srcCol, inverseCt ); if ( !inside ) continue; // we have everything set to no data qgssize srcIndex = ( qgssize )srcRow * mSrcCols + srcCol; QgsDebugMsgLevel( QString( "row = %1 col = %2 srcRow = %3 srcCol = %4" ).arg( i ).arg( j ).arg( srcRow ).arg( srcCol ), 5 ); // isNoData() may be slow so we check doNoData first if ( doNoData && inputBlock->isNoData( srcRow, srcCol ) ) { outputBlock->setIsNoData( i, j ); continue; } qgssize destIndex = ( qgssize )i * width + j; char *srcBits = inputBlock->bits( srcIndex ); char *destBits = outputBlock->bits( destIndex ); if ( !srcBits ) { QgsDebugMsg( QString( "Cannot get input block data: row = %1 col = %2" ).arg( i ).arg( j ) ); continue; } if ( !destBits ) { QgsDebugMsg( QString( "Cannot set output block data: srcRow = %1 srcCol = %2" ).arg( srcRow ).arg( srcCol ) ); continue; } memcpy( destBits, srcBits, pixelSize ); outputBlock->setIsData( i, j ); } } delete inputBlock; return outputBlock; }
int QgsRasterCalculator::processCalculation( QProgressDialog* p ) { //prepare search string / tree QString errorString; QgsRasterCalcNode* calcNode = QgsRasterCalcNode::parseRasterCalcString( mFormulaString, errorString ); if ( !calcNode ) { //error return static_cast<int>( ParserError ); } QMap< QString, QgsRasterBlock* > inputBlocks; QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin(); for ( ; it != mRasterEntries.constEnd(); ++it ) { if ( !it->raster ) // no raster layer in entry { delete calcNode; qDeleteAll( inputBlocks ); return static_cast< int >( InputLayerError ); } QgsRasterBlock* block = nullptr; // if crs transform needed if ( it->raster->crs() != mOutputCrs ) { QgsRasterProjector proj; proj.setCRS( it->raster->crs(), mOutputCrs ); proj.setInput( it->raster->dataProvider() ); proj.setPrecision( QgsRasterProjector::Exact ); block = proj.block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ); } else { block = it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ); } if ( block->isEmpty() ) { delete block; delete calcNode; qDeleteAll( inputBlocks ); return static_cast<int>( MemoryError ); } inputBlocks.insert( it->ref, block ); } //open output dataset for writing GDALDriverH outputDriver = openOutputDriver(); if ( !outputDriver ) { return static_cast< int >( CreateOutputError ); } GDALDatasetH outputDataset = openOutputFile( outputDriver ); GDALSetProjection( outputDataset, mOutputCrs.toWkt().toLocal8Bit().data() ); GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset, 1 ); float outputNodataValue = -FLT_MAX; GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue ); if ( p ) { p->setMaximum( mNumOutputRows ); } QgsRasterMatrix resultMatrix; resultMatrix.setNodataValue( outputNodataValue ); //read / write line by line for ( int i = 0; i < mNumOutputRows; ++i ) { if ( p ) { p->setValue( i ); } if ( p && p->wasCanceled() ) { break; } if ( calcNode->calculate( inputBlocks, resultMatrix, i ) ) { bool resultIsNumber = resultMatrix.isNumber(); float* calcData = new float[mNumOutputColumns]; for ( int j = 0; j < mNumOutputColumns; ++j ) { calcData[j] = ( float )( resultIsNumber ? resultMatrix.number() : resultMatrix.data()[j] ); } //write scanline to the dataset if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None ) { qWarning( "RasterIO error!" ); } delete[] calcData; } } if ( p ) { p->setValue( mNumOutputRows ); } //close datasets and release memory delete calcNode; qDeleteAll( inputBlocks ); inputBlocks.clear(); if ( p && p->wasCanceled() ) { //delete the dataset without closing (because it is faster) GDALDeleteDataset( outputDriver, TO8F( mOutputFile ) ); return static_cast< int >( Cancelled ); } GDALClose( outputDataset ); return static_cast< int >( Success ); }
QgsRasterBlock * QgsHueSaturationFilter::block( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback ) { Q_UNUSED( bandNo ); QgsDebugMsgLevel( QString( "width = %1 height = %2 extent = %3" ).arg( width ).arg( height ).arg( extent.toString() ), 4 ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { return outputBlock; } // At this moment we know that we read rendered image int bandNumber = 1; QgsRasterBlock *inputBlock = mInput->block( bandNumber, extent, width, height, feedback ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } if ( mSaturation == 0 && mGrayscaleMode == GrayscaleOff && !mColorizeOn ) { QgsDebugMsgLevel( "No hue/saturation change.", 4 ); delete outputBlock; return inputBlock; } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; return outputBlock; } // adjust image QRgb myNoDataColor = qRgba( 0, 0, 0, 0 ); QRgb myRgb; QColor myColor; int h, s, l; int r, g, b, alpha; double alphaFactor = 1.0; for ( qgssize i = 0; i < ( qgssize )width*height; i++ ) { if ( inputBlock->color( i ) == myNoDataColor ) { outputBlock->setColor( i, myNoDataColor ); continue; } myRgb = inputBlock->color( i ); myColor = QColor( myRgb ); // Alpha must be taken from QRgb, since conversion from QRgb->QColor loses alpha alpha = qAlpha( myRgb ); if ( alpha == 0 ) { // totally transparent, no changes required outputBlock->setColor( i, myRgb ); continue; } // Get rgb for color myColor.getRgb( &r, &g, &b ); if ( alpha != 255 ) { // Semi-transparent pixel. We need to adjust the colors since we are using Qgis::ARGB32_Premultiplied // and color values have been premultiplied by alpha alphaFactor = alpha / 255.; r /= alphaFactor; g /= alphaFactor; b /= alphaFactor; myColor = QColor::fromRgb( r, g, b ); } myColor.getHsl( &h, &s, &l ); // Changing saturation? if (( mGrayscaleMode != GrayscaleOff ) || ( mSaturationScale != 1 ) ) { processSaturation( r, g, b, h, s, l ); } // Colorizing? if ( mColorizeOn ) { processColorization( r, g, b, h, s, l ); } // Convert back to rgb if ( alpha != 255 ) { // Transparent pixel, need to premultiply color components r *= alphaFactor; g *= alphaFactor; b *= alphaFactor; } outputBlock->setColor( i, qRgba( r, g, b, alpha ) ); } delete inputBlock; return outputBlock; }
QgsRasterBlock * QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle const & extent, int width, int height ) { Q_UNUSED( bandNo ); QgsDebugMsg( QString( "width = %1 height = %2 extent = %3" ).arg( width ).arg( height ).arg( extent.toString() ) ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { return outputBlock; } // At this moment we know that we read rendered image int bandNumber = 1; QgsRasterBlock *inputBlock = mInput->block( bandNumber, extent, width, height ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } if ( mBrightness == 0 && mContrast == 0 ) { QgsDebugMsg( "No brightness changes." ); delete outputBlock; return inputBlock; } if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; return outputBlock; } // adjust image QRgb myNoDataColor = qRgba( 0, 0, 0, 0 ); QRgb myColor; int r, g, b, alpha; double f = qPow(( mContrast + 100 ) / 100.0, 2 ); for ( qgssize i = 0; i < ( qgssize )width*height; i++ ) { if ( inputBlock->color( i ) == myNoDataColor ) { outputBlock->setColor( i, myNoDataColor ); continue; } myColor = inputBlock->color( i ); alpha = qAlpha( myColor ); r = adjustColorComponent( qRed( myColor ), alpha, mBrightness, f ); g = adjustColorComponent( qGreen( myColor ), alpha, mBrightness, f ); b = adjustColorComponent( qBlue( myColor ), alpha, mBrightness, f ); outputBlock->setColor( i, qRgba( r, g, b, alpha ) ); } delete inputBlock; return outputBlock; }
QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &extent, int width, int height ) { Q_UNUSED( bandNo ); QgsRasterBlock *outputBlock = new QgsRasterBlock(); if ( !mInput ) { QgsDebugMsg( "No input raster!" ); return outputBlock; } QgsRasterBlock *inputBlock = mInput->block( mBand, extent, width, height ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); delete inputBlock; return outputBlock; } if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) ) { delete inputBlock; return outputBlock; } double cellXSize = extent.width() / double( width ); double cellYSize = extent.height() / double( height ); double zenithRad = qMax( 0.0, 90 - mLightAngle ) * M_PI / 180.0; double azimuthRad = -1 * mLightAzimuth * M_PI / 180.0; double cosZenithRad = cos( zenithRad ); double sinZenithRad = sin( zenithRad ); QRgb myDefaultColor = NODATA_COLOR; for ( qgssize i = 0; i < ( qgssize )height; i++ ) { for ( qgssize j = 0; j < ( qgssize )width; j++ ) { if ( inputBlock->isNoData( i, j ) ) { outputBlock->setColor( i, j, myDefaultColor ); continue; } qgssize iUp, iDown, jLeft, jRight; if ( i == 0 ) { iUp = i; iDown = i + 1; } else if ( i < ( qgssize )height - 1 ) { iUp = i - 1; iDown = i + 1; } else { iUp = i - 1; iDown = i; } if ( j == 0 ) { jLeft = j; jRight = j + 1; } else if ( j < ( qgssize )width - 1 ) { jLeft = j - 1; jRight = j + 1; } else { jLeft = j - 1; jRight = j; } double x11; double x21; double x31; double x12; double x22; // Working cell double x32; double x13; double x23; double x33; // This is center cell. It is not nodata. Use this in place of nodata neighbors x22 = inputBlock->value( i, j ); x11 = inputBlock->isNoData( iUp, jLeft ) ? x22 : inputBlock->value( iUp, jLeft ); x21 = inputBlock->isNoData( i, jLeft ) ? x22 : inputBlock->value( i, jLeft ); x31 = inputBlock->isNoData( iDown, jLeft ) ? x22 : inputBlock->value( iDown, jLeft ); x12 = inputBlock->isNoData( iUp, j ) ? x22 : inputBlock->value( iUp, j ); // x22 x32 = inputBlock->isNoData( iDown, j ) ? x22 : inputBlock->value( iDown, j ); x13 = inputBlock->isNoData( iUp, jRight ) ? x22 : inputBlock->value( iUp, jRight ); x23 = inputBlock->isNoData( i, jRight ) ? x22 : inputBlock->value( i, jRight ); x33 = inputBlock->isNoData( iDown, jRight ) ? x22 : inputBlock->value( iDown, jRight ); double derX = calcFirstDerX( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellXSize ); double derY = calcFirstDerY( x11, x21, x31, x12, x22, x32, x13, x23, x33, cellYSize ); double slope_rad = atan( mZFactor * sqrt( derX * derX + derY * derY ) ); double aspectRad = atan2( derX, -derY ); double colorvalue = qBound( 0.0, 255.0 * (( cosZenithRad * cos( slope_rad ) ) + ( sinZenithRad * sin( slope_rad ) * cos( azimuthRad - aspectRad ) ) ), 255.0 ); outputBlock->setColor( i, j, qRgb( colorvalue, colorvalue, colorvalue ) ); } } return outputBlock; }
QgsRasterBlock * QgsRasterDataProvider::block( int theBandNo, QgsRectangle const & theExtent, int theWidth, int theHeight ) { QgsDebugMsg( QString( "theBandNo = %1 theWidth = %2 theHeight = %3" ).arg( theBandNo ).arg( theWidth ).arg( theHeight ) ); QgsDebugMsg( QString( "theExtent = %1" ).arg( theExtent.toString() ) ); QgsRasterBlock *block = new QgsRasterBlock( dataType( theBandNo ), theWidth, theHeight, noDataValue( theBandNo ) ); if ( block->isEmpty() ) { QgsDebugMsg( "Couldn't create raster block" ); return block; } // Read necessary extent only QgsRectangle tmpExtent = extent().intersect( &theExtent ); if ( tmpExtent.isEmpty() ) { QgsDebugMsg( "Extent outside provider extent" ); block->setIsNoData(); return block; } double xRes = theExtent.width() / theWidth; double yRes = theExtent.height() / theHeight; double tmpXRes, tmpYRes; double providerXRes = 0; double providerYRes = 0; if ( capabilities() & ExactResolution ) { providerXRes = extent().width() / xSize(); providerYRes = extent().height() / ySize(); tmpXRes = qMax( providerXRes, xRes ); tmpYRes = qMax( providerYRes, yRes ); if ( doubleNear( tmpXRes, xRes ) ) tmpXRes = xRes; if ( doubleNear( tmpYRes, yRes ) ) tmpYRes = yRes; } else { tmpXRes = xRes; tmpYRes = yRes; } if ( tmpExtent != theExtent || tmpXRes > xRes || tmpYRes > yRes ) { // Read smaller extent or lower resolution // Calculate row/col limits (before tmpExtent is aligned) int fromRow = qRound(( theExtent.yMaximum() - tmpExtent.yMaximum() ) / yRes ); int toRow = qRound(( theExtent.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1; int fromCol = qRound(( tmpExtent.xMinimum() - theExtent.xMinimum() ) / xRes ) ; int toCol = qRound(( tmpExtent.xMaximum() - theExtent.xMinimum() ) / xRes ) - 1; QgsDebugMsg( QString( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ) ); if ( fromRow < 0 || fromRow >= theHeight || toRow < 0 || toRow >= theHeight || fromCol < 0 || fromCol >= theWidth || toCol < 0 || toCol >= theWidth ) { // Should not happen QgsDebugMsg( "Row or column limits out of range" ); return block; } // If lower source resolution is used, the extent must beS aligned to original // resolution to avoid possible shift due to resampling if ( tmpXRes > xRes ) { int col = floor(( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes ); col = ceil(( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes ); } if ( tmpYRes > yRes ) { int row = floor(( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes ); tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes ); row = ceil(( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes ); tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes ); } int tmpWidth = qRound( tmpExtent.width() / tmpXRes ); int tmpHeight = qRound( tmpExtent.height() / tmpYRes ); tmpXRes = tmpExtent.width() / tmpWidth; tmpYRes = tmpExtent.height() / tmpHeight; QgsDebugMsg( QString( "Reading smaller block tmpWidth = %1 theHeight = %2" ).arg( tmpWidth ).arg( tmpHeight ) ); QgsDebugMsg( QString( "tmpExtent = %1" ).arg( tmpExtent.toString() ) ); block->setIsNoData(); QgsRasterBlock *tmpBlock = new QgsRasterBlock( dataType( theBandNo ), tmpWidth, tmpHeight, noDataValue( theBandNo ) ); readBlock( theBandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->data() ); int pixelSize = dataTypeSize( theBandNo ); double xMin = theExtent.xMinimum(); double yMax = theExtent.yMaximum(); double tmpXMin = tmpExtent.xMinimum(); double tmpYMax = tmpExtent.yMaximum(); for ( int row = fromRow; row <= toRow; row++ ) { double y = yMax - ( row + 0.5 ) * yRes; int tmpRow = floor(( tmpYMax - y ) / tmpYRes ); for ( int col = fromCol; col <= toCol; col++ ) { double x = xMin + ( col + 0.5 ) * xRes; int tmpCol = floor(( x - tmpXMin ) / tmpXRes ); if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth ) { QgsDebugMsg( "Source row or column limits out of range" ); block->setIsNoData(); // so that the problem becomes obvious and fixed delete tmpBlock; return block; } size_t tmpIndex = tmpRow * tmpWidth + tmpCol; size_t index = row * theWidth + col; char *tmpBits = tmpBlock->bits( tmpIndex ); char *bits = block->bits( index ); if ( !tmpBits ) { QgsDebugMsg( QString( "Cannot get input block data tmpRow = %1 tmpCol = %2 tmpIndex = %3." ).arg( tmpRow ).arg( tmpCol ).arg( tmpIndex ) ); continue; } if ( !bits ) { QgsDebugMsg( "Cannot set output block data." ); continue; } memcpy( bits, tmpBits, pixelSize ); } } delete tmpBlock; } else { readBlock( theBandNo, theExtent, theWidth, theHeight, block->data() ); } // apply user no data values // TODO: there are other readBlock methods where no data are not applied block->applyNodataValues( userNoDataValue( theBandNo ) ); return block; }