Skip to content

Commit

Permalink
Merge pull request #10593 from OSGeo/backport-10589-to-release/3.9
Browse files Browse the repository at this point in the history
[Backport release/3.9] VRT: fix reading from virtual overviews when SrcRect / DstRect elements are missing
  • Loading branch information
rouault committed Aug 13, 2024
2 parents 028f632 + 61eea4e commit e1871ff
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 46 deletions.
30 changes: 30 additions & 0 deletions autotest/gcore/vrt_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,36 @@ def test_vrt_read_21():
gdal.Unlink("/vsimem/byte.tif")


###############################################################################
# Test implicit virtual overviews


def test_vrt_read_virtual_overview_no_srcrect_dstrect(tmp_vsimem):

ds = gdal.Open("data/byte.tif")
data = ds.ReadRaster(0, 0, 20, 20, 400, 400)
ds = None
tmp_tif = str(tmp_vsimem / "tmp.tif")
ds = gdal.GetDriverByName("GTiff").Create(tmp_tif, 400, 400)
ds.WriteRaster(0, 0, 400, 400, data)
ds.BuildOverviews("NEAR", [2])
ref_cs = ds.GetRasterBand(1).GetOverview(0).Checksum()
ds = None

ds = gdal.Open(
f"""<VRTDataset rasterXSize="400" rasterYSize="400">
<VRTRasterBand dataType="Byte" band="1">
<SimpleSource>
<SourceFilename>{tmp_tif}</SourceFilename>
<SourceBand>1</SourceBand>
</SimpleSource>
</VRTRasterBand>
</VRTDataset>"""
)
assert ds.GetRasterBand(1).GetOverviewCount() == 1
assert ds.GetRasterBand(1).GetOverview(0).Checksum() == ref_cs


###############################################################################
# Test that we honour NBITS with SimpleSource and ComplexSource

Expand Down
11 changes: 8 additions & 3 deletions autotest/gdrivers/vrtprocesseddataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,19 +676,20 @@ def test_vrtprocesseddataset_dehazing_different_resolution(tmp_vsimem):
src_ds.GetRasterBand(1).WriteArray(
np.array([[1, 1, 2, 2, 3, 3], [1, 1, 2, 2, 3, 3]])
)
src_ds.SetGeoTransform([0, 0.5, 0, 0, 0, 0.5])
src_ds.SetGeoTransform([0, 0.5 * 10, 0, 0, 0, 0.5 * 10])
src_ds.BuildOverviews("NEAR", [2])
src_ds.Close()

gain_filename = str(tmp_vsimem / "gain.tif")
gain_ds = gdal.GetDriverByName("GTiff").Create(gain_filename, 3, 1, 1)
gain_ds.GetRasterBand(1).WriteArray(np.array([[2, 4, 6]]))
gain_ds.SetGeoTransform([0, 1, 0, 0, 0, 1])
gain_ds.SetGeoTransform([0, 1 * 10, 0, 0, 0, 1 * 10])
gain_ds.Close()

offset_filename = str(tmp_vsimem / "offset.tif")
offset_ds = gdal.GetDriverByName("GTiff").Create(offset_filename, 3, 1, 1)
offset_ds.GetRasterBand(1).WriteArray(np.array([[1, 2, 3]]))
offset_ds.SetGeoTransform([0, 1, 0, 0, 0, 1])
offset_ds.SetGeoTransform([0, 1 * 10, 0, 0, 0, 1 * 10])
offset_ds.Close()

ds = gdal.Open(
Expand All @@ -712,6 +713,10 @@ def test_vrtprocesseddataset_dehazing_different_resolution(tmp_vsimem):
ds.GetRasterBand(1).ReadAsArray(),
np.array([[1, 2, 6, 8, 15, 15], [1, 2, 6, 8, 15, 15]]),
)
np.testing.assert_equal(
ds.GetRasterBand(1).GetOverview(0).ReadAsArray(),
np.array([[1, 6, 15]]),
)


