Skip to content

Commit

Permalink
Overview/RasterIO resampling: fix infinite looping when nodata has a …
Browse files Browse the repository at this point in the history
…big absolute value (#9428)

Fix #9427
  • Loading branch information
elpaso authored Mar 9, 2024
1 parent 4f1938c commit efccb78
Show file tree
Hide file tree
Showing 6 changed files with 426 additions and 104 deletions.
187 changes: 187 additions & 0 deletions autotest/cpp/test_gdal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2462,6 +2462,193 @@ TEST_F(test_gdal, GDALBufferHasOnlyNoData)
GSF_FLOATING_POINT));
}

// Test GetRasterNoDataReplacementValue()
TEST_F(test_gdal, GetRasterNoDataReplacementValue)
{
// Test GDT_Byte
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Byte, std::numeric_limits<double>::lowest()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_Byte,
std::numeric_limits<double>::max()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Byte, std::numeric_limits<uint8_t>::lowest()),
std::numeric_limits<uint8_t>::lowest() + 1);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Byte, std::numeric_limits<uint8_t>::max()),
std::numeric_limits<uint8_t>::max() - 1);

// Test GDT_Int8
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Int8, std::numeric_limits<double>::lowest()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_Int8,
std::numeric_limits<double>::max()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Int8, std::numeric_limits<int8_t>::lowest()),
std::numeric_limits<int8_t>::lowest() + 1);
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_Int8,
std::numeric_limits<int8_t>::max()),
std::numeric_limits<int8_t>::max() - 1);

// Test GDT_UInt16
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_UInt16, std::numeric_limits<double>::lowest()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_UInt16,
std::numeric_limits<double>::max()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_UInt16, std::numeric_limits<uint16_t>::lowest()),
std::numeric_limits<uint16_t>::lowest() + 1);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_UInt16, std::numeric_limits<uint16_t>::max()),
std::numeric_limits<uint16_t>::max() - 1);

// Test GDT_Int16
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Int16, std::numeric_limits<double>::lowest()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_Int16,
std::numeric_limits<double>::max()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Int16, std::numeric_limits<int16_t>::lowest()),
std::numeric_limits<int16_t>::lowest() + 1);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Int16, std::numeric_limits<int16_t>::max()),
std::numeric_limits<int16_t>::max() - 1);

// Test GDT_UInt32
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_UInt32, std::numeric_limits<double>::lowest()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_UInt32,
std::numeric_limits<double>::max()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_UInt32, std::numeric_limits<uint32_t>::lowest()),
std::numeric_limits<uint32_t>::lowest() + 1);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_UInt32, std::numeric_limits<uint32_t>::max()),
std::numeric_limits<uint32_t>::max() - 1);

// Test GDT_Int32
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Int32, std::numeric_limits<double>::lowest()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_Int32,
std::numeric_limits<double>::max()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Int32, std::numeric_limits<int32_t>::lowest()),
std::numeric_limits<int32_t>::lowest() + 1);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Int32, std::numeric_limits<int32_t>::max()),
std::numeric_limits<int32_t>::max() - 1);

// Test GDT_UInt64
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_UInt64, std::numeric_limits<double>::lowest()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_UInt64,
std::numeric_limits<double>::max()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_UInt64,
static_cast<double>(std::numeric_limits<uint64_t>::lowest())),
static_cast<double>(std::numeric_limits<uint64_t>::lowest()) + 1);
// uin64_t max is not representable in double so we expect the next value to be returned
EXPECT_EQ(
GDALGetNoDataReplacementValue(
GDT_UInt64,
static_cast<double>(std::numeric_limits<uint64_t>::max())),
std::nextafter(
static_cast<double>(std::numeric_limits<uint64_t>::max()), 0) -
1);

// Test GDT_Int64
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Int64, std::numeric_limits<double>::lowest()),
0);
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_Int64,
std::numeric_limits<double>::max()),
0);
// in64_t max is not representable in double so we expect the next value to be returned
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Int64,
static_cast<double>(std::numeric_limits<int64_t>::lowest())),
static_cast<double>(std::numeric_limits<int64_t>::lowest()) + 1);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Int64,
static_cast<double>(std::numeric_limits<int64_t>::max())),
std::nextafter(
static_cast<double>(std::numeric_limits<int64_t>::max()), 0) -
1);

