Skip to content

Commit

Permalink
Merge pull request #4001 from rouault/fix_3984
Browse files Browse the repository at this point in the history
GTiff signedbyte: do not write 0 values for tiles/strips entirely at a negative nodata value (fixes #3984)
  • Loading branch information
rouault authored Jun 17, 2021
2 parents 17f1333 + b22a570 commit e9ce2ba
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 5 deletions.
25 changes: 22 additions & 3 deletions autotest/gcore/numpy_rw.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,7 @@ def test_numpy_rw_9():
def test_numpy_rw_10():

ds = gdal.GetDriverByName('GTiff').Create('/vsimem/signed8.tif', 2, 1, options=['PIXELTYPE=SIGNEDBYTE'])
ar = numpy.empty([1, 2], dtype=numpy.int8)
ar[0][0] = -128
ar[0][1] = 127
ar = numpy.array([[-128, 127]], dtype=numpy.int8)
ds.GetRasterBand(1).WriteArray(ar)
ds = None

Expand All @@ -218,6 +216,27 @@ def test_numpy_rw_10():

assert ar3[0][0] == -128 and ar3[0][1] == 127, 'did not get expected result (2)'

###############################################################################
# Test signed byte handling with all values set to nodata


@pytest.mark.parametrize("options", [[], ['SPARSE_OK=YES']])
def test_numpy_rw_10_bis(options):
"""Reproduce https://github.com/mapbox/rasterio/issues/2180"""
ds = gdal.GetDriverByName('GTiff').Create('/vsimem/signed8.tif', 2, 1,
options=['PIXELTYPE=SIGNEDBYTE']+options)
ar = numpy.array([[-1, -1]], dtype=numpy.int8)
ds.GetRasterBand(1).WriteArray(ar)
ds.GetRasterBand(1).SetNoDataValue(-1)
ds = None

ds = gdal.Open('/vsimem/signed8.tif')
ar2 = ds.ReadAsArray()
ds = None
gdal.Unlink('/vsimem/signed8.tif')

assert ar2[0][0] == -1 and ar2[0][1] == -1

###############################################################################
# Test all datatypes

Expand Down
28 changes: 26 additions & 2 deletions gdal/frmts/gtiff/geotiff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5946,7 +5946,7 @@ void GTiffRasterBand::NullBlock( void *pData )
const int nChunkSize = std::max(1, GDALGetDataTypeSizeBytes(eDataType));

int bNoDataSetIn = FALSE;
const double dfNoData = GetNoDataValue( &bNoDataSetIn );
double dfNoData = GetNoDataValue( &bNoDataSetIn );
if( !bNoDataSetIn )
{
#ifdef ESRI_BUILD
Expand All @@ -5960,6 +5960,17 @@ void GTiffRasterBand::NullBlock( void *pData )
}
else
{
// Hack for Signed Int8 case. As the data type is GDT_Byte (unsigned),
// we have to convert a negative nodata value in the range [-128,-1] in
// [128, 255]
if( m_poGDS->m_nBitsPerSample == 8 &&
m_poGDS->m_nSampleFormat == SAMPLEFORMAT_INT &&
dfNoData < 0 && dfNoData >= -128 &&
static_cast<int>(dfNoData) == dfNoData )
{
dfNoData = 256 + dfNoData;
}

// Will convert nodata value to the right type and copy efficiently.
GDALCopyWords64( &dfNoData, GDT_Float64, 0,
pData, eDataType, nChunkSize, nWords);
Expand Down Expand Up @@ -8126,7 +8137,20 @@ void GTiffDataset::FillEmptyTiles()
if( nDataTypeSize &&
nDataTypeSize * 8 == static_cast<int>(m_nBitsPerSample) )
{
GDALCopyWords64( &m_dfNoDataValue, GDT_Float64, 0,
double dfNoData = m_dfNoDataValue;

// Hack for Signed Int8 case. As the data type is GDT_Byte (unsigned),
// we have to convert a negative nodata value in the range [-128,-1] in
// [128, 255]
if( m_nBitsPerSample == 8 &&
m_nSampleFormat == SAMPLEFORMAT_INT &&
dfNoData < 0 && dfNoData >= -128 &&
static_cast<int>(dfNoData) == dfNoData )
{
dfNoData = 256 + dfNoData;
}

GDALCopyWords64( &dfNoData, GDT_Float64, 0,
pabyData, eDataType,
nDataTypeSize,
nBlockBytes / nDataTypeSize );
Expand Down

0 comments on commit e9ce2ba

Please sign in to comment.