###############################################################################
Expand Down
38 changes: 29 additions & 9 deletions frmts/vrt/vrtdataset.h
Original file line number Diff line number Diff line change
Expand Up @@ -1236,15 +1236,21 @@ class CPL_DLL VRTSimpleSource CPL_NON_FINAL : public VRTSource
int m_nBand = 0;
bool m_bGetMaskBand = false;

double m_dfSrcXOff = 0;
double m_dfSrcYOff = 0;
double m_dfSrcXSize = 0;
double m_dfSrcYSize = 0;

double m_dfDstXOff = 0;
double m_dfDstYOff = 0;
double m_dfDstXSize = 0;
double m_dfDstYSize = 0;
/* Value for uninitialized source or destination window. It is chosen such
* that SrcToDst() and DstToSrc() are no-ops if both source and destination
* windows are unset.
*/
static constexpr double UNINIT_WINDOW = -1.0;

double m_dfSrcXOff = UNINIT_WINDOW;
double m_dfSrcYOff = UNINIT_WINDOW;
double m_dfSrcXSize = UNINIT_WINDOW;
double m_dfSrcYSize = UNINIT_WINDOW;

double m_dfDstXOff = UNINIT_WINDOW;
double m_dfDstYOff = UNINIT_WINDOW;
double m_dfDstXSize = UNINIT_WINDOW;
double m_dfDstYSize = UNINIT_WINDOW;

CPLString m_osResampling{};

Expand Down Expand Up @@ -1275,6 +1281,20 @@ class CPL_DLL VRTSimpleSource CPL_NON_FINAL : public VRTSource
return true;
}

/** Returns whether the source window is set */
bool IsSrcWinSet() const
{
return m_dfSrcXOff != UNINIT_WINDOW || m_dfSrcYOff != UNINIT_WINDOW ||
m_dfSrcXSize != UNINIT_WINDOW || m_dfSrcYSize != UNINIT_WINDOW;
}

/** Returns whether the destination window is set */
bool IsDstWinSet() const
{
return m_dfDstXOff != UNINIT_WINDOW || m_dfDstYOff != UNINIT_WINDOW ||
m_dfDstXSize != UNINIT_WINDOW || m_dfDstYSize != UNINIT_WINDOW;
}

public:
VRTSimpleSource();
VRTSimpleSource(const VRTSimpleSource *poSrcSource, double dfXDstRatio,
Expand Down
94 changes: 60 additions & 34 deletions frmts/vrt/vrtsources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,34 @@ VRTSimpleSource::VRTSimpleSource(const VRTSimpleSource *poSrcSource,
m_dfSrcYOff(poSrcSource->m_dfSrcYOff),
m_dfSrcXSize(poSrcSource->m_dfSrcXSize),
m_dfSrcYSize(poSrcSource->m_dfSrcYSize),
m_dfDstXOff(poSrcSource->m_dfDstXOff * dfXDstRatio),
m_dfDstYOff(poSrcSource->m_dfDstYOff * dfYDstRatio),
m_dfDstXSize(poSrcSource->m_dfDstXSize * dfXDstRatio),
m_dfDstYSize(poSrcSource->m_dfDstYSize * dfYDstRatio),
m_nMaxValue(poSrcSource->m_nMaxValue), m_bRelativeToVRTOri(-1),
m_nExplicitSharedStatus(poSrcSource->m_nExplicitSharedStatus),
m_osSrcDSName(poSrcSource->m_osSrcDSName),
m_bDropRefOnSrcBand(poSrcSource->m_bDropRefOnSrcBand)
{
if (!poSrcSource->IsSrcWinSet() && !poSrcSource->IsDstWinSet() &&
(dfXDstRatio != 1.0 || dfYDstRatio != 1.0))
{
auto l_band = GetRasterBand();
if (l_band)
{
m_dfSrcXOff = 0;
m_dfSrcYOff = 0;
m_dfSrcXSize = l_band->GetXSize();
m_dfSrcYSize = l_band->GetYSize();
m_dfDstXOff = 0;
m_dfDstYOff = 0;
m_dfDstXSize = l_band->GetXSize() * dfXDstRatio;
m_dfDstYSize = l_band->GetYSize() * dfYDstRatio;
}
}
else if (poSrcSource->IsDstWinSet())
{
m_dfDstXOff = poSrcSource->m_dfDstXOff * dfXDstRatio;
m_dfDstYOff = poSrcSource->m_dfDstYOff * dfYDstRatio;
m_dfDstXSize = poSrcSource->m_dfDstXSize * dfXDstRatio;
m_dfDstYSize = poSrcSource->m_dfDstYSize * dfYDstRatio;
}
}

/************************************************************************/
Expand Down Expand Up @@ -470,8 +489,7 @@ CPLXMLNode *VRTSimpleSource::SerializeToXML(const char *pszVRTPath)
CPLSPrintf("%d", nBlockYSize));
}