// Test floating point types

// out of range for float32
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Float32, std::numeric_limits<double>::lowest()),
0.0);
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_Float32,
std::numeric_limits<double>::max()),
0.0);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Float32, std::numeric_limits<double>::infinity()),
0.0);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Float32, -std::numeric_limits<double>::infinity()),
0.0);

// in range for float 32
EXPECT_EQ(
static_cast<float>(GDALGetNoDataReplacementValue(GDT_Float32, -1.0)),
std::nextafter(float(-1.0), 0.0f));
EXPECT_EQ(
static_cast<float>(GDALGetNoDataReplacementValue(GDT_Float32, 1.1)),
std::nextafter(float(1.1), 2.0f));
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Float32, std::numeric_limits<float>::lowest()),
std::nextafter(std::numeric_limits<float>::lowest(), 0.0f));

EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_Float32,
std::numeric_limits<float>::max()),
static_cast<double>(
std::nextafter(std::numeric_limits<float>::max(), 0.0f)));

// in range for float64
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Float64, std::numeric_limits<double>::lowest()),
std::nextafter(std::numeric_limits<double>::lowest(), 0.0));
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_Float64,
std::numeric_limits<double>::max()),
std::nextafter(std::numeric_limits<double>::max(), 0.0));

EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Float64, std::numeric_limits<double>::lowest()),
std::nextafter(std::numeric_limits<double>::lowest(), 0.0));
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_Float64,
std::numeric_limits<double>::max()),
std::nextafter(std::numeric_limits<double>::max(), 0.0));

EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_Float64, double(-1.0)),
std::nextafter(double(-1.0), 0.0));
EXPECT_EQ(GDALGetNoDataReplacementValue(GDT_Float64, double(1.1)),
std::nextafter(double(1.1), 2.0));

// test infinity
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Float64, std::numeric_limits<double>::infinity()),
0.0);
EXPECT_EQ(GDALGetNoDataReplacementValue(
GDT_Float64, -std::numeric_limits<double>::infinity()),
0.0);
}

// Test GDALRasterBand::GetIndexColorTranslationTo()
TEST_F(test_gdal, GetIndexColorTranslationTo)
{
Expand Down
4 changes: 2 additions & 2 deletions autotest/gcore/rasterio.py
Original file line number Diff line number Diff line change
Expand Up @@ -1265,14 +1265,14 @@ def test_rasterio_resampled_value_is_nodata():
buf_xsize=1, buf_ysize=1, resample_alg=gdal.GRIORA_Lanczos
)
data_ar = struct.unpack("f" * 1, data)
expected_ar = (1.1754943508222875e-38,)
expected_ar = (1.401298464324817e-45,)
assert data_ar == expected_ar

data = ds.GetRasterBand(1).ReadRaster(
buf_xsize=1, buf_ysize=1, resample_alg=gdal.GRIORA_Average
)
data_ar = struct.unpack("f" * 1, data)
expected_ar = (1.1754943508222875e-38,)
expected_ar = (1.401298464324817e-45,)
assert data_ar == expected_ar

gdal.Unlink("/vsimem/in.asc")
Expand Down
2 changes: 2 additions & 0 deletions gcore/gdal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1705,6 +1705,8 @@ void CPL_DLL GDALDeinterleave(const void *pSourceBuffer, GDALDataType eSourceDT,
int nComponents, void **ppDestBuffer,
GDALDataType eDestDT, size_t nIters);

double CPL_DLL GDALGetNoDataReplacementValue(GDALDataType, double);

int CPL_DLL CPL_STDCALL GDALLoadWorldFile(const char *, double *);
int CPL_DLL CPL_STDCALL GDALReadWorldFile(const char *, const char *, double *);
int CPL_DLL CPL_STDCALL GDALWriteWorldFile(const char *, const char *,
Expand Down
Loading

0 comments on commit efccb78

Please sign in to comment.