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; }
void * QgsRasterProjector::readBlock( 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 ) return 0; 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 ) { return 0; } void * inputData = mInput->block( bandNo, srcExtent(), srcCols(), srcRows() ); if ( !inputData ) return 0; size_t pixelSize = mInput->typeSize( mInput->dataType( bandNo ) ) / 8; size_t inputSize = pixelSize * srcCols() * srcRows(); size_t outputSize = width * height * pixelSize; void * outputData = malloc( outputSize ); // Check for allcoation error if ( ! outputData ) { QgsDebugMsg( QString( "Couldn't malloc %1 bytes!" ).arg( outputSize ) ); free( inputData ); return 0; } // TODO: fill by transparent int srcRow, srcCol; for ( int i = 0; i < height; ++i ) { for ( int j = 0; j < width; ++j ) { srcRowCol( i, j, &srcRow, &srcCol ); size_t srcIndex = pixelSize * ( srcRow * mSrcCols + srcCol ); size_t destIndex = pixelSize * ( i * width + j ); if ( srcIndex >= inputSize || destIndex >= outputSize ) continue; // should not happen memcpy(( char* )outputData + destIndex, ( char* )inputData + srcIndex, pixelSize ); } } free( inputData ); return outputData; }