if (m_dfSrcXOff != -1 || m_dfSrcYOff != -1 || m_dfSrcXSize != -1 ||
m_dfSrcYSize != -1)
if (IsSrcWinSet())
{
CPLSetXMLValue(psSrc, "SrcRect.#xOff",
CPLSPrintf("%.15g", m_dfSrcXOff));
Expand All @@ -483,8 +501,7 @@ CPLXMLNode *VRTSimpleSource::SerializeToXML(const char *pszVRTPath)
CPLSPrintf("%.15g", m_dfSrcYSize));
}

if (m_dfDstXOff != -1 || m_dfDstYOff != -1 || m_dfDstXSize != -1 ||
m_dfDstYSize != -1)
if (IsDstWinSet())
{
CPLSetXMLValue(psSrc, "DstRect.#xOff",
CPLSPrintf("%.15g", m_dfDstXOff));
Expand Down Expand Up @@ -581,21 +598,30 @@ VRTSimpleSource::XMLInit(const CPLXMLNode *psSrc, const char *pszVRTPath,

CPLErr VRTSimpleSource::ParseSrcRectAndDstRect(const CPLXMLNode *psSrc)
{
const auto GetAttrValue = [](const CPLXMLNode *psNode,
const char *pszAttrName, double dfDefaultVal)
{
if (const char *pszVal = CPLGetXMLValue(psNode, pszAttrName, nullptr))
return CPLAtof(pszVal);
else
return dfDefaultVal;
};

/* -------------------------------------------------------------------- */
/* Set characteristics. */
/* -------------------------------------------------------------------- */
const CPLXMLNode *const psSrcRect = CPLGetXMLNode(psSrc, "SrcRect");
if (psSrcRect)
{
double xOff = CPLAtof(CPLGetXMLValue(psSrcRect, "xOff", "-1"));
double yOff = CPLAtof(CPLGetXMLValue(psSrcRect, "yOff", "-1"));
double xSize = CPLAtof(CPLGetXMLValue(psSrcRect, "xSize", "-1"));
double ySize = CPLAtof(CPLGetXMLValue(psSrcRect, "ySize", "-1"));
double xOff = GetAttrValue(psSrcRect, "xOff", UNINIT_WINDOW);
double yOff = GetAttrValue(psSrcRect, "yOff", UNINIT_WINDOW);
double xSize = GetAttrValue(psSrcRect, "xSize", UNINIT_WINDOW);
double ySize = GetAttrValue(psSrcRect, "ySize", UNINIT_WINDOW);
// Test written that way to catch NaN values
if (!(xOff >= INT_MIN && xOff <= INT_MAX) ||
!(yOff >= INT_MIN && yOff <= INT_MAX) ||
!(xSize > 0 || xSize == -1) || xSize > INT_MAX ||
!(ySize > 0 || ySize == -1) || ySize > INT_MAX)
!(xSize > 0 || xSize == UNINIT_WINDOW) || xSize > INT_MAX ||
!(ySize > 0 || ySize == UNINIT_WINDOW) || ySize > INT_MAX)
{
CPLError(CE_Failure, CPLE_AppDefined, "Wrong values in SrcRect");
return CE_Failure;
Expand All @@ -604,24 +630,26 @@ CPLErr VRTSimpleSource::ParseSrcRectAndDstRect(const CPLXMLNode *psSrc)
}
else
{
m_dfSrcXOff = -1;
m_dfSrcYOff = -1;
m_dfSrcXSize = -1;
m_dfSrcYSize = -1;
m_dfSrcXOff = UNINIT_WINDOW;
m_dfSrcYOff = UNINIT_WINDOW;
m_dfSrcXSize = UNINIT_WINDOW;
m_dfSrcYSize = UNINIT_WINDOW;
}

const CPLXMLNode *const psDstRect = CPLGetXMLNode(psSrc, "DstRect");
if (psDstRect)
{
double xOff = CPLAtof(CPLGetXMLValue(psDstRect, "xOff", "-1"));
double yOff = CPLAtof(CPLGetXMLValue(psDstRect, "yOff", "-1"));
double xSize = CPLAtof(CPLGetXMLValue(psDstRect, "xSize", "-1"));
double ySize = CPLAtof(CPLGetXMLValue(psDstRect, "ySize", "-1"));
double xOff = GetAttrValue(psDstRect, "xOff", UNINIT_WINDOW);
;
double yOff = GetAttrValue(psDstRect, "yOff", UNINIT_WINDOW);
double xSize = GetAttrValue(psDstRect, "xSize", UNINIT_WINDOW);
;
double ySize = GetAttrValue(psDstRect, "ySize", UNINIT_WINDOW);
// Test written that way to catch NaN values
if (!(xOff >= INT_MIN && xOff <= INT_MAX) ||
!(yOff >= INT_MIN && yOff <= INT_MAX) ||
!(xSize > 0 || xSize == -1) || xSize > INT_MAX ||
!(ySize > 0 || ySize == -1) || ySize > INT_MAX)
!(xSize > 0 || xSize == UNINIT_WINDOW) || xSize > INT_MAX ||
!(ySize > 0 || ySize == UNINIT_WINDOW) || ySize > INT_MAX)
{
CPLError(CE_Failure, CPLE_AppDefined, "Wrong values in DstRect");
return CE_Failure;
Expand All @@ -630,10 +658,10 @@ CPLErr VRTSimpleSource::ParseSrcRectAndDstRect(const CPLXMLNode *psSrc)
}
else
{
m_dfDstXOff = -1;
m_dfDstYOff = -1;
m_dfDstXSize = -1;
m_dfDstYSize = -1;
m_dfDstXOff = UNINIT_WINDOW;
m_dfDstYOff = UNINIT_WINDOW;
m_dfDstXSize = UNINIT_WINDOW;
m_dfDstYSize = UNINIT_WINDOW;
}

return CE_None;
Expand Down Expand Up @@ -807,7 +835,7 @@ int VRTSimpleSource::IsSameExceptBandNumber(VRTSimpleSource *poOtherSource)
/************************************************************************/
/* SrcToDst() */
/* */
/* Note: this is a no-op if the dst window is -1,-1,-1,-1. */
/* Note: this is a no-op if the both src and dst windows are unset */
/************************************************************************/

void VRTSimpleSource::SrcToDst(double dfX, double dfY, double &dfXOut,
Expand All @@ -821,7 +849,7 @@ void VRTSimpleSource::SrcToDst(double dfX, double dfY, double &dfXOut,
/************************************************************************/
/* DstToSrc() */
/* */
/* Note: this is a no-op if the dst window is -1,-1,-1,-1. */
/* Note: this is a no-op if the both src and dst windows are unset */
/************************************************************************/

void VRTSimpleSource::DstToSrc(double dfX, double dfY, double &dfXOut,
Expand Down Expand Up @@ -852,12 +880,10 @@ int VRTSimpleSource::GetSrcDstWindow(
return FALSE;
}

const bool bDstWinSet = m_dfDstXOff != -1 || m_dfDstXSize != -1 ||
m_dfDstYOff != -1 || m_dfDstYSize != -1;
const bool bDstWinSet = IsDstWinSet();

#ifdef DEBUG
const bool bSrcWinSet = m_dfSrcXOff != -1 || m_dfSrcXSize != -1 ||
m_dfSrcYOff != -1 || m_dfSrcYSize != -1;
const bool bSrcWinSet = IsSrcWinSet();

if (bSrcWinSet != bDstWinSet)
{
Expand Down

0 comments on commit e1871ff

Please sign in to comment.