Skip to content

Commit

Permalink
[GDAL provider] Expose GDAL mask band as a pseudo alpha band.
Browse files Browse the repository at this point in the history
Some TIFF formulations, for example RGB TIFF with JPEG YCbCr compression, cannot
include a regular alpha band and instead use the GDAL mask band mechanism. Such
mask bands were ignored up to now. Now expose them as if they were alpha bands.
  • Loading branch information
rouault committed Feb 21, 2017
1 parent 66888fe commit 0f16eb8
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 13 deletions.
58 changes: 45 additions & 13 deletions src/providers/gdal/qgsgdalprovider.cpp
Expand Up @@ -310,6 +310,10 @@ QString QgsGdalProvider::metadata()
}

}
if ( mMaskBandExposedAsAlpha )
{
myMetadata += "<p class=\"glossy\">" + tr( "Mask band (exposed as alpha band)" ) + "</p>\n";
}

// end my added code

Expand Down Expand Up @@ -406,7 +410,7 @@ void QgsGdalProvider::readBlock( int bandNo, int xBlock, int yBlock, void *block

//QgsDebugMsg( "yBlock = " + QString::number( yBlock ) );

GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
GDALRasterBandH myGdalBand = getBand( bandNo );
//GDALReadBlock( myGdalBand, xBlock, yBlock, block );

// We have to read with correct data type consistent with other readBlock functions
Expand Down Expand Up @@ -584,7 +588,7 @@ void QgsGdalProvider::readBlock( int bandNo, QgsRectangle const & extent, int p
QgsDebugMsg( QString( "Couldn't allocate temporary buffer of %1 bytes" ).arg( dataSize * tmpWidth * tmpHeight ) );
return;
}
GDALRasterBandH gdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
GDALRasterBandH gdalBand = getBand( bandNo );
GDALDataType type = ( GDALDataType )mGdalDataType.at( bandNo - 1 );
CPLErrorReset();

Expand Down Expand Up @@ -1055,6 +1059,9 @@ int QgsGdalProvider::capabilities() const

Qgis::DataType QgsGdalProvider::sourceDataType( int bandNo ) const
{
if ( mMaskBandExposedAsAlpha && bandNo == GDALGetRasterCount( mGdalDataset ) + 1 )
return dataTypeFromGdal( GDT_Byte );

GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
GDALDataType myGdalDataType = GDALGetRasterDataType( myGdalBand );
Qgis::DataType myDataType = dataTypeFromGdal( myGdalDataType );
Expand Down Expand Up @@ -1094,14 +1101,17 @@ Qgis::DataType QgsGdalProvider::sourceDataType( int bandNo ) const

Qgis::DataType QgsGdalProvider::dataType( int bandNo ) const
{
if ( mMaskBandExposedAsAlpha && bandNo == GDALGetRasterCount( mGdalDataset ) + 1 )
return dataTypeFromGdal( GDT_Byte );

if ( bandNo <= 0 || bandNo > mGdalDataType.count() ) return Qgis::UnknownDataType;

return dataTypeFromGdal( mGdalDataType[bandNo-1] );
}

double QgsGdalProvider::bandScale( int bandNo ) const
{
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
GDALRasterBandH myGdalBand = getBand( bandNo );
int bGotScale;
double myScale = GDALGetRasterScale( myGdalBand, &bGotScale );
if ( bGotScale )
Expand All @@ -1112,7 +1122,7 @@ double QgsGdalProvider::bandScale( int bandNo ) const

double QgsGdalProvider::bandOffset( int bandNo ) const
{
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
GDALRasterBandH myGdalBand = getBand( bandNo );
int bGotOffset;
double myOffset = GDALGetRasterOffset( myGdalBand, &bGotOffset );
if ( bGotOffset )
Expand All @@ -1124,13 +1134,15 @@ double QgsGdalProvider::bandOffset( int bandNo ) const
int QgsGdalProvider::bandCount() const
{
if ( mGdalDataset )
return GDALGetRasterCount( mGdalDataset );
return GDALGetRasterCount( mGdalDataset ) + ( mMaskBandExposedAsAlpha ? 1 : 0 );
else
return 1;
}

int QgsGdalProvider::colorInterpretation( int bandNo ) const
{
if ( mMaskBandExposedAsAlpha && bandNo == GDALGetRasterCount( mGdalDataset ) + 1 )
return colorInterpretationFromGdal( GCI_AlphaBand );
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
return colorInterpretationFromGdal( GDALGetRasterColorInterpretation( myGdalBand ) );
}
Expand Down Expand Up @@ -1229,7 +1241,7 @@ bool QgsGdalProvider::hasHistogram( int bandNo,

QgsDebugMsg( "Looking for GDAL histogram" );

GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
GDALRasterBandH myGdalBand = getBand( bandNo );
if ( ! myGdalBand )
{
return false;
Expand Down Expand Up @@ -1315,7 +1327,7 @@ QgsRasterHistogram QgsGdalProvider::histogram( int bandNo,

QgsDebugMsg( "Computing GDAL histogram" );

GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
GDALRasterBandH myGdalBand = getBand( bandNo );

int bApproxOK = false;
if ( sampleSize > 0 )
Expand Down Expand Up @@ -2204,7 +2216,7 @@ bool QgsGdalProvider::hasStatistics( int bandNo,

QgsDebugMsg( "Looking for GDAL statistics" );

GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
GDALRasterBandH myGdalBand = getBand( bandNo );
if ( ! myGdalBand )
{
return false;
Expand Down Expand Up @@ -2298,7 +2310,7 @@ QgsRasterBandStats QgsGdalProvider::bandStatistics( int bandNo, int stats, const
}

QgsDebugMsg( "Using GDAL statistics." );
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
GDALRasterBandH myGdalBand = getBand( bandNo );

//int bApproxOK = false; //as we asked for stats, don't get approx values
// GDAL does not have sample size parameter in API, just bApproxOK or not,
Expand Down Expand Up @@ -2551,7 +2563,8 @@ void QgsGdalProvider::initBaseDataset()
// Determine the nodata value and data type
//
//mValidNoDataValue = true;
for ( int i = 1; i <= GDALGetRasterCount( mGdalBaseDataset ); i++ )
const int bandCount = GDALGetRasterCount( mGdalBaseDataset );
for ( int i = 1; i <= bandCount; i++ )
{
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, i );
GDALDataType myGdalDataType = GDALGetRasterDataType( myGdalBand );
Expand Down Expand Up @@ -2657,6 +2670,18 @@ void QgsGdalProvider::initBaseDataset()
//QgsDebugMsg( QString( "mInternalNoDataValue[%1] = %2" ).arg( i - 1 ).arg( mInternalNoDataValue[i-1] ) );
}

// Check if the dataset has a mask band, that applies to the whole dataset
// If so then expose it as an alpha band.
int nMaskFlags = GDALGetMaskFlags( myGDALBand );
if (( nMaskFlags == 0 && bandCount == 1 ) || nMaskFlags == GMF_PER_DATASET )
{
mMaskBandExposedAsAlpha = true;
mSrcNoDataValue.append( std::numeric_limits<double>::quiet_NaN() );
mSrcHasNoDataValue.append( false );
mUseSrcNoDataValue.append( false );
mGdalDataType.append( GDT_Byte );
}

mValid = true;
}

Expand Down Expand Up @@ -2720,8 +2745,7 @@ bool QgsGdalProvider::write( void* data, int band, int width, int height, int xO
{
return false;
}

GDALRasterBandH rasterBand = GDALGetRasterBand( mGdalDataset, band );
GDALRasterBandH rasterBand = getBand( band );
if ( !rasterBand )
{
return false;
Expand All @@ -2736,7 +2760,7 @@ bool QgsGdalProvider::setNoDataValue( int bandNo, double noDataValue )
return false;
}

GDALRasterBandH rasterBand = GDALGetRasterBand( mGdalDataset, bandNo );
GDALRasterBandH rasterBand = getBand( bandNo );
CPLErrorReset();
CPLErr err = GDALSetRasterNoDataValue( rasterBand, noDataValue );
if ( err != CPLE_None )
Expand Down Expand Up @@ -2962,6 +2986,14 @@ bool QgsGdalProvider::setEditable( bool enabled )
return true;
}

GDALRasterBandH QgsGdalProvider::getBand( int bandNo ) const
{
if ( mMaskBandExposedAsAlpha && bandNo == GDALGetRasterCount( mGdalDataset ) + 1 )
return GDALGetMaskBand( GDALGetRasterBand( mGdalDataset, 1 ) );
else
return GDALGetRasterBand( mGdalDataset, bandNo );
}

// pyramids resampling

// see http://www.gdal.org/gdaladdo.html
Expand Down
6 changes: 6 additions & 0 deletions src/providers/gdal/qgsgdalprovider.h
Expand Up @@ -211,6 +211,12 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase

//! \brief sublayers list saved for subsequent access
QStringList mSubLayers;

//! Whether a per-dataset mask band is exposed as an alpha band for the point of view of the rest of the application.
bool mMaskBandExposedAsAlpha = false;

//! Wrapper for GDALGetRasterBand() that takes into account mMaskBandExposedAsAlpha.
GDALRasterBandH getBand( int bandNo ) const;
};

#endif
Expand Down
24 changes: 24 additions & 0 deletions tests/src/providers/testqgsgdalprovider.cpp
Expand Up @@ -48,6 +48,7 @@ class TestQgsGdalProvider : public QObject
void noData();
void invalidNoDataInSourceIgnored();
void isRepresentableValue();
void mask();

private:
QString mTestDataDir;
Expand Down Expand Up @@ -196,5 +197,28 @@ void TestQgsGdalProvider::isRepresentableValue()
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::max(), Qgis::Float64 ), true );
}

void TestQgsGdalProvider::mask()
{
QString raster = QStringLiteral( TEST_DATA_DIR ) + "/raster/rgb_with_mask.tif";
QgsDataProvider* provider = QgsProviderRegistry::instance()->provider( QStringLiteral( "gdal" ), raster );
QVERIFY( provider->isValid() );
QgsRasterDataProvider* rp = dynamic_cast< QgsRasterDataProvider* >( provider );
QVERIFY( rp );
if ( rp )
{
QCOMPARE( rp->bandCount(), 4 );
QCOMPARE( rp->dataType( 4 ), Qgis::Byte );
QCOMPARE( rp->sourceDataType( 4 ), Qgis::Byte );
QCOMPARE( rp->colorInterpretation( 4 ), static_cast<int>( QgsRaster::AlphaBand ) );
QCOMPARE( rp->bandScale( 4 ), 1.0 );
QCOMPARE( rp->bandOffset( 4 ), 0.0 );
QgsRectangle rect( 0, 0, 162, 150 );
QgsRasterBlock* block = rp->block( 4, rect, 162, 150 );
QVERIFY( block );
delete block;
}
delete provider;
}

QGSTEST_MAIN( TestQgsGdalProvider )
#include "testqgsgdalprovider.moc"
Binary file added tests/testdata/raster/rgb_with_mask.tif
Binary file not shown.

0 comments on commit 0f16eb8

Please sign in to comment.