Skip to content

Commit 3a35a53

Browse files
committedApr 13, 2013
raster nodata bitmap and reprojector fix; fixes qgis_composerscalebartest
1 parent 3a7a897 commit 3a35a53

File tree

4 files changed

+277
-33
lines changed

4 files changed

+277
-33
lines changed
 

‎src/core/raster/qgsrasterblock.cpp

Lines changed: 234 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ QgsRasterBlock::QgsRasterBlock()
3737
, mData( 0 )
3838
, mImage( 0 )
3939
, mNoDataBitmap( 0 )
40+
, mNoDataBitmapWidth( 0 )
41+
, mNoDataBitmapSize( 0 )
4042
{
4143
}
4244

@@ -51,6 +53,8 @@ QgsRasterBlock::QgsRasterBlock( QGis::DataType theDataType, int theWidth, int th
5153
, mData( 0 )
5254
, mImage( 0 )
5355
, mNoDataBitmap( 0 )
56+
, mNoDataBitmapWidth( 0 )
57+
, mNoDataBitmapSize( 0 )
5458
{
5559
reset( mDataType, mWidth, mHeight );
5660
}
@@ -66,6 +70,8 @@ QgsRasterBlock::QgsRasterBlock( QGis::DataType theDataType, int theWidth, int th
6670
, mData( 0 )
6771
, mImage( 0 )
6872
, mNoDataBitmap( 0 )
73+
, mNoDataBitmapWidth( 0 )
74+
, mNoDataBitmapSize( 0 )
6975
{
7076
reset( mDataType, mWidth, mHeight, mNoDataValue );
7177
}
@@ -312,8 +318,11 @@ bool QgsRasterBlock::isNoData( size_t index )
312318
// no data are not defined
313319
return false;
314320
}
315-
size_t byte = index / 8;
316-
int bit = index % 8;
321+
// TODO: optimize
322+
int row = index / mWidth;
323+
int column = index % mWidth;
324+
size_t byte = ( size_t )row * mNoDataBitmapWidth + column / 8 ;
325+
int bit = column % 8;
317326
int mask = 0b10000000 >> bit;
318327
//int x = mNoDataBitmap[byte] & mask;
319328
//QgsDebugMsg ( QString("byte = %1 bit = %2 mask = %3 nodata = %4 is nodata = %5").arg(byte).arg(bit).arg(mask, 0, 2 ).arg( x, 0, 2 ).arg( (bool)(x) ) );
@@ -382,50 +391,215 @@ bool QgsRasterBlock::setIsNoData( size_t index )
382391
{
383392
return setValue( index, mNoDataValue );
384393
}
385-
if ( mNoDataBitmap == 0 )
394+
else
386395
{
387-
if ( !createNoDataBitmap() )
396+
if ( mNoDataBitmap == 0 )
388397
{
389-
return false;
398+
if ( !createNoDataBitmap() )
399+
{
400+
return false;
401+
}
390402
}
403+
// TODO: optimize
404+
int row = index / mWidth;
405+
int column = index % mWidth;
406+
size_t byte = ( size_t )row * mNoDataBitmapWidth + column / 8;
407+
int bit = column % 8;
408+
int nodata = 0b10000000 >> bit;
409+
//QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
410+
mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
411+
return true;
391412
}
392-
size_t byte = index / 8;
393-
int bit = index % 8;
394-
int nodata = 0b10000000 >> bit;
395-
//QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
396-
mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
397-
return true;
398413
}
399414

400415
bool QgsRasterBlock::setIsNoData()
401416
{
402-
if ( mHasNoDataValue )
417+
QgsDebugMsg( "Entered" );
418+
if ( typeIsNumeric( mDataType ) )
419+
{
420+
if ( mHasNoDataValue )
421+
{
422+
if ( !mData )
423+
{
424+
QgsDebugMsg( "Data block not allocated" );
425+
return false;
426+
}
427+
428+
QgsDebugMsg( "set mData to mNoDataValue" );
429+
int dataTypeSize = typeSize( mDataType );
430+
QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
431+
432+
char *nodata = noDataByteArray.data();
433+
for ( size_t i = 0; i < ( size_t )mWidth*mHeight; i++ )
434+
{
435+
memcpy(( char* )mData + i*dataTypeSize, nodata, dataTypeSize );
436+
}
437+
}
438+
else
439+
{
440+
// use bitmap
441+
if ( mNoDataBitmap == 0 )
442+
{
443+
if ( !createNoDataBitmap() )
444+
{
445+
return false;
446+
}
447+
}
448+
QgsDebugMsg( "set mNoDataBitmap to 1" );
449+
memset( mNoDataBitmap, 0b11111111, mNoDataBitmapSize );
450+
}
451+
return true;
452+
}
453+
else
403454
{
404-
if ( !mData )
455+
// image
456+
if ( !mImage )
405457
{
406-
QgsDebugMsg( "Data block not allocated" );
458+
QgsDebugMsg( "Image not allocated" );
407459
return false;
408460
}
461+
QgsDebugMsg( "Fill image" );
462+
mImage->fill( qRgba( 0, 0, 0, 0 ) );
463+
return true;
464+
}
465+
}
409466

410-
int dataTypeSize = typeSize( mDataType );
411-
QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
467+
bool QgsRasterBlock::setIsNoDataExcept( const QRect & theExceptRect )
468+
{
469+
int top = theExceptRect.top();
470+
int bottom = theExceptRect.bottom();
471+
int left = theExceptRect.left();
472+
int right = theExceptRect.right();
473+
if ( top < 0 ) top = 0;
474+
if ( left < 0 ) left = 0;
475+
if ( bottom >= mHeight ) bottom = mHeight - 1;
476+
if ( right >= mWidth ) right = mWidth - 1;
412477

413-
char *nodata = noDataByteArray.data();
414-
for ( size_t i = 0; i < ( size_t )mWidth*mHeight; i++ )
478+
QgsDebugMsg( "Entered" );
479+
if ( typeIsNumeric( mDataType ) )
480+
{
481+
if ( mHasNoDataValue )
482+
{
483+
if ( !mData )
484+
{
485+
QgsDebugMsg( "Data block not allocated" );
486+
return false;
487+
}
488+
489+
QgsDebugMsg( "set mData to mNoDataValue" );
490+
int dataTypeSize = typeSize( mDataType );
491+
QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
492+
493+
char *nodata = noDataByteArray.data();
494+
char nodataRow[mWidth]; // full row of no data
495+
for ( int c = 0; c < mWidth; c ++ )
496+
{
497+
memcpy( nodataRow + c*dataTypeSize, nodata, dataTypeSize );
498+
}
499+
500+
// top and bottom
501+
for ( int r = 0; r < mHeight; r++ )
502+
{
503+
if ( r >= top && r <= bottom ) continue; // middle
504+
size_t i = ( size_t )r * mWidth;
505+
memcpy(( char* )mData + i*dataTypeSize, nodataRow, dataTypeSize*mWidth );
506+
}
507+
// middle
508+
for ( int r = top; r <= bottom; r++ )
509+
{
510+
size_t i = r * mWidth;
511+
// middle left
512+
memcpy(( char* )mData + i*dataTypeSize, nodataRow, dataTypeSize*left );
513+
// middle right
514+
i += right + 1;
515+
int w = mWidth - right;
516+
memcpy(( char* )mData + i*dataTypeSize, nodataRow, dataTypeSize*w );
517+
}
518+
}
519+
else
415520
{
416-
memcpy(( char* )mData + i*dataTypeSize, nodata, dataTypeSize );
521+
// use bitmap
522+
if ( mNoDataBitmap == 0 )
523+
{
524+
if ( !createNoDataBitmap() )
525+
{
526+
return false;
527+
}
528+
}
529+
QgsDebugMsg( "set mNoDataBitmap to 1" );
530+
531+
char nodataRow[mNoDataBitmapWidth]; // full row of no data
532+
memset( nodataRow, 0, mNoDataBitmapWidth );
533+
for ( int c = 0; c < mWidth; c ++ )
534+
{
535+
int byte = c / 8;
536+
int bit = c % 8;
537+
int nodata = 0b10000000 >> bit;
538+
memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
539+
}
540+
541+
// top and bottom
542+
for ( int r = 0; r < mHeight; r++ )
543+
{
544+
if ( r >= top && r <= bottom ) continue; // middle
545+
size_t i = ( size_t )r * mNoDataBitmapWidth;
546+
memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
547+
}
548+
// middle
549+
memset( nodataRow, 0, mNoDataBitmapWidth );
550+
for ( int c = 0; c < mWidth; c ++ )
551+
{
552+
if ( c >= left && c <= right ) continue; // middle
553+
int byte = c / 8;
554+
int bit = c % 8;
555+
int nodata = 0b10000000 >> bit;
556+
memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
557+
}
558+
for ( int r = top; r <= bottom; r++ )
559+
{
560+
size_t i = ( size_t )r * mNoDataBitmapWidth;
561+
memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
562+
}
417563
}
564+
return true;
418565
}
419-
// use bitmap
420-
if ( mNoDataBitmap == 0 )
566+
else
421567
{
422-
if ( !createNoDataBitmap() )
568+
// image
569+
if ( !mImage )
423570
{
571+
QgsDebugMsg( "Image not allocated" );
424572
return false;
425573
}
574+
QgsDebugMsg( "Fill image" );
575+
QRgb nodataRgba = qRgba( 0, 0, 0, 0 );
576+
QRgb nodataRow[mWidth]; // full row of no data
577+
int rgbSize = sizeof( QRgb );
578+
for ( int c = 0; c < mWidth; c ++ )
579+
{
580+
nodataRow[c] = nodataRgba;
581+
}
582+
583+
// top and bottom
584+
for ( int r = 0; r < mHeight; r++ )
585+
{
586+
if ( r >= top && r <= bottom ) continue; // middle
587+
size_t i = ( size_t )r * mWidth;
588+
memcpy(( void * )( mImage->bits() + rgbSize*i ), nodataRow, rgbSize*mWidth );
589+
}
590+
// middle
591+
for ( int r = top; r <= bottom; r++ )
592+
{
593+
size_t i = r * mWidth;
594+
// middle left
595+
memcpy(( void * )( mImage->bits() + rgbSize*i ), nodataRow, rgbSize*left );
596+
// middle right
597+
i += right + 1;
598+
int w = mWidth - right;
599+
memcpy(( void * )( mImage->bits() + rgbSize*i ), nodataRow, rgbSize*w );
600+
}
601+
return true;
426602
}
427-
memset( mNoDataBitmap, 0b11111111, sizeof( mNoDataBitmap ) );
428-
return true;
429603
}
430604

