QgsRasterBlock *QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback ) { Q_UNUSED( bandNo ) QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2 extent = %3" ).arg( width ).arg( height ).arg( extent.toString() ), 4 ); std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() ); if ( !mInput ) { return outputBlock.release(); } // At this moment we know that we read rendered image int bandNumber = 1; std::unique_ptr< QgsRasterBlock > inputBlock( mInput->block( bandNumber, extent, width, height, feedback ) ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( QStringLiteral( "No raster data!" ) ); return outputBlock.release(); } if ( mBrightness == 0 && mContrast == 0 ) { QgsDebugMsgLevel( QStringLiteral( "No brightness changes." ), 4 ); return inputBlock.release(); } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { return outputBlock.release(); } // adjust image QRgb myNoDataColor = qRgba( 0, 0, 0, 0 ); QRgb myColor; int r, g, b, alpha; double f = std::pow( ( 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 ) ); } return outputBlock.release(); }
QgsRasterBlock *QgsRasterProjector::block( int bandNo, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback ) { QgsDebugMsgLevel( QStringLiteral( "extent:\n%1" ).arg( extent.toString() ), 4 ); QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2" ).arg( width ).arg( height ), 4 ); if ( !mInput ) { QgsDebugMsgLevel( QStringLiteral( "Input not set" ), 4 ); return new QgsRasterBlock(); } if ( feedback && feedback->isCanceled() ) return new QgsRasterBlock(); if ( ! mSrcCRS.isValid() || ! mDestCRS.isValid() || mSrcCRS == mDestCRS ) { QgsDebugMsgLevel( QStringLiteral( "No projection necessary" ), 4 ); return mInput->block( bandNo, extent, width, height, feedback ); } QgsCoordinateTransform inverseCt( mDestCRS, mSrcCRS, mDestDatumTransform, mSrcDatumTransform ); ProjectorData pd( extent, width, height, mInput, inverseCt, mPrecision ); QgsDebugMsgLevel( QStringLiteral( "srcExtent:\n%1" ).arg( pd.srcExtent().toString() ), 4 ); QgsDebugMsgLevel( QStringLiteral( "srcCols = %1 srcRows = %2" ).arg( pd.srcCols() ).arg( pd.srcRows() ), 4 ); // If we zoom out too much, projector srcRows / srcCols maybe 0, which can cause problems in providers if ( pd.srcRows() <= 0 || pd.srcCols() <= 0 ) { QgsDebugMsgLevel( QStringLiteral( "Zero srcRows or srcCols" ), 4 ); return new QgsRasterBlock(); } std::unique_ptr< QgsRasterBlock > inputBlock( mInput->block( bandNo, pd.srcExtent(), pd.srcCols(), pd.srcRows(), feedback ) ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( QStringLiteral( "No raster data!" ) ); return new QgsRasterBlock(); } qgssize pixelSize = QgsRasterBlock::typeSize( mInput->dataType( bandNo ) ); std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock( inputBlock->dataType(), width, height ) ); if ( inputBlock->hasNoDataValue() ) { outputBlock->setNoDataValue( inputBlock->noDataValue() ); } if ( !outputBlock->isValid() ) { QgsDebugMsg( QStringLiteral( "Cannot create block" ) ); return outputBlock.release(); } // 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(); outputBlock->setIsNoData(); int srcRow, srcCol; for ( int i = 0; i < height; ++i ) { if ( feedback && feedback->isCanceled() ) break; for ( int j = 0; j < width; ++j ) { bool inside = pd.srcRowCol( i, j, &srcRow, &srcCol ); if ( !inside ) continue; // we have everything set to no data qgssize srcIndex = static_cast< qgssize >( srcRow ) * pd.srcCols() + srcCol; // isNoData() may be slow so we check doNoData first if ( doNoData && inputBlock->isNoData( srcRow, srcCol ) ) { outputBlock->setIsNoData( i, j ); continue; } qgssize destIndex = static_cast< qgssize >( i ) * width + j; char *srcBits = inputBlock->bits( srcIndex ); char *destBits = outputBlock->bits( destIndex ); if ( !srcBits ) { // QgsDebugMsg( QStringLiteral( "Cannot get input block data: row = %1 col = %2" ).arg( i ).arg( j ) ); continue; } if ( !destBits ) { // QgsDebugMsg( QStringLiteral( "Cannot set output block data: srcRow = %1 srcCol = %2" ).arg( srcRow ).arg( srcCol ) ); continue; } memcpy( destBits, srcBits, pixelSize ); outputBlock->setIsData( i, j ); } } return outputBlock.release(); }
QgsRasterBlock *QgsSingleBandGrayRenderer::block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback ) { Q_UNUSED( bandNo ); QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2" ).arg( width ).arg( height ), 4 ); std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() ); if ( !mInput ) { return outputBlock.release(); } std::shared_ptr< QgsRasterBlock > inputBlock( mInput->block( mGrayBand, extent, width, height, feedback ) ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( QStringLiteral( "No raster data!" ) ); return outputBlock.release(); } std::shared_ptr< QgsRasterBlock > alphaBlock; if ( mAlphaBand > 0 && mGrayBand != mAlphaBand ) { alphaBlock.reset( mInput->block( mAlphaBand, extent, width, height, feedback ) ); if ( !alphaBlock || alphaBlock->isEmpty() ) { // TODO: better to render without alpha return outputBlock.release(); } } else if ( mAlphaBand > 0 ) { alphaBlock = inputBlock; } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { return outputBlock.release(); } QRgb myDefaultColor = NODATA_COLOR; bool isNoData = false; for ( qgssize i = 0; i < ( qgssize )width * height; i++ ) { double grayVal = inputBlock->valueAndNoData( i, isNoData ); if ( isNoData ) { outputBlock->setColor( i, myDefaultColor ); continue; } 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 ) ); } } return outputBlock.release(); }
QgsRasterBlock *QgsPalettedRasterRenderer::block( int bandNo, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback ) { std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() ); if ( !mInput || mClassData.isEmpty() ) { return outputBlock.release(); } std::shared_ptr< QgsRasterBlock > inputBlock( mInput->block( bandNo, extent, width, height, feedback ) ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( QStringLiteral( "No raster data!" ) ); return outputBlock.release(); } double currentOpacity = mOpacity; //rendering is faster without considering user-defined transparency bool hasTransparency = usesTransparency(); std::shared_ptr< QgsRasterBlock > alphaBlock; if ( mAlphaBand > 0 && mAlphaBand != mBand ) { alphaBlock.reset( mInput->block( mAlphaBand, extent, width, height, feedback ) ); if ( !alphaBlock || alphaBlock->isEmpty() ) { return outputBlock.release(); } } else if ( mAlphaBand == mBand ) { alphaBlock = inputBlock; } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { return outputBlock.release(); } 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; bool isNoData = false; for ( qgssize i = 0; i < rasterSize; ++i ) { const double value = inputBlock->valueAndNoData( i, isNoData ); if ( isNoData ) { outputData[i] = myDefaultColor; continue; } int val = static_cast< int >( value ); if ( !mColors.contains( val ) ) { outputData[i] = myDefaultColor; continue; } if ( !hasTransparency ) { outputData[i] = mColors.value( val ); } else { currentOpacity = mOpacity; if ( mRasterTransparency ) { currentOpacity = mRasterTransparency->alphaValue( val, mOpacity * 255 ) / 255.0; } if ( mAlphaBand > 0 ) { currentOpacity *= alphaBlock->value( i ) / 255.0; } QRgb c = mColors.value( val ); outputData[i] = qRgba( currentOpacity * qRed( c ), currentOpacity * qGreen( c ), currentOpacity * qBlue( c ), currentOpacity * qAlpha( c ) ); } } return outputBlock.release(); }
QgsRasterBlock *QgsRasterResampleFilter::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 ); std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() ); if ( !mInput ) return outputBlock.release(); double oversampling = 1.0; // approximate global oversampling factor if ( mZoomedInResampler || mZoomedOutResampler ) { QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider *>( mInput->sourceInput() ); 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; QgsDebugMsgLevel( QString( "xRes = %1 providerXRes = %2 pixelRatio = %3 oversampling = %4" ).arg( xRes ).arg( providerXRes ).arg( pixelRatio ).arg( oversampling ), 4 ); } 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; } } QgsDebugMsgLevel( QString( "oversampling %1" ).arg( oversampling ), 4 ); 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 ) ) { QgsDebugMsgLevel( "No oversampling.", 4 ); return mInput->block( bandNumber, extent, width, height, feedback ); } //effective oversampling factors are different to global one because of rounding double oversamplingX = ( static_cast< double >( width ) * oversampling ) / width; double oversamplingY = ( static_cast< 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; std::unique_ptr< QgsRasterBlock > inputBlock( mInput->block( bandNumber, extent, resWidth, resHeight, feedback ) ); if ( !inputBlock || inputBlock->isEmpty() ) { QgsDebugMsg( "No raster data!" ); return outputBlock.release(); } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { return outputBlock.release(); } //resample image QImage img = inputBlock->image(); QImage dstImg = QImage( width, height, QImage::Format_ARGB32_Premultiplied ); if ( mZoomedInResampler && ( oversamplingX < 1.0 || qgsDoubleNear( oversampling, 1.0 ) ) ) { QgsDebugMsgLevel( "zoomed in resampling", 4 ); mZoomedInResampler->resample( img, dstImg ); } else if ( mZoomedOutResampler && oversamplingX > 1.0 ) { QgsDebugMsgLevel( "zoomed out resampling", 4 ); mZoomedOutResampler->resample( img, dstImg ); } else { // Should not happen QgsDebugMsg( "Unexpected resampling" ); dstImg = img.scaled( width, height ); } outputBlock->setImage( &dstImg ); return outputBlock.release(); // No resampling }