QgsRasterBlock *QgsMultiBandColorRenderer::block( int bandNo, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback ) { Q_UNUSED( bandNo ); std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() ); if ( !mInput ) { return outputBlock.release(); } //In some (common) cases, we can simplify the drawing loop considerably and save render time bool fastDraw = ( !usesTransparency() && mRedBand > 0 && mGreenBand > 0 && mBlueBand > 0 && mAlphaBand < 1 ); QSet<int> bands; if ( mRedBand > 0 ) { bands << mRedBand; } if ( mGreenBand > 0 ) { bands << mGreenBand; } if ( mBlueBand > 0 ) { bands << mBlueBand; } if ( bands.empty() ) { // no need to draw anything if no band is set // TODO:: we should probably return default color block return outputBlock.release(); } if ( mAlphaBand > 0 ) { bands << mAlphaBand; } QMap<int, QgsRasterBlock *> bandBlocks; QgsRasterBlock *defaultPointer = nullptr; QSet<int>::const_iterator bandIt = bands.constBegin(); for ( ; bandIt != bands.constEnd(); ++bandIt ) { bandBlocks.insert( *bandIt, defaultPointer ); } QgsRasterBlock *redBlock = nullptr; QgsRasterBlock *greenBlock = nullptr; QgsRasterBlock *blueBlock = nullptr; QgsRasterBlock *alphaBlock = nullptr; bandIt = bands.constBegin(); for ( ; bandIt != bands.constEnd(); ++bandIt ) { bandBlocks[*bandIt] = mInput->block( *bandIt, extent, width, height, feedback ); if ( !bandBlocks[*bandIt] ) { // We should free the alloced mem from block(). QgsDebugMsg( QStringLiteral( "No input band" ) ); --bandIt; for ( ; bandIt != bands.constBegin(); --bandIt ) { delete bandBlocks[*bandIt]; } return outputBlock.release(); } } if ( mRedBand > 0 ) { redBlock = bandBlocks[mRedBand]; } if ( mGreenBand > 0 ) { greenBlock = bandBlocks[mGreenBand]; } if ( mBlueBand > 0 ) { blueBlock = bandBlocks[mBlueBand]; } if ( mAlphaBand > 0 ) { alphaBlock = bandBlocks[mAlphaBand]; } if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) ) { for ( int i = 0; i < bandBlocks.size(); i++ ) { delete bandBlocks.value( i ); } return outputBlock.release(); } QRgb *outputBlockColorData = outputBlock->colorData(); // faster data access to data for the common case that input data are coming from RGB image with 8-bit bands bool hasByteRgb = ( redBlock && greenBlock && blueBlock && redBlock->dataType() == Qgis::Byte && greenBlock->dataType() == Qgis::Byte && blueBlock->dataType() == Qgis::Byte ); const quint8 *redData = nullptr, *greenData = nullptr, *blueData = nullptr; if ( hasByteRgb ) { redData = redBlock->byteData(); greenData = greenBlock->byteData(); blueData = blueBlock->byteData(); } QRgb myDefaultColor = NODATA_COLOR; if ( fastDraw ) { // By default RGB raster layers have contrast enhancement assigned and normally that requires us to take the slow // route that applies the enhancement. However if the algorithm type is "no enhancement" and all input bands are byte-sized, // no transform would be applied to the input values and we can take the fast route. bool hasEnhancement; if ( hasByteRgb ) { hasEnhancement = ( mRedContrastEnhancement && mRedContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement ) || ( mGreenContrastEnhancement && mGreenContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement ) || ( mBlueContrastEnhancement && mBlueContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement ); } else { hasEnhancement = mRedContrastEnhancement || mGreenContrastEnhancement || mBlueContrastEnhancement; } if ( hasEnhancement ) fastDraw = false; } qgssize count = ( qgssize )width * height; for ( qgssize i = 0; i < count; i++ ) { if ( fastDraw ) //fast rendering if no transparency, stretching, color inversion, etc. { if ( redBlock->isNoData( i ) || greenBlock->isNoData( i ) || blueBlock->isNoData( i ) ) { outputBlock->setColor( i, myDefaultColor ); } else { if ( hasByteRgb ) { outputBlockColorData[i] = qRgb( redData[i], greenData[i], blueData[i] ); } else { int redVal = static_cast<int>( redBlock->value( i ) ); int greenVal = static_cast<int>( greenBlock->value( i ) ); int blueVal = static_cast<int>( blueBlock->value( i ) ); outputBlockColorData[i] = qRgb( redVal, greenVal, blueVal ); } } continue; } bool isNoData = false; double redVal = 0; double greenVal = 0; double blueVal = 0; if ( mRedBand > 0 ) { redVal = redBlock->value( i ); if ( redBlock->isNoData( i ) ) isNoData = true; } if ( !isNoData && mGreenBand > 0 ) { greenVal = greenBlock->value( i ); if ( greenBlock->isNoData( i ) ) isNoData = true; } if ( !isNoData && mBlueBand > 0 ) { blueVal = blueBlock->value( i ); if ( blueBlock->isNoData( i ) ) isNoData = true; } if ( isNoData ) { outputBlock->setColor( i, myDefaultColor ); continue; } //apply default color if red, green or blue not in displayable range if ( ( mRedContrastEnhancement && !mRedContrastEnhancement->isValueInDisplayableRange( redVal ) ) || ( mGreenContrastEnhancement && !mGreenContrastEnhancement->isValueInDisplayableRange( redVal ) ) || ( mBlueContrastEnhancement && !mBlueContrastEnhancement->isValueInDisplayableRange( redVal ) ) ) { outputBlock->setColor( i, myDefaultColor ); continue; } //stretch color values if ( mRedContrastEnhancement ) { redVal = mRedContrastEnhancement->enhanceContrast( redVal ); } if ( mGreenContrastEnhancement ) { greenVal = mGreenContrastEnhancement->enhanceContrast( greenVal ); } if ( mBlueContrastEnhancement ) { blueVal = mBlueContrastEnhancement->enhanceContrast( blueVal ); } //opacity double currentOpacity = mOpacity; if ( mRasterTransparency ) { currentOpacity = mRasterTransparency->alphaValue( redVal, greenVal, blueVal, mOpacity * 255 ) / 255.0; } if ( mAlphaBand > 0 ) { currentOpacity *= alphaBlock->value( i ) / 255.0; } if ( qgsDoubleNear( currentOpacity, 1.0 ) ) { outputBlock->setColor( i, qRgba( redVal, greenVal, blueVal, 255 ) ); } else { outputBlock->setColor( i, qRgba( currentOpacity * redVal, currentOpacity * greenVal, currentOpacity * blueVal, currentOpacity * 255 ) ); } } //delete input blocks QMap<int, QgsRasterBlock *>::const_iterator bandDelIt = bandBlocks.constBegin(); for ( ; bandDelIt != bandBlocks.constEnd(); ++bandDelIt ) { delete bandDelIt.value(); } return outputBlock.release(); }
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; }
QgsRasterBlock * QgsRasterNuller::block( int bandNo, QgsRectangle const & extent, int width, int height ) { QgsDebugMsg( "Entered" ); if ( !mInput ) { return new QgsRasterBlock(); } QgsRasterBlock *inputBlock = mInput->block( bandNo, extent, width, height ); if ( !inputBlock ) { return new QgsRasterBlock(); } // We don't support nuller for color types if ( QgsRasterBlock::typeIsColor( inputBlock->dataType() ) ) { return inputBlock; } QgsRasterBlock *outputBlock = 0; if ( mHasOutputNoData.value( bandNo - 1 ) || inputBlock->hasNoDataValue() ) { double noDataValue; if ( mHasOutputNoData.value( bandNo - 1 ) ) { noDataValue = mOutputNoData.value( bandNo - 1 ); } else { noDataValue = inputBlock->noDataValue(); } outputBlock = new QgsRasterBlock( inputBlock->dataType(), width, height, noDataValue ); } else { outputBlock = new QgsRasterBlock( inputBlock->dataType(), width, height ); } for ( int i = 0; i < height; i++ ) { for ( int j = 0; j < width; j++ ) { double value = inputBlock->value( i, j ); bool isNoData = inputBlock->isNoData( i, j ); if ( QgsRasterRange::contains( value, mNoData.value( bandNo - 1 ) ) ) { isNoData = true; } outputBlock->setValue( i, j, inputBlock->value( i, j ) ); if ( isNoData ) { outputBlock->setIsNoData( i, j ); } else { outputBlock->setValue( i, j, value ); } } } delete inputBlock; return outputBlock; }