431605
char * QgsRasterBlock::bits( size_t index )
@@ -651,14 +825,45 @@ QByteArray QgsRasterBlock::valueBytes( QGis::DataType theDataType, double theVal
651825

652826
bool QgsRasterBlock::createNoDataBitmap()
653827
{
654-
size_t size = mWidth * mHeight / 8 + 1;
655-
QgsDebugMsg( QString( "allocate %1 bytes" ).arg( size ) );
656-
mNoDataBitmap = ( char* )qgsMalloc( size );
828+
mNoDataBitmapWidth = mWidth / 8 + 1;
829+
mNoDataBitmapSize = ( size_t )mNoDataBitmapWidth * mHeight;
830+
QgsDebugMsg( QString( "allocate %1 bytes" ).arg( mNoDataBitmapSize ) );
831+
mNoDataBitmap = ( char* )qgsMalloc( mNoDataBitmapSize );
657832
if ( mNoDataBitmap == 0 )
658833
{
659-
QgsDebugMsg( QString( "Couldn't allocate no data memory of %1 bytes" ).arg( size ) );
834+
QgsDebugMsg( QString( "Couldn't allocate no data memory of %1 bytes" ).arg( mNoDataBitmapSize ) );
660835
return false;
661836
}
662-
memset( mNoDataBitmap, 0, size );
837+
memset( mNoDataBitmap, 0, mNoDataBitmapSize );
663838
return true;
664839
}
840+
841+
QRect QgsRasterBlock::subRect( const QgsRectangle & theExtent, int theWidth, int theHeight, const QgsRectangle & theSubExtent )
842+
{
843+
double xRes = theExtent.width() / theWidth;
844+
double yRes = theExtent.height() / theHeight;
845+
846+
int top = 0;
847+
int bottom = theHeight - 1;
848+
int left = 0;
849+
int right = theWidth - 1;
850+
851+
if ( theSubExtent.yMaximum() < theExtent.yMaximum() )
852+
{
853+
top = qRound(( theExtent.yMaximum() - theSubExtent.yMaximum() ) / yRes );
854+
}
855+
if ( theSubExtent.yMinimum() > theExtent.yMinimum() )
856+
{
857+
bottom = qRound(( theExtent.yMaximum() - theSubExtent.yMinimum() ) / yRes ) - 1;
858+
}
859+
860+
if ( theSubExtent.xMinimum() > theExtent.xMinimum() )
861+
{
862+
left = qRound(( theSubExtent.xMinimum() - theExtent.xMinimum() ) / xRes );
863+
}
864+
if ( theSubExtent.xMaximum() < theExtent.xMaximum() )
865+
{
866+
right = qRound(( theSubExtent.xMaximum() - theExtent.xMinimum() ) / xRes ) - 1;
867+
}
868+
return QRect( left, top, right - left + 1, bottom - top + 1 );
869+
}

‎src/core/raster/qgsrasterblock.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ class CORE_EXPORT QgsRasterBlock
230230
* @return true on success */
231231
bool setIsNoData( );
232232

233+
/** \brief Set the whole block to no data except specified rectangle
234+
* @return true on success */
235+
bool setIsNoDataExcept( const QRect & theExceptRect );
236+
233237
/** \brief Set color on index (indexed line by line)
234238
* @param index data matrix index
235239
* @param color the color to be set, QRgb value
@@ -296,6 +300,17 @@ class CORE_EXPORT QgsRasterBlock
296300
/** \brief Set error */
297301
void setError( const QgsError & theError ) { mError = theError;}
298302

303+
/** \brief For theExtent and theWidht, theHeight find rectangle covered by subextent.
304+
* The output rect has x oriented from left to right and y from top to bottom
305+
* (upper-left to lower-right orientation).
306+
* @param theExtent extent, usually the larger
307+
* @param theWidth numbers of columns in theExtent
308+
* @param theHeight numbers of rows in theExtent
309+
* @param theSubExtent extent, usually smaller than theExtent
310+
* @return the rectangle covered by sub extent
311+
*/
312+
static QRect subRect( const QgsRectangle & theExtent, int theWidth, int theHeight, const QgsRectangle & theSubExtent );
313+
299314
private:
300315
static QImage::Format imageFormat( QGis::DataType theDataType );
301316
static QGis::DataType dataType( QImage::Format theFormat );
@@ -336,8 +351,15 @@ class CORE_EXPORT QgsRasterBlock
336351
QImage *mImage;
337352

338353
// Bitmap of no data. One bit for each pixel. Bit is 1 if a pixels is no data.
354+
// Each row is represented by whole number of bytes (last bits may be unused)
355+
// to make processing rows easy.
339356
char *mNoDataBitmap;
340357

358+
// number of bytes in mNoDataBitmap row
359+
int mNoDataBitmapWidth;
360+
// total size in bytes of mNoDataBitmap
361+
size_t mNoDataBitmapSize;
362+
341363
// Error
342364
QgsError mError;
343365
};

‎src/core/raster/qgsrasterprojector.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -752,18 +752,26 @@ QgsRasterBlock * QgsRasterProjector::block( int bandNo, QgsRectangle const & ex
752752
return outputBlock;
753753
}
754754

755-
// TODO: fill by no data or transparent
755+
// we are using memcpy on bits, so we have to use setIsNoData if necessary
756+
// so that bitmaps is set if necessary
757+
//outputBlock->setIsNoData();
756758

757759
int srcRow, srcCol;
758760
for ( int i = 0; i < height; ++i )
759761
{
760762
for ( int j = 0; j < width; ++j )
761763
{
762764
srcRowCol( i, j, &srcRow, &srcCol );
763-
QgsDebugMsgLevel( QString( "row = %1 col = %2 srcRow = %3 srcCol = %4" ).arg( i ).arg( j ).arg( srcRow ).arg( srcCol ), 5 );
764765
size_t srcIndex = srcRow * mSrcCols + srcCol;
765-
size_t destIndex = i * width + j;
766+
QgsDebugMsgLevel( QString( "row = %1 col = %2 srcRow = %3 srcCol = %4" ).arg( i ).arg( j ).arg( srcRow ).arg( srcCol ), 5 );
766767

768+
if ( inputBlock->isNoData( srcRow, srcCol ) )
769+
{
770+
outputBlock->setIsNoData( srcRow, srcCol );
771+
continue ;
772+
}
773+
774+
size_t destIndex = i * width + j;
767775
char *srcBits = inputBlock->bits( srcIndex );
768776
char *destBits = outputBlock->bits( destIndex );
769777
if ( !srcBits )

‎src/providers/gdal/qgsgdalprovider.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,8 @@ QgsRasterBlock* QgsGdalProvider::block( int theBandNo, const QgsRectangle &theEx
382382

383383
if ( !mExtent.contains( theExtent ) )
384384
{
385-
block->setIsNoData();
385+
QRect subRect = QgsRasterBlock::subRect( theExtent, theWidth, theHeight, mExtent );
386+
block->setIsNoDataExcept( subRect );
386387
}
387388
readBlock( theBandNo, theExtent, theWidth, theHeight, block->data() );
388389
block->applyNodataValues( userNoDataValue( theBandNo ) );
@@ -450,6 +451,7 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent,
450451

451452
// Find top, bottom rows and left, right column the raster extent covers
452453
// These are limits in target grid space
454+
#if 0
453455
int top = 0;
454456
int bottom = thePixelHeight - 1;
455457
int left = 0;
@@ -472,8 +474,15 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent,
472474
{
473475
right = qRound(( myRasterExtent.xMaximum() - theExtent.xMinimum() ) / xRes ) - 1;
474476
}
477+
#endif
478+
QRect subRect = QgsRasterBlock::subRect( theExtent, thePixelWidth, thePixelHeight, myRasterExtent );
479+
int top = subRect.top();
480+
int bottom = subRect.bottom();
481+
int left = subRect.left();
482+
int right = subRect.right();
475483
QgsDebugMsg( QString( "top = %1 bottom = %2 left = %3 right = %4" ).arg( top ).arg( bottom ).arg( left ).arg( right ) );
476484

485+
477486
// We want to avoid another resampling, so we read data approximately with
478487
// the same resolution as requested and exactly the width/height we need.
479488

0 commit comments

Comments
 (0)
Please sign in to comment.