Skip to content

Commit 7877247

Browse files
authored
Merge pull request #13902 from rouault/fix_13862
VRTDerivedRasterBand::IRasterIO(): fix output buffer zero intialization when nLineSpace != nBufXSize * nPixelSpace
2 parents 37c08e4 + 8335f79 commit 7877247

File tree

4 files changed

+221
-124
lines changed

4 files changed

+221
-124
lines changed

autotest/gdrivers/vrtderived.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2504,3 +2504,39 @@ def test_vrt_derived_virtual_overviews(tmp_vsimem):
25042504
with gdaltest.error_raised(gdal.CE_None):
25052505
got = mask_band.ReadRaster(0, 0, width, height, 1, 1)
25062506
assert got == b"\x02"
2507+
2508+
2509+
def test_vrt_derived_zero_initialization(tmp_vsimem):
2510+
2511+
vrt_w = 20
2512+
vrt_h = 10
2513+
tile_w = vrt_w // 2
2514+
tile_h = vrt_h
2515+
tile_offset = vrt_w - tile_w
2516+
with gdal.GetDriverByName("GTiff").Create(
2517+
tmp_vsimem / "src1.tif", tile_w, tile_h, 1, gdal.GDT_UInt8
2518+
) as src:
2519+
src.WriteRaster(0, 0, tile_w, tile_h, b"\x01" * (tile_w * tile_h))
2520+
2521+
xml = f"""
2522+
<VRTDataset rasterXSize="{vrt_w}" rasterYSize="{vrt_h}">
2523+
<VRTRasterBand dataType="Byte" band="1" subclass="VRTDerivedRasterBand">
2524+
<PixelFunctionType>sum</PixelFunctionType>
2525+
<SimpleSource>
2526+
<SourceFilename>{tmp_vsimem / "src1.tif"}</SourceFilename>
2527+
<SourceBand>1</SourceBand>
2528+
<SrcRect xOff="0" yOff="0" xSize="{tile_w}" ySize="{tile_h}" />
2529+
<DstRect xOff="{tile_offset}" yOff="0" xSize="{tile_w}" ySize="{tile_h}" />
2530+
</SimpleSource>
2531+
</VRTRasterBand>
2532+
</VRTDataset>"""
2533+
2534+
buf_obj = bytearray(b"\xFF" * ((vrt_h - 1) * vrt_w + tile_offset))
2535+
got = gdal.Open(xml).ReadRaster(
2536+
0, 0, tile_offset, vrt_h, buf_obj=buf_obj, buf_line_space=vrt_w
2537+
)
2538+
assert (
2539+
got
2540+
== (b"\x00" * tile_offset + b"\xFF" * tile_w) * (vrt_h - 1)
2541+
+ b"\x00" * tile_offset
2542+
)

frmts/vrt/vrtdataset.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,10 @@ class CPL_DLL VRTSourcedRasterBand CPL_NON_FINAL : public VRTRasterBand
955955
protected:
956956
bool SkipBufferInitialization();
957957

958+
void InitializeOutputBuffer(void *pData, int nBufXSize, int nBufYSize,
959+
GDALDataType eBufType, GSpacing nPixelSpace,
960+
GSpacing nLineSpace) const;
961+
958962
public:
959963
std::vector<std::unique_ptr<VRTSource>> m_papoSources{};
960964

frmts/vrt/vrtderivedrasterband.cpp

Lines changed: 113 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,56 @@ CPLErr VRTDerivedRasterBand::GetPixelFunctionArguments(
982982
return CE_None;
983983
}
984984

985+
/************************************************************************/
986+
/* CreateMapBufferIdxToSourceIdx() */
987+
/************************************************************************/
988+
989+
static bool CreateMapBufferIdxToSourceIdx(
990+
const std::vector<std::unique_ptr<VRTSource>> &apoSources,
991+
bool bSkipNonContributingSources, int nXOff, int nYOff, int nXSize,
992+
int nYSize, int nBufXSize, int nBufYSize, GDALRasterIOExtraArg *psExtraArg,
993+
bool &bCreateMapBufferIdxToSourceIdxHasRun,
994+
std::vector<int> &anMapBufferIdxToSourceIdx,
995+
bool &bSkipOutputBufferInitialization)
996+
{
997+
CPLAssert(!bCreateMapBufferIdxToSourceIdxHasRun);
998+
bCreateMapBufferIdxToSourceIdxHasRun = true;
999+
anMapBufferIdxToSourceIdx.reserve(apoSources.size());
1000+
for (int iSource = 0; iSource < static_cast<int>(apoSources.size());
1001+
iSource++)
1002+
{
1003+
if (bSkipNonContributingSources &&
1004+
apoSources[iSource]->IsSimpleSource())
1005+
{
1006+
bool bError = false;
1007+
double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize;
1008+
int nReqXOff, nReqYOff, nReqXSize, nReqYSize;
1009+
int nOutXOff, nOutYOff, nOutXSize, nOutYSize;
1010+
auto poSource =
1011+
static_cast<VRTSimpleSource *>(apoSources[iSource].get());
1012+
if (!poSource->GetSrcDstWindow(
1013+
nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
1014+
psExtraArg->eResampleAlg, &dfReqXOff, &dfReqYOff,
1015+
&dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff, &nReqXSize,
1016+
&nReqYSize, &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize,
1017+
bError))
1018+
{
1019+
if (bError)
1020+
{
1021+
return false;
1022+
}
1023+
1024+
// Skip non contributing source
1025+
bSkipOutputBufferInitialization = false;
1026+
continue;
1027+
}
1028+
}
1029+
1030+
anMapBufferIdxToSourceIdx.push_back(iSource);
1031+
}
1032+
return true;
1033+
}
1034+
9851035
/************************************************************************/
9861036
/* IRasterIO() */
9871037
/************************************************************************/
@@ -1142,6 +1192,10 @@ CPLErr VRTDerivedRasterBand::IRasterIO(
11421192
}
11431193
const int nSrcTypeSize = GDALGetDataTypeSizeBytes(eSrcType);
11441194

1195+
std::vector<int> anMapBufferIdxToSourceIdx;
1196+
bool bSkipOutputBufferInitialization = !m_papoSources.empty();
1197+
bool bCreateMapBufferIdxToSourceIdxHasRun = false;
1198+
11451199
// If acquiring the region of interest in a single time is going
11461200
// to consume too much RAM, split in halves, and that recursively
11471201
// until we get below m_nAllowedRAMUsage.
@@ -1151,11 +1205,34 @@ CPLErr VRTDerivedRasterBand::IRasterIO(
11511205
m_poPrivate->m_nAllowedRAMUsage /
11521206
(static_cast<int>(m_papoSources.size()) * nSrcTypeSize))
11531207
{
1154-
CPLErr eErr = SplitRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1155-
pData, nBufXSize, nBufYSize, eBufType,
1156-
nPixelSpace, nLineSpace, psExtraArg);
1157-
if (eErr != CE_Warning)
1158-
return eErr;
1208+
bool bSplit = true;
1209+
if (m_poPrivate->m_bSkipNonContributingSources)
1210+
{
1211+
// More accurate check by comparing against the number of
1212+
// actually contributing sources.
1213+
if (!CreateMapBufferIdxToSourceIdx(
1214+
m_papoSources, m_poPrivate->m_bSkipNonContributingSources,
1215+
nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
1216+
psExtraArg, bCreateMapBufferIdxToSourceIdxHasRun,
1217+
anMapBufferIdxToSourceIdx, bSkipOutputBufferInitialization))
1218+
{
1219+
return CE_Failure;
1220+
}
1221+
bSplit =
1222+
!anMapBufferIdxToSourceIdx.empty() &&
1223+
static_cast<GIntBig>(nBufXSize) * nBufYSize >
1224+
m_poPrivate->m_nAllowedRAMUsage /
1225+
(static_cast<int>(anMapBufferIdxToSourceIdx.size()) *
1226+
nSrcTypeSize);
1227+
}
1228+
if (bSplit)
1229+
{
1230+
CPLErr eErr = SplitRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1231+
pData, nBufXSize, nBufYSize, eBufType,
1232+
nPixelSpace, nLineSpace, psExtraArg);
1233+
if (eErr != CE_Warning)
1234+
return eErr;
1235+
}
11591236
}
11601237

11611238
/* ---- Get pixel function for band ---- */
@@ -1196,51 +1273,32 @@ CPLErr VRTDerivedRasterBand::IRasterIO(
11961273
}
11971274
const int nExtBufXSize = nBufXSize + 2 * nBufferRadius;
11981275
const int nExtBufYSize = nBufYSize + 2 * nBufferRadius;
1199-
int nBufferCount = 0;
12001276

1201-
std::vector<std::unique_ptr<void, VSIFreeReleaser>> apBuffers(
1202-
m_papoSources.size());
1203-
std::vector<int> anMapBufferIdxToSourceIdx(m_papoSources.size());
1204-
bool bSkipOutputBufferInitialization = !m_papoSources.empty();
1205-
for (int iSource = 0; iSource < static_cast<int>(m_papoSources.size());
1206-
iSource++)
1277+
if (!bCreateMapBufferIdxToSourceIdxHasRun)
12071278
{
1208-
if (m_poPrivate->m_bSkipNonContributingSources &&
1209-
m_papoSources[iSource]->IsSimpleSource())
1279+
if (!CreateMapBufferIdxToSourceIdx(
1280+
m_papoSources, m_poPrivate->m_bSkipNonContributingSources,
1281+
nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, psExtraArg,
1282+
bCreateMapBufferIdxToSourceIdxHasRun, anMapBufferIdxToSourceIdx,
1283+
bSkipOutputBufferInitialization))
12101284
{
1211-
bool bError = false;
1212-
double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize;
1213-
int nReqXOff, nReqYOff, nReqXSize, nReqYSize;
1214-
int nOutXOff, nOutYOff, nOutXSize, nOutYSize;
1215-
auto poSource =
1216-
static_cast<VRTSimpleSource *>(m_papoSources[iSource].get());
1217-
if (!poSource->GetSrcDstWindow(
1218-
nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
1219-
psExtraArg->eResampleAlg, &dfReqXOff, &dfReqYOff,
1220-
&dfReqXSize, &dfReqYSize, &nReqXOff, &nReqYOff, &nReqXSize,
1221-
&nReqYSize, &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize,
1222-
bError))
1223-
{
1224-
if (bError)
1225-
{
1226-
return CE_Failure;
1227-
}
1228-
1229-
// Skip non contributing source
1230-
bSkipOutputBufferInitialization = false;
1231-
continue;
1232-
}
1285+
return CE_Failure;
12331286
}
1234-
1235-
anMapBufferIdxToSourceIdx[nBufferCount] = iSource;
1236-
apBuffers[nBufferCount].reset(
1287+
}
1288+
std::vector<std::unique_ptr<void, VSIFreeReleaser>> apBuffers(
1289+
anMapBufferIdxToSourceIdx.size());
1290+
for (size_t iBuffer = 0; iBuffer < anMapBufferIdxToSourceIdx.size();
1291+
++iBuffer)
1292+
{
1293+
apBuffers[iBuffer].reset(
12371294
VSI_MALLOC3_VERBOSE(nSrcTypeSize, nExtBufXSize, nExtBufYSize));
1238-
if (apBuffers[nBufferCount] == nullptr)
1295+
if (apBuffers[iBuffer] == nullptr)
12391296
{
12401297
return CE_Failure;
12411298
}
12421299

12431300
bool bBufferInit = true;
1301+
const int iSource = anMapBufferIdxToSourceIdx[iBuffer];
12441302
if (m_papoSources[iSource]->IsSimpleSource())
12451303
{
12461304
const auto poSS =
@@ -1279,53 +1337,36 @@ CPLErr VRTDerivedRasterBand::IRasterIO(
12791337
/* ------------------------------------------------------------ */
12801338
if (!m_bNoDataValueSet || m_dfNoDataValue == 0)
12811339
{
1282-
memset(apBuffers[nBufferCount].get(), 0,
1340+
memset(apBuffers[iBuffer].get(), 0,
12831341
static_cast<size_t>(nSrcTypeSize) * nExtBufXSize *
12841342
nExtBufYSize);
12851343
}
12861344
else
12871345
{
1288-
GDALCopyWords64(
1289-
&m_dfNoDataValue, GDT_Float64, 0,
1290-
static_cast<GByte *>(apBuffers[nBufferCount].get()),
1291-
eSrcType, nSrcTypeSize,
1292-
static_cast<GPtrDiff_t>(nExtBufXSize) * nExtBufYSize);
1346+
GDALCopyWords64(&m_dfNoDataValue, GDT_Float64, 0,
1347+
static_cast<GByte *>(apBuffers[iBuffer].get()),
1348+
eSrcType, nSrcTypeSize,
1349+
static_cast<GPtrDiff_t>(nExtBufXSize) *
1350+
nExtBufYSize);
12931351
}
12941352
}
1295-
1296-
++nBufferCount;
12971353
}
12981354

12991355
/* -------------------------------------------------------------------- */
13001356
/* Initialize the buffer to some background value. Use the */
13011357
/* nodata value if available. */
13021358
/* -------------------------------------------------------------------- */
1303-
if (bSkipOutputBufferInitialization)
1304-
{
1305-
// Do nothing
1306-
}
1307-
else if (nPixelSpace == nBufTypeSize &&
1308-
(!m_bNoDataValueSet || m_dfNoDataValue == 0))
1359+
if (!bSkipOutputBufferInitialization)
13091360
{
1310-
memset(pData, 0,
1311-
static_cast<size_t>(nBufXSize) * nBufYSize * nBufTypeSize);
1312-
}
1313-
else if (m_bNoDataValueSet)
1314-
{
1315-
double dfWriteValue = m_dfNoDataValue;
1316-
1317-
for (int iLine = 0; iLine < nBufYSize; iLine++)
1318-
{
1319-
GDALCopyWords64(&dfWriteValue, GDT_Float64, 0,
1320-
static_cast<GByte *>(pData) + nLineSpace * iLine,
1321-
eBufType, static_cast<int>(nPixelSpace), nBufXSize);
1322-
}
1361+
InitializeOutputBuffer(pData, nBufXSize, nBufYSize, eBufType,
1362+
nPixelSpace, nLineSpace);
13231363
}
13241364

13251365
// No contributing sources and SkipNonContributingSources mode ?
13261366
// Do not call the pixel function and just return the 0/nodata initialized
13271367
// output buffer.
1328-
if (nBufferCount == 0 && m_poPrivate->m_bSkipNonContributingSources)
1368+
if (anMapBufferIdxToSourceIdx.empty() &&
1369+
m_poPrivate->m_bSkipNonContributingSources)
13291370
{
13301371
return CE_None;
13311372
}
@@ -1400,7 +1441,9 @@ CPLErr VRTDerivedRasterBand::IRasterIO(
14001441
// Load values for sources into packed buffers.
14011442
CPLErr eErr = CE_None;
14021443
VRTSource::WorkingState oWorkingState;
1403-
for (int iBuffer = 0; iBuffer < nBufferCount && eErr == CE_None; iBuffer++)
1444+
for (size_t iBuffer = 0;
1445+
iBuffer < anMapBufferIdxToSourceIdx.size() && eErr == CE_None;
1446+
iBuffer++)
14041447
{
14051448
const int iSource = anMapBufferIdxToSourceIdx[iBuffer];
14061449
GByte *pabyBuffer = static_cast<GByte *>(apBuffers[iBuffer].get());
@@ -1486,6 +1529,7 @@ CPLErr VRTDerivedRasterBand::IRasterIO(
14861529
}
14871530

14881531
// Apply pixel function.
1532+
const int nBufferCount = static_cast<int>(anMapBufferIdxToSourceIdx.size());
14891533
if (eErr == CE_None && EQUAL(m_poPrivate->m_osLanguage, "Python"))
14901534
{
14911535
// numpy doesn't have native cint16/cint32/cfloat16

0 commit comments

Comments
 (0)