Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Virtual GDAL Datasets
4 : * Purpose: Implementation of VRTSimpleSource, VRTFuncSource and
5 : * VRTAveragedSource.
6 : * Author: Frank Warmerdam <warmerdam@pobox.com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
10 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "gdal_vrt.h"
32 : #include "vrtdataset.h"
33 :
34 : #include <cassert>
35 : #include <climits>
36 : #include <cmath>
37 : #include <cstddef>
38 : #include <cstdio>
39 : #include <cstdlib>
40 : #include <cstring>
41 : #include <algorithm>
42 : #include <limits>
43 : #include <string>
44 :
45 : #include "cpl_conv.h"
46 : #include "cpl_error.h"
47 : #include "cpl_hash_set.h"
48 : #include "cpl_minixml.h"
49 : #include "cpl_progress.h"
50 : #include "cpl_string.h"
51 : #include "cpl_vsi.h"
52 : #include "gdal.h"
53 : #include "gdal_priv.h"
54 : #include "gdal_proxy.h"
55 : #include "gdal_priv_templates.hpp"
56 :
57 : /*! @cond Doxygen_Suppress */
58 :
59 : // #define DEBUG_VERBOSE 1
60 :
61 : // See #5459
62 : #ifdef isnan
63 : #define HAS_ISNAN_MACRO
64 : #endif
65 : #include <algorithm>
66 : #if defined(HAS_ISNAN_MACRO) && !defined(isnan)
67 : #define isnan std::isnan
68 : #endif
69 :
70 : /************************************************************************/
71 : /* ==================================================================== */
72 : /* VRTSource */
73 : /* ==================================================================== */
74 : /************************************************************************/
75 :
76 7217 : VRTSource::~VRTSource()
77 : {
78 7217 : }
79 :
80 : /************************************************************************/
81 : /* GetFileList() */
82 : /************************************************************************/
83 :
84 0 : void VRTSource::GetFileList(char *** /* ppapszFileList */, int * /* pnSize */,
85 : int * /* pnMaxSize */, CPLHashSet * /* hSetFiles */)
86 : {
87 0 : }
88 :
89 : /************************************************************************/
90 : /* ==================================================================== */
91 : /* VRTSimpleSource */
92 : /* ==================================================================== */
93 : /************************************************************************/
94 :
95 : /************************************************************************/
96 : /* VRTSimpleSource() */
97 : /************************************************************************/
98 :
99 : VRTSimpleSource::VRTSimpleSource() = default;
100 :
101 : /************************************************************************/
102 : /* VRTSimpleSource() */
103 : /************************************************************************/
104 :
105 39 : VRTSimpleSource::VRTSimpleSource(const VRTSimpleSource *poSrcSource,
106 39 : double dfXDstRatio, double dfYDstRatio)
107 39 : : m_poMapSharedSources(poSrcSource->m_poMapSharedSources),
108 39 : m_poRasterBand(poSrcSource->m_poRasterBand),
109 39 : m_poMaskBandMainBand(poSrcSource->m_poMaskBandMainBand),
110 39 : m_aosOpenOptions(poSrcSource->m_aosOpenOptions),
111 39 : m_nBand(poSrcSource->m_nBand),
112 39 : m_bGetMaskBand(poSrcSource->m_bGetMaskBand),
113 39 : m_dfSrcXOff(poSrcSource->m_dfSrcXOff),
114 39 : m_dfSrcYOff(poSrcSource->m_dfSrcYOff),
115 39 : m_dfSrcXSize(poSrcSource->m_dfSrcXSize),
116 39 : m_dfSrcYSize(poSrcSource->m_dfSrcYSize),
117 39 : m_dfDstXOff(poSrcSource->m_dfDstXOff * dfXDstRatio),
118 39 : m_dfDstYOff(poSrcSource->m_dfDstYOff * dfYDstRatio),
119 39 : m_dfDstXSize(poSrcSource->m_dfDstXSize * dfXDstRatio),
120 39 : m_dfDstYSize(poSrcSource->m_dfDstYSize * dfYDstRatio),
121 39 : m_nMaxValue(poSrcSource->m_nMaxValue), m_bRelativeToVRTOri(-1),
122 39 : m_nExplicitSharedStatus(poSrcSource->m_nExplicitSharedStatus),
123 39 : m_osSrcDSName(poSrcSource->m_osSrcDSName),
124 39 : m_bDropRefOnSrcBand(poSrcSource->m_bDropRefOnSrcBand)
125 : {
126 39 : }
127 :
128 : /************************************************************************/
129 : /* ~VRTSimpleSource() */
130 : /************************************************************************/
131 :
132 14576 : VRTSimpleSource::~VRTSimpleSource()
133 :
134 : {
135 7186 : if (!m_bDropRefOnSrcBand)
136 186 : return;
137 :
138 7000 : if (m_poMaskBandMainBand != nullptr)
139 : {
140 43 : if (m_poMaskBandMainBand->GetDataset() != nullptr)
141 : {
142 43 : m_poMaskBandMainBand->GetDataset()->ReleaseRef();
143 : }
144 : }
145 12714 : else if (m_poRasterBand != nullptr &&
146 5757 : m_poRasterBand->GetDataset() != nullptr)
147 : {
148 5757 : m_poRasterBand->GetDataset()->ReleaseRef();
149 : }
150 13832 : }
151 :
152 : /************************************************************************/
153 : /* FlushCache() */
154 : /************************************************************************/
155 :
156 7458 : CPLErr VRTSimpleSource::FlushCache(bool bAtClosing)
157 :
158 : {
159 7458 : if (m_poMaskBandMainBand != nullptr)
160 : {
161 5 : return m_poMaskBandMainBand->FlushCache(bAtClosing);
162 : }
163 7453 : else if (m_poRasterBand != nullptr)
164 : {
165 6235 : return m_poRasterBand->FlushCache(bAtClosing);
166 : }
167 1218 : return CE_None;
168 : }
169 :
170 : /************************************************************************/
171 : /* UnsetPreservedRelativeFilenames() */
172 : /************************************************************************/
173 :
174 6 : void VRTSimpleSource::UnsetPreservedRelativeFilenames()
175 : {
176 11 : if (!STARTS_WITH(m_osSourceFileNameOri.c_str(), "http://") &&
177 5 : !STARTS_WITH(m_osSourceFileNameOri.c_str(), "https://"))
178 : {
179 5 : m_bRelativeToVRTOri = -1;
180 5 : m_osSourceFileNameOri = "";
181 : }
182 6 : }
183 :
184 : /************************************************************************/
185 : /* SetSrcBand() */
186 : /************************************************************************/
187 :
188 422 : void VRTSimpleSource::SetSrcBand(const char *pszFilename, int nBand)
189 :
190 : {
191 422 : m_nBand = nBand;
192 422 : m_osSrcDSName = pszFilename;
193 422 : }
194 :
195 : /************************************************************************/
196 : /* SetSrcBand() */
197 : /************************************************************************/
198 :
199 5090 : void VRTSimpleSource::SetSrcBand(GDALRasterBand *poNewSrcBand)
200 :
201 : {
202 5090 : m_poRasterBand = poNewSrcBand;
203 5090 : m_nBand = m_poRasterBand->GetBand();
204 5090 : auto poDS = poNewSrcBand->GetDataset();
205 5090 : if (poDS != nullptr)
206 : {
207 5090 : m_osSrcDSName = poDS->GetDescription();
208 5090 : m_aosOpenOptions = CSLDuplicate(poDS->GetOpenOptions());
209 : }
210 5090 : }
211 :
212 : /************************************************************************/
213 : /* SetSrcMaskBand() */
214 : /************************************************************************/
215 :
216 : // poSrcBand is not the mask band, but the band from which the mask band is
217 : // taken.
218 28 : void VRTSimpleSource::SetSrcMaskBand(GDALRasterBand *poNewSrcBand)
219 :
220 : {
221 28 : m_poRasterBand = poNewSrcBand->GetMaskBand();
222 28 : m_poMaskBandMainBand = poNewSrcBand;
223 28 : m_nBand = poNewSrcBand->GetBand();
224 28 : auto poDS = poNewSrcBand->GetDataset();
225 28 : if (poDS != nullptr)
226 : {
227 28 : m_osSrcDSName = poDS->GetDescription();
228 28 : m_aosOpenOptions = CSLDuplicate(poDS->GetOpenOptions());
229 : }
230 28 : m_bGetMaskBand = true;
231 28 : }
232 :
233 : /************************************************************************/
234 : /* RoundIfCloseToInt() */
235 : /************************************************************************/
236 :
237 235084 : static double RoundIfCloseToInt(double dfValue)
238 : {
239 235084 : double dfClosestInt = floor(dfValue + 0.5);
240 235084 : return (fabs(dfValue - dfClosestInt) < 1e-3) ? dfClosestInt : dfValue;
241 : }
242 :
243 : /************************************************************************/
244 : /* SetSrcWindow() */
245 : /************************************************************************/
246 :
247 6811 : void VRTSimpleSource::SetSrcWindow(double dfNewXOff, double dfNewYOff,
248 : double dfNewXSize, double dfNewYSize)
249 :
250 : {
251 6811 : m_dfSrcXOff = RoundIfCloseToInt(dfNewXOff);
252 6811 : m_dfSrcYOff = RoundIfCloseToInt(dfNewYOff);
253 6811 : m_dfSrcXSize = RoundIfCloseToInt(dfNewXSize);
254 6811 : m_dfSrcYSize = RoundIfCloseToInt(dfNewYSize);
255 6811 : }
256 :
257 : /************************************************************************/
258 : /* SetDstWindow() */
259 : /************************************************************************/
260 :
261 6810 : void VRTSimpleSource::SetDstWindow(double dfNewXOff, double dfNewYOff,
262 : double dfNewXSize, double dfNewYSize)
263 :
264 : {
265 6810 : m_dfDstXOff = RoundIfCloseToInt(dfNewXOff);
266 6810 : m_dfDstYOff = RoundIfCloseToInt(dfNewYOff);
267 6810 : m_dfDstXSize = RoundIfCloseToInt(dfNewXSize);
268 6810 : m_dfDstYSize = RoundIfCloseToInt(dfNewYSize);
269 6810 : }
270 :
271 : /************************************************************************/
272 : /* GetDstWindow() */
273 : /************************************************************************/
274 :
275 1 : void VRTSimpleSource::GetDstWindow(double &dfDstXOff, double &dfDstYOff,
276 : double &dfDstXSize, double &dfDstYSize)
277 : {
278 1 : dfDstXOff = m_dfDstXOff;
279 1 : dfDstYOff = m_dfDstYOff;
280 1 : dfDstXSize = m_dfDstXSize;
281 1 : dfDstYSize = m_dfDstYSize;
282 1 : }
283 :
284 : /************************************************************************/
285 : /* SerializeToXML() */
286 : /************************************************************************/
287 :
288 900 : static bool IsSlowSource(const char *pszSrcName)
289 : {
290 1800 : return strstr(pszSrcName, "/vsicurl/http") != nullptr ||
291 1800 : strstr(pszSrcName, "/vsicurl/ftp") != nullptr ||
292 900 : (strstr(pszSrcName, "/vsicurl?") != nullptr &&
293 900 : strstr(pszSrcName, "&url=http") != nullptr);
294 : }
295 :
296 931 : CPLXMLNode *VRTSimpleSource::SerializeToXML(const char *pszVRTPath)
297 :
298 : {
299 : CPLXMLNode *const psSrc =
300 931 : CPLCreateXMLNode(nullptr, CXT_Element, "SimpleSource");
301 :
302 931 : if (!m_osResampling.empty())
303 : {
304 8 : CPLCreateXMLNode(CPLCreateXMLNode(psSrc, CXT_Attribute, "resampling"),
305 : CXT_Text, m_osResampling.c_str());
306 : }
307 :
308 : VSIStatBufL sStat;
309 931 : int bRelativeToVRT = FALSE; // TODO(schwehr): Make this a bool?
310 931 : std::string osSourceFilename;
311 :
312 931 : if (m_bRelativeToVRTOri >= 0)
313 : {
314 31 : osSourceFilename = m_osSourceFileNameOri;
315 31 : bRelativeToVRT = m_bRelativeToVRTOri;
316 : }
317 900 : else if (IsSlowSource(m_osSrcDSName))
318 : {
319 : // Testing the existence of remote resources can be excruciating
320 : // slow, so let's just suppose they exist.
321 0 : osSourceFilename = m_osSrcDSName;
322 0 : bRelativeToVRT = FALSE;
323 : }
324 : // If this isn't actually a file, don't even try to know if it is a
325 : // relative path. It can't be !, and unfortunately CPLIsFilenameRelative()
326 : // can only work with strings that are filenames To be clear
327 : // NITF_TOC_ENTRY:CADRG_JOG-A_250K_1_0:some_path isn't a relative file
328 : // path.
329 900 : else if (VSIStatExL(m_osSrcDSName, &sStat, VSI_STAT_EXISTS_FLAG) != 0)
330 : {
331 56 : osSourceFilename = m_osSrcDSName;
332 56 : bRelativeToVRT = FALSE;
333 :
334 : // Try subdatasetinfo API first
335 : // Note: this will become the only branch when subdatasetinfo will become
336 : // available for NITF_IM, RASTERLITE and TILEDB
337 56 : const auto oSubDSInfo{GDALGetSubdatasetInfo(osSourceFilename.c_str())};
338 56 : if (oSubDSInfo && !oSubDSInfo->GetPathComponent().empty())
339 : {
340 10 : auto path{oSubDSInfo->GetPathComponent()};
341 : std::string relPath{CPLExtractRelativePath(pszVRTPath, path.c_str(),
342 10 : &bRelativeToVRT)};
343 5 : osSourceFilename = oSubDSInfo->ModifyPathComponent(relPath);
344 5 : GDALDestroySubdatasetInfo(oSubDSInfo);
345 : }
346 : else
347 : {
348 291 : for (const char *pszSyntax : VRTDataset::apszSpecialSyntax)
349 : {
350 243 : CPLString osPrefix(pszSyntax);
351 243 : osPrefix.resize(strchr(pszSyntax, ':') - pszSyntax + 1);
352 243 : if (pszSyntax[osPrefix.size()] == '"')
353 48 : osPrefix += '"';
354 243 : if (EQUALN(osSourceFilename.c_str(), osPrefix, osPrefix.size()))
355 : {
356 3 : if (STARTS_WITH_CI(pszSyntax + osPrefix.size(), "{ANY}"))
357 : {
358 : const char *pszLastPart =
359 3 : strrchr(osSourceFilename.c_str(), ':') + 1;
360 : // CSV:z:/foo.xyz
361 1 : if ((pszLastPart[0] == '/' || pszLastPart[0] == '\\') &&
362 6 : pszLastPart - osSourceFilename.c_str() >= 3 &&
363 2 : pszLastPart[-3] == ':')
364 0 : pszLastPart -= 2;
365 3 : CPLString osPrefixFilename(osSourceFilename);
366 3 : osPrefixFilename.resize(pszLastPart -
367 3 : osSourceFilename.c_str());
368 : osSourceFilename = CPLExtractRelativePath(
369 3 : pszVRTPath, pszLastPart, &bRelativeToVRT);
370 3 : osSourceFilename = osPrefixFilename + osSourceFilename;
371 : }
372 0 : else if (STARTS_WITH_CI(pszSyntax + osPrefix.size(),
373 : "{FILENAME}"))
374 : {
375 0 : CPLString osFilename(osSourceFilename.c_str() +
376 0 : osPrefix.size());
377 0 : size_t nPos = 0;
378 0 : if (osFilename.size() >= 3 && osFilename[1] == ':' &&
379 0 : (osFilename[2] == '\\' || osFilename[2] == '/'))
380 0 : nPos = 2;
381 0 : nPos = osFilename.find(
382 0 : pszSyntax[osPrefix.size() + strlen("{FILENAME}")],
383 : nPos);
384 0 : if (nPos != std::string::npos)
385 : {
386 0 : const CPLString osSuffix = osFilename.substr(nPos);
387 0 : osFilename.resize(nPos);
388 : osSourceFilename = CPLExtractRelativePath(
389 0 : pszVRTPath, osFilename, &bRelativeToVRT);
390 : osSourceFilename =
391 0 : osPrefix + osSourceFilename + osSuffix;
392 : }
393 : }
394 3 : break;
395 : }
396 : }
397 : }
398 : }
399 : else
400 : {
401 1688 : std::string osVRTFilename = pszVRTPath;
402 1688 : std::string osSourceDataset = m_osSrcDSName;
403 844 : char *pszCurDir = CPLGetCurrentDir();
404 844 : if (CPLIsFilenameRelative(osSourceDataset.c_str()) &&
405 844 : !CPLIsFilenameRelative(osVRTFilename.c_str()) &&
406 : pszCurDir != nullptr)
407 : {
408 : osSourceDataset =
409 61 : CPLFormFilename(pszCurDir, osSourceDataset.c_str(), nullptr);
410 : }
411 783 : else if (!CPLIsFilenameRelative(osSourceDataset.c_str()) &&
412 783 : CPLIsFilenameRelative(osVRTFilename.c_str()) &&
413 : pszCurDir != nullptr)
414 : {
415 : osVRTFilename =
416 93 : CPLFormFilename(pszCurDir, osVRTFilename.c_str(), nullptr);
417 : }
418 844 : CPLFree(pszCurDir);
419 : osSourceFilename = CPLExtractRelativePath(
420 844 : osVRTFilename.c_str(), osSourceDataset.c_str(), &bRelativeToVRT);
421 : }
422 :
423 931 : CPLSetXMLValue(psSrc, "SourceFilename", osSourceFilename.c_str());
424 :
425 931 : CPLCreateXMLNode(CPLCreateXMLNode(CPLGetXMLNode(psSrc, "SourceFilename"),
426 : CXT_Attribute, "relativeToVRT"),
427 931 : CXT_Text, bRelativeToVRT ? "1" : "0");
428 :
429 : // Determine if we must write the shared attribute. The config option
430 : // will override the m_nExplicitSharedStatus value
431 931 : const char *pszShared = CPLGetConfigOption("VRT_SHARED_SOURCE", nullptr);
432 931 : if ((pszShared == nullptr && m_nExplicitSharedStatus == 0) ||
433 0 : (pszShared != nullptr && !CPLTestBool(pszShared)))
434 : {
435 0 : CPLCreateXMLNode(
436 : CPLCreateXMLNode(CPLGetXMLNode(psSrc, "SourceFilename"),
437 : CXT_Attribute, "shared"),
438 : CXT_Text, "0");
439 : }
440 :
441 931 : GDALSerializeOpenOptionsToXML(psSrc, m_aosOpenOptions.List());
442 :
443 931 : if (m_bGetMaskBand)
444 14 : CPLSetXMLValue(psSrc, "SourceBand", CPLSPrintf("mask,%d", m_nBand));
445 : else
446 917 : CPLSetXMLValue(psSrc, "SourceBand", CPLSPrintf("%d", m_nBand));
447 :
448 : // TODO: in a later version, no longer emit SourceProperties, which
449 : // is no longer used by GDAL 3.4
450 931 : if (m_poRasterBand)
451 : {
452 : /* Write a few additional useful properties of the dataset */
453 : /* so that we can use a proxy dataset when re-opening. See XMLInit() */
454 : /* below */
455 835 : CPLSetXMLValue(psSrc, "SourceProperties.#RasterXSize",
456 835 : CPLSPrintf("%d", m_poRasterBand->GetXSize()));
457 835 : CPLSetXMLValue(psSrc, "SourceProperties.#RasterYSize",
458 835 : CPLSPrintf("%d", m_poRasterBand->GetYSize()));
459 835 : CPLSetXMLValue(
460 : psSrc, "SourceProperties.#DataType",
461 835 : GDALGetDataTypeName(m_poRasterBand->GetRasterDataType()));
462 :
463 835 : int nBlockXSize = 0;
464 835 : int nBlockYSize = 0;
465 835 : m_poRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
466 :
467 835 : CPLSetXMLValue(psSrc, "SourceProperties.#BlockXSize",
468 : CPLSPrintf("%d", nBlockXSize));
469 835 : CPLSetXMLValue(psSrc, "SourceProperties.#BlockYSize",
470 : CPLSPrintf("%d", nBlockYSize));
471 : }
472 :
473 931 : if (m_dfSrcXOff != -1 || m_dfSrcYOff != -1 || m_dfSrcXSize != -1 ||
474 12 : m_dfSrcYSize != -1)
475 : {
476 919 : CPLSetXMLValue(psSrc, "SrcRect.#xOff",
477 : CPLSPrintf("%.15g", m_dfSrcXOff));
478 919 : CPLSetXMLValue(psSrc, "SrcRect.#yOff",
479 : CPLSPrintf("%.15g", m_dfSrcYOff));
480 919 : CPLSetXMLValue(psSrc, "SrcRect.#xSize",
481 : CPLSPrintf("%.15g", m_dfSrcXSize));
482 919 : CPLSetXMLValue(psSrc, "SrcRect.#ySize",
483 : CPLSPrintf("%.15g", m_dfSrcYSize));
484 : }
485 :
486 931 : if (m_dfDstXOff != -1 || m_dfDstYOff != -1 || m_dfDstXSize != -1 ||
487 12 : m_dfDstYSize != -1)
488 : {
489 919 : CPLSetXMLValue(psSrc, "DstRect.#xOff",
490 : CPLSPrintf("%.15g", m_dfDstXOff));
491 919 : CPLSetXMLValue(psSrc, "DstRect.#yOff",
492 : CPLSPrintf("%.15g", m_dfDstYOff));
493 919 : CPLSetXMLValue(psSrc, "DstRect.#xSize",
494 : CPLSPrintf("%.15g", m_dfDstXSize));
495 919 : CPLSetXMLValue(psSrc, "DstRect.#ySize",
496 : CPLSPrintf("%.15g", m_dfDstYSize));
497 : }
498 :
499 1862 : return psSrc;
500 : }
501 :
502 : /************************************************************************/
503 : /* XMLInit() */
504 : /************************************************************************/
505 :
506 : CPLErr
507 1472 : VRTSimpleSource::XMLInit(const CPLXMLNode *psSrc, const char *pszVRTPath,
508 : std::map<CPLString, GDALDataset *> &oMapSharedSources)
509 :
510 : {
511 1472 : m_poMapSharedSources = &oMapSharedSources;
512 :
513 1472 : m_osResampling = CPLGetXMLValue(psSrc, "resampling", "");
514 :
515 : /* -------------------------------------------------------------------- */
516 : /* Prepare filename. */
517 : /* -------------------------------------------------------------------- */
518 : const CPLXMLNode *psSourceFileNameNode =
519 1472 : CPLGetXMLNode(psSrc, "SourceFilename");
520 : const char *pszFilename =
521 1472 : psSourceFileNameNode ? CPLGetXMLValue(psSourceFileNameNode, nullptr, "")
522 1472 : : "";
523 :
524 1472 : if (pszFilename[0] == '\0')
525 : {
526 1 : CPLError(CE_Warning, CPLE_AppDefined,
527 : "Missing <SourceFilename> element in VRTRasterBand.");
528 1 : return CE_Failure;
529 : }
530 :
531 : // Backup original filename and relativeToVRT so as to be able to
532 : // serialize them identically again (#5985)
533 1471 : m_osSourceFileNameOri = pszFilename;
534 1471 : m_bRelativeToVRTOri =
535 1471 : atoi(CPLGetXMLValue(psSourceFileNameNode, "relativetoVRT", "0"));
536 : const char *pszShared =
537 1471 : CPLGetXMLValue(psSourceFileNameNode, "shared", nullptr);
538 1471 : if (pszShared == nullptr)
539 : {
540 1469 : pszShared = CPLGetConfigOption("VRT_SHARED_SOURCE", nullptr);
541 : }
542 1471 : if (pszShared != nullptr)
543 : {
544 8 : m_nExplicitSharedStatus = CPLTestBool(pszShared);
545 : }
546 :
547 1471 : m_osSrcDSName = VRTDataset::BuildSourceFilename(
548 2942 : pszFilename, pszVRTPath, CPL_TO_BOOL(m_bRelativeToVRTOri));
549 :
550 1471 : const char *pszSourceBand = CPLGetXMLValue(psSrc, "SourceBand", "1");
551 1471 : m_bGetMaskBand = false;
552 1471 : if (STARTS_WITH_CI(pszSourceBand, "mask"))
553 : {
554 18 : m_bGetMaskBand = true;
555 18 : if (pszSourceBand[4] == ',')
556 18 : m_nBand = atoi(pszSourceBand + 5);
557 : else
558 0 : m_nBand = 1;
559 : }
560 : else
561 : {
562 1453 : m_nBand = atoi(pszSourceBand);
563 : }
564 1471 : if (!GDALCheckBandCount(m_nBand, 0))
565 : {
566 0 : CPLError(CE_Warning, CPLE_AppDefined,
567 : "Invalid <SourceBand> element in VRTRasterBand.");
568 0 : return CE_Failure;
569 : }
570 :
571 1471 : m_aosOpenOptions = GDALDeserializeOpenOptionsFromXML(psSrc);
572 1471 : if (strstr(m_osSrcDSName.c_str(), "<VRTDataset") != nullptr)
573 2 : m_aosOpenOptions.SetNameValue("ROOT_PATH", pszVRTPath);
574 :
575 1471 : return ParseSrcRectAndDstRect(psSrc);
576 : }
577 :
578 : /************************************************************************/
579 : /* ParseSrcRectAndDstRect() */
580 : /************************************************************************/
581 :
582 1484 : CPLErr VRTSimpleSource::ParseSrcRectAndDstRect(const CPLXMLNode *psSrc)
583 : {
584 : /* -------------------------------------------------------------------- */
585 : /* Set characteristics. */
586 : /* -------------------------------------------------------------------- */
587 1484 : const CPLXMLNode *const psSrcRect = CPLGetXMLNode(psSrc, "SrcRect");
588 1484 : if (psSrcRect)
589 : {
590 1273 : double xOff = CPLAtof(CPLGetXMLValue(psSrcRect, "xOff", "-1"));
591 1273 : double yOff = CPLAtof(CPLGetXMLValue(psSrcRect, "yOff", "-1"));
592 1273 : double xSize = CPLAtof(CPLGetXMLValue(psSrcRect, "xSize", "-1"));
593 1273 : double ySize = CPLAtof(CPLGetXMLValue(psSrcRect, "ySize", "-1"));
594 : // Test written that way to catch NaN values
595 1273 : if (!(xOff >= INT_MIN && xOff <= INT_MAX) ||
596 1273 : !(yOff >= INT_MIN && yOff <= INT_MAX) ||
597 1273 : !(xSize > 0 || xSize == -1) || xSize > INT_MAX ||
598 1272 : !(ySize > 0 || ySize == -1) || ySize > INT_MAX)
599 : {
600 1 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong values in SrcRect");
601 1 : return CE_Failure;
602 : }
603 1272 : SetSrcWindow(xOff, yOff, xSize, ySize);
604 : }
605 : else
606 : {
607 211 : m_dfSrcXOff = -1;
608 211 : m_dfSrcYOff = -1;
609 211 : m_dfSrcXSize = -1;
610 211 : m_dfSrcYSize = -1;
611 : }
612 :
613 1483 : const CPLXMLNode *const psDstRect = CPLGetXMLNode(psSrc, "DstRect");
614 1483 : if (psDstRect)
615 : {
616 1272 : double xOff = CPLAtof(CPLGetXMLValue(psDstRect, "xOff", "-1"));
617 1272 : double yOff = CPLAtof(CPLGetXMLValue(psDstRect, "yOff", "-1"));
618 1272 : double xSize = CPLAtof(CPLGetXMLValue(psDstRect, "xSize", "-1"));
619 1272 : double ySize = CPLAtof(CPLGetXMLValue(psDstRect, "ySize", "-1"));
620 : // Test written that way to catch NaN values
621 1272 : if (!(xOff >= INT_MIN && xOff <= INT_MAX) ||
622 1272 : !(yOff >= INT_MIN && yOff <= INT_MAX) ||
623 1272 : !(xSize > 0 || xSize == -1) || xSize > INT_MAX ||
624 1272 : !(ySize > 0 || ySize == -1) || ySize > INT_MAX)
625 : {
626 1 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong values in DstRect");
627 1 : return CE_Failure;
628 : }
629 1271 : SetDstWindow(xOff, yOff, xSize, ySize);
630 : }
631 : else
632 : {
633 211 : m_dfDstXOff = -1;
634 211 : m_dfDstYOff = -1;
635 211 : m_dfDstXSize = -1;
636 211 : m_dfDstYSize = -1;
637 : }
638 :
639 1482 : return CE_None;
640 : }
641 :
642 : /************************************************************************/
643 : /* GetFileList() */
644 : /************************************************************************/
645 :
646 102 : void VRTSimpleSource::GetFileList(char ***ppapszFileList, int *pnSize,
647 : int *pnMaxSize, CPLHashSet *hSetFiles)
648 : {
649 102 : if (!m_osSrcDSName.empty())
650 : {
651 102 : const char *pszFilename = m_osSrcDSName.c_str();
652 :
653 : /* --------------------------------------------------------------------
654 : */
655 : /* Is it already in the list ? */
656 : /* --------------------------------------------------------------------
657 : */
658 102 : if (CPLHashSetLookup(hSetFiles, pszFilename) != nullptr)
659 15 : return;
660 :
661 : /* --------------------------------------------------------------------
662 : */
663 : /* Grow array if necessary */
664 : /* --------------------------------------------------------------------
665 : */
666 87 : if (*pnSize + 1 >= *pnMaxSize)
667 : {
668 60 : *pnMaxSize = std::max(*pnSize + 2, 2 + 2 * (*pnMaxSize));
669 60 : *ppapszFileList = static_cast<char **>(
670 60 : CPLRealloc(*ppapszFileList, sizeof(char *) * (*pnMaxSize)));
671 : }
672 :
673 : /* --------------------------------------------------------------------
674 : */
675 : /* Add the string to the list */
676 : /* --------------------------------------------------------------------
677 : */
678 87 : (*ppapszFileList)[*pnSize] = CPLStrdup(pszFilename);
679 87 : (*ppapszFileList)[(*pnSize + 1)] = nullptr;
680 87 : CPLHashSetInsert(hSetFiles, (*ppapszFileList)[*pnSize]);
681 :
682 87 : (*pnSize)++;
683 : }
684 : }
685 :
686 : /************************************************************************/
687 : /* OpenSource() */
688 : /************************************************************************/
689 :
690 913 : void VRTSimpleSource::OpenSource() const
691 : {
692 913 : CPLAssert(m_poRasterBand == nullptr);
693 :
694 : /* ----------------------------------------------------------------- */
695 : /* Create a proxy dataset */
696 : /* ----------------------------------------------------------------- */
697 913 : GDALProxyPoolDataset *proxyDS = nullptr;
698 913 : std::string osKeyMapSharedSources;
699 913 : if (m_poMapSharedSources)
700 : {
701 869 : osKeyMapSharedSources = m_osSrcDSName;
702 875 : for (int i = 0; i < m_aosOpenOptions.size(); ++i)
703 : {
704 6 : osKeyMapSharedSources += "||";
705 6 : osKeyMapSharedSources += m_aosOpenOptions[i];
706 : }
707 :
708 869 : auto oIter = m_poMapSharedSources->find(osKeyMapSharedSources);
709 869 : if (oIter != m_poMapSharedSources->end())
710 150 : proxyDS = cpl::down_cast<GDALProxyPoolDataset *>(oIter->second);
711 : }
712 :
713 913 : if (proxyDS == nullptr)
714 : {
715 763 : int bShared = true;
716 763 : if (m_nExplicitSharedStatus != -1)
717 8 : bShared = m_nExplicitSharedStatus;
718 :
719 763 : const CPLString osUniqueHandle(CPLSPrintf("%p", m_poMapSharedSources));
720 763 : proxyDS = GDALProxyPoolDataset::Create(
721 : m_osSrcDSName, m_aosOpenOptions.List(), GA_ReadOnly, bShared,
722 : osUniqueHandle.c_str());
723 763 : if (proxyDS == nullptr)
724 207 : return;
725 : }
726 : else
727 : {
728 150 : proxyDS->Reference();
729 : }
730 :
731 706 : if (m_bGetMaskBand)
732 : {
733 : GDALProxyPoolRasterBand *poMaskBand =
734 11 : cpl::down_cast<GDALProxyPoolRasterBand *>(
735 11 : proxyDS->GetRasterBand(m_nBand));
736 11 : poMaskBand->AddSrcMaskBandDescriptionFromUnderlying();
737 : }
738 :
739 : /* -------------------------------------------------------------------- */
740 : /* Get the raster band. */
741 : /* -------------------------------------------------------------------- */
742 :
743 706 : m_poRasterBand = proxyDS->GetRasterBand(m_nBand);
744 706 : if (m_poRasterBand == nullptr || !ValidateOpenedBand(m_poRasterBand))
745 : {
746 2 : proxyDS->ReleaseRef();
747 2 : return;
748 : }
749 :
750 704 : if (m_bGetMaskBand)
751 : {
752 11 : m_poRasterBand = m_poRasterBand->GetMaskBand();
753 11 : if (m_poRasterBand == nullptr)
754 : {
755 0 : proxyDS->ReleaseRef();
756 0 : return;
757 : }
758 11 : m_poMaskBandMainBand = m_poRasterBand;
759 : }
760 :
761 704 : if (m_poMapSharedSources)
762 : {
763 660 : (*m_poMapSharedSources)[osKeyMapSharedSources] = proxyDS;
764 : }
765 : }
766 :
767 : /************************************************************************/
768 : /* GetRasterBand() */
769 : /************************************************************************/
770 :
771 93138 : GDALRasterBand *VRTSimpleSource::GetRasterBand() const
772 : {
773 93138 : if (m_poRasterBand == nullptr)
774 911 : OpenSource();
775 93138 : return m_poRasterBand;
776 : }
777 :
778 : /************************************************************************/
779 : /* GetMaskBandMainBand() */
780 : /************************************************************************/
781 :
782 1130 : GDALRasterBand *VRTSimpleSource::GetMaskBandMainBand()
783 : {
784 1130 : if (m_poRasterBand == nullptr)
785 2 : OpenSource();
786 1130 : return m_poMaskBandMainBand;
787 : }
788 :
789 : /************************************************************************/
790 : /* IsSameExceptBandNumber() */
791 : /************************************************************************/
792 :
793 2144 : int VRTSimpleSource::IsSameExceptBandNumber(VRTSimpleSource *poOtherSource)
794 : {
795 4288 : return m_dfSrcXOff == poOtherSource->m_dfSrcXOff &&
796 2144 : m_dfSrcYOff == poOtherSource->m_dfSrcYOff &&
797 2144 : m_dfSrcXSize == poOtherSource->m_dfSrcXSize &&
798 2144 : m_dfSrcYSize == poOtherSource->m_dfSrcYSize &&
799 2144 : m_dfDstXOff == poOtherSource->m_dfDstXOff &&
800 2144 : m_dfDstYOff == poOtherSource->m_dfDstYOff &&
801 2144 : m_dfDstXSize == poOtherSource->m_dfDstXSize &&
802 2144 : m_dfDstYSize == poOtherSource->m_dfDstYSize &&
803 6432 : !m_osSrcDSName.empty() &&
804 4288 : m_osSrcDSName == poOtherSource->m_osSrcDSName;
805 : }
806 :
807 : /************************************************************************/
808 : /* SrcToDst() */
809 : /* */
810 : /* Note: this is a no-op if the dst window is -1,-1,-1,-1. */
811 : /************************************************************************/
812 :
813 5368 : void VRTSimpleSource::SrcToDst(double dfX, double dfY, double &dfXOut,
814 : double &dfYOut) const
815 :
816 : {
817 5368 : dfXOut = ((dfX - m_dfSrcXOff) / m_dfSrcXSize) * m_dfDstXSize + m_dfDstXOff;
818 5368 : dfYOut = ((dfY - m_dfSrcYOff) / m_dfSrcYSize) * m_dfDstYSize + m_dfDstYOff;
819 5368 : }
820 :
821 : /************************************************************************/
822 : /* DstToSrc() */
823 : /* */
824 : /* Note: this is a no-op if the dst window is -1,-1,-1,-1. */
825 : /************************************************************************/
826 :
827 4022310 : void VRTSimpleSource::DstToSrc(double dfX, double dfY, double &dfXOut,
828 : double &dfYOut) const
829 :
830 : {
831 4022310 : dfXOut = ((dfX - m_dfDstXOff) / m_dfDstXSize) * m_dfSrcXSize + m_dfSrcXOff;
832 4022310 : dfYOut = ((dfY - m_dfDstYOff) / m_dfDstYSize) * m_dfSrcYSize + m_dfSrcYOff;
833 4022310 : }
834 :
835 : /************************************************************************/
836 : /* GetSrcDstWindow() */
837 : /************************************************************************/
838 :
839 65449 : int VRTSimpleSource::GetSrcDstWindow(
840 : double dfXOff, double dfYOff, double dfXSize, double dfYSize, int nBufXSize,
841 : int nBufYSize, double *pdfReqXOff, double *pdfReqYOff, double *pdfReqXSize,
842 : double *pdfReqYSize, int *pnReqXOff, int *pnReqYOff, int *pnReqXSize,
843 : int *pnReqYSize, int *pnOutXOff, int *pnOutYOff, int *pnOutXSize,
844 : int *pnOutYSize, bool &bErrorOut)
845 :
846 : {
847 65449 : bErrorOut = false;
848 :
849 65449 : if (m_dfSrcXSize == 0.0 || m_dfSrcYSize == 0.0 || m_dfDstXSize == 0.0 ||
850 65449 : m_dfDstYSize == 0.0)
851 : {
852 0 : return FALSE;
853 : }
854 :
855 298 : const bool bDstWinSet = m_dfDstXOff != -1 || m_dfDstXSize != -1 ||
856 65747 : m_dfDstYOff != -1 || m_dfDstYSize != -1;
857 :
858 : #ifdef DEBUG
859 298 : const bool bSrcWinSet = m_dfSrcXOff != -1 || m_dfSrcXSize != -1 ||
860 65747 : m_dfSrcYOff != -1 || m_dfSrcYSize != -1;
861 :
862 65449 : if (bSrcWinSet != bDstWinSet)
863 : {
864 0 : return FALSE;
865 : }
866 : #endif
867 :
868 : /* -------------------------------------------------------------------- */
869 : /* If the input window completely misses the portion of the */
870 : /* virtual dataset provided by this source we have nothing to do. */
871 : /* -------------------------------------------------------------------- */
872 65449 : if (bDstWinSet)
873 : {
874 65151 : if (dfXOff >= m_dfDstXOff + m_dfDstXSize ||
875 54723 : dfYOff >= m_dfDstYOff + m_dfDstYSize ||
876 54187 : dfXOff + dfXSize <= m_dfDstXOff || dfYOff + dfYSize <= m_dfDstYOff)
877 20185 : return FALSE;
878 : }
879 :
880 : /* -------------------------------------------------------------------- */
881 : /* This request window corresponds to the whole output buffer. */
882 : /* -------------------------------------------------------------------- */
883 45264 : *pnOutXOff = 0;
884 45264 : *pnOutYOff = 0;
885 45264 : *pnOutXSize = nBufXSize;
886 45264 : *pnOutYSize = nBufYSize;
887 :
888 : /* -------------------------------------------------------------------- */
889 : /* If the input window extents outside the portion of the on */
890 : /* the virtual file that this source can set, then clip down */
891 : /* the requested window. */
892 : /* -------------------------------------------------------------------- */
893 45264 : bool bModifiedX = false;
894 45264 : bool bModifiedY = false;
895 45264 : double dfRXOff = dfXOff;
896 45264 : double dfRYOff = dfYOff;
897 45264 : double dfRXSize = dfXSize;
898 45264 : double dfRYSize = dfYSize;
899 :
900 45264 : if (bDstWinSet)
901 : {
902 44966 : if (dfRXOff < m_dfDstXOff)
903 : {
904 1330 : dfRXSize = dfRXSize + dfRXOff - m_dfDstXOff;
905 1330 : dfRXOff = m_dfDstXOff;
906 1330 : bModifiedX = true;
907 : }
908 :
909 44966 : if (dfRYOff < m_dfDstYOff)
910 : {
911 163 : dfRYSize = dfRYSize + dfRYOff - m_dfDstYOff;
912 163 : dfRYOff = m_dfDstYOff;
913 163 : bModifiedY = true;
914 : }
915 :
916 44966 : if (dfRXOff + dfRXSize > m_dfDstXOff + m_dfDstXSize)
917 : {
918 1398 : dfRXSize = m_dfDstXOff + m_dfDstXSize - dfRXOff;
919 1398 : bModifiedX = true;
920 : }
921 :
922 44966 : if (dfRYOff + dfRYSize > m_dfDstYOff + m_dfDstYSize)
923 : {
924 250 : dfRYSize = m_dfDstYOff + m_dfDstYSize - dfRYOff;
925 250 : bModifiedY = true;
926 : }
927 : }
928 :
929 : /* -------------------------------------------------------------------- */
930 : /* Translate requested region in virtual file into the source */
931 : /* band coordinates. */
932 : /* -------------------------------------------------------------------- */
933 45264 : const double dfScaleX = m_dfSrcXSize / m_dfDstXSize;
934 45264 : const double dfScaleY = m_dfSrcYSize / m_dfDstYSize;
935 :
936 45264 : *pdfReqXOff = (dfRXOff - m_dfDstXOff) * dfScaleX + m_dfSrcXOff;
937 45264 : *pdfReqYOff = (dfRYOff - m_dfDstYOff) * dfScaleY + m_dfSrcYOff;
938 45264 : *pdfReqXSize = dfRXSize * dfScaleX;
939 45264 : *pdfReqYSize = dfRYSize * dfScaleY;
940 :
941 45264 : if (!CPLIsFinite(*pdfReqXOff) || !CPLIsFinite(*pdfReqYOff) ||
942 45264 : !CPLIsFinite(*pdfReqXSize) || !CPLIsFinite(*pdfReqYSize) ||
943 45264 : *pdfReqXOff > INT_MAX || *pdfReqYOff > INT_MAX || *pdfReqXSize < 0 ||
944 45264 : *pdfReqYSize < 0)
945 : {
946 0 : return FALSE;
947 : }
948 :
949 : /* -------------------------------------------------------------------- */
950 : /* Clamp within the bounds of the available source data. */
951 : /* -------------------------------------------------------------------- */
952 45264 : if (*pdfReqXOff < 0)
953 : {
954 6 : *pdfReqXSize += *pdfReqXOff;
955 6 : *pdfReqXOff = 0;
956 6 : bModifiedX = true;
957 : }
958 45264 : if (*pdfReqYOff < 0)
959 : {
960 6 : *pdfReqYSize += *pdfReqYOff;
961 6 : *pdfReqYOff = 0;
962 6 : bModifiedY = true;
963 : }
964 :
965 45264 : *pnReqXOff = static_cast<int>(floor(*pdfReqXOff));
966 45264 : *pnReqYOff = static_cast<int>(floor(*pdfReqYOff));
967 :
968 45264 : constexpr double EPS = 1e-3;
969 45264 : constexpr double ONE_MINUS_EPS = 1.0 - EPS;
970 45264 : if (*pdfReqXOff - *pnReqXOff > ONE_MINUS_EPS)
971 : {
972 2 : (*pnReqXOff)++;
973 2 : *pdfReqXOff = *pnReqXOff;
974 : }
975 45264 : if (*pdfReqYOff - *pnReqYOff > ONE_MINUS_EPS)
976 : {
977 18 : (*pnReqYOff)++;
978 18 : *pdfReqYOff = *pnReqYOff;
979 : }
980 :
981 45264 : if (*pdfReqXSize > INT_MAX)
982 0 : *pnReqXSize = INT_MAX;
983 : else
984 45264 : *pnReqXSize = static_cast<int>(floor(*pdfReqXSize + 0.5));
985 :
986 45264 : if (*pdfReqYSize > INT_MAX)
987 0 : *pnReqYSize = INT_MAX;
988 : else
989 45264 : *pnReqYSize = static_cast<int>(floor(*pdfReqYSize + 0.5));
990 :
991 : /* -------------------------------------------------------------------- */
992 : /* Clamp within the bounds of the available source data. */
993 : /* -------------------------------------------------------------------- */
994 :
995 45264 : if (*pnReqXSize == 0)
996 675 : *pnReqXSize = 1;
997 45264 : if (*pnReqYSize == 0)
998 22878 : *pnReqYSize = 1;
999 :
1000 45264 : auto l_band = GetRasterBand();
1001 45264 : if (!l_band)
1002 : {
1003 105 : bErrorOut = true;
1004 105 : return FALSE;
1005 : }
1006 90318 : if (*pnReqXSize > INT_MAX - *pnReqXOff ||
1007 45159 : *pnReqXOff + *pnReqXSize > l_band->GetXSize())
1008 : {
1009 36 : *pnReqXSize = l_band->GetXSize() - *pnReqXOff;
1010 36 : bModifiedX = true;
1011 : }
1012 45159 : if (*pdfReqXOff + *pdfReqXSize > l_band->GetXSize())
1013 : {
1014 36 : *pdfReqXSize = l_band->GetXSize() - *pdfReqXOff;
1015 36 : bModifiedX = true;
1016 : }
1017 :
1018 90318 : if (*pnReqYSize > INT_MAX - *pnReqYOff ||
1019 45159 : *pnReqYOff + *pnReqYSize > l_band->GetYSize())
1020 : {
1021 36 : *pnReqYSize = l_band->GetYSize() - *pnReqYOff;
1022 36 : bModifiedY = true;
1023 : }
1024 45159 : if (*pdfReqYOff + *pdfReqYSize > l_band->GetYSize())
1025 : {
1026 48 : *pdfReqYSize = l_band->GetYSize() - *pdfReqYOff;
1027 48 : bModifiedY = true;
1028 : }
1029 :
1030 : /* -------------------------------------------------------------------- */
1031 : /* Don't do anything if the requesting region is completely off */
1032 : /* the source image. */
1033 : /* -------------------------------------------------------------------- */
1034 90314 : if (*pnReqXOff >= l_band->GetXSize() || *pnReqYOff >= l_band->GetYSize() ||
1035 90314 : *pnReqXSize <= 0 || *pnReqYSize <= 0)
1036 : {
1037 9 : return FALSE;
1038 : }
1039 :
1040 : /* -------------------------------------------------------------------- */
1041 : /* If we haven't had to modify the source rectangle, then the */
1042 : /* destination rectangle must be the whole region. */
1043 : /* -------------------------------------------------------------------- */
1044 45150 : if (bModifiedX || bModifiedY)
1045 : {
1046 : /* --------------------------------------------------------------------
1047 : */
1048 : /* Now transform this possibly reduced request back into the */
1049 : /* destination buffer coordinates in case the output region is */
1050 : /* less than the whole buffer. */
1051 : /* --------------------------------------------------------------------
1052 : */
1053 2684 : double dfDstULX = 0.0;
1054 2684 : double dfDstULY = 0.0;
1055 2684 : double dfDstLRX = 0.0;
1056 2684 : double dfDstLRY = 0.0;
1057 :
1058 2684 : SrcToDst(*pdfReqXOff, *pdfReqYOff, dfDstULX, dfDstULY);
1059 2684 : SrcToDst(*pdfReqXOff + *pdfReqXSize, *pdfReqYOff + *pdfReqYSize,
1060 : dfDstLRX, dfDstLRY);
1061 : #if DEBUG_VERBOSE
1062 : CPLDebug("VRT", "dfDstULX=%g dfDstULY=%g dfDstLRX=%g dfDstLRY=%g",
1063 : dfDstULX, dfDstULY, dfDstLRX, dfDstLRY);
1064 : #endif
1065 :
1066 2684 : if (bModifiedX)
1067 : {
1068 2642 : const double dfScaleWinToBufX = nBufXSize / dfXSize;
1069 :
1070 2642 : const double dfOutXOff = (dfDstULX - dfXOff) * dfScaleWinToBufX;
1071 2642 : if (dfOutXOff <= 0)
1072 1312 : *pnOutXOff = 0;
1073 1330 : else if (dfOutXOff > INT_MAX)
1074 0 : *pnOutXOff = INT_MAX;
1075 : else
1076 1330 : *pnOutXOff = static_cast<int>(dfOutXOff + EPS);
1077 :
1078 : // Apply correction on floating-point source window
1079 : {
1080 2642 : double dfDstDeltaX =
1081 2642 : (dfOutXOff - *pnOutXOff) / dfScaleWinToBufX;
1082 2642 : double dfSrcDeltaX = dfDstDeltaX / m_dfDstXSize * m_dfSrcXSize;
1083 2642 : *pdfReqXOff -= dfSrcDeltaX;
1084 5284 : *pdfReqXSize = std::min(*pdfReqXSize + dfSrcDeltaX,
1085 2642 : static_cast<double>(INT_MAX));
1086 : }
1087 :
1088 2642 : double dfOutRightXOff = (dfDstLRX - dfXOff) * dfScaleWinToBufX;
1089 2642 : if (dfOutRightXOff < dfOutXOff)
1090 0 : return FALSE;
1091 2642 : if (dfOutRightXOff > INT_MAX)
1092 0 : dfOutRightXOff = INT_MAX;
1093 2642 : const int nOutRightXOff =
1094 2642 : static_cast<int>(ceil(dfOutRightXOff - EPS));
1095 2642 : *pnOutXSize = nOutRightXOff - *pnOutXOff;
1096 :
1097 2642 : if (*pnOutXSize > INT_MAX - *pnOutXOff ||
1098 2642 : *pnOutXOff + *pnOutXSize > nBufXSize)
1099 0 : *pnOutXSize = nBufXSize - *pnOutXOff;
1100 :
1101 : // Apply correction on floating-point source window
1102 : {
1103 2642 : double dfDstDeltaX =
1104 2642 : (nOutRightXOff - dfOutRightXOff) / dfScaleWinToBufX;
1105 2642 : double dfSrcDeltaX = dfDstDeltaX / m_dfDstXSize * m_dfSrcXSize;
1106 5284 : *pdfReqXSize = std::min(*pdfReqXSize + dfSrcDeltaX,
1107 2642 : static_cast<double>(INT_MAX));
1108 : }
1109 : }
1110 :
1111 2684 : if (bModifiedY)
1112 : {
1113 344 : const double dfScaleWinToBufY = nBufYSize / dfYSize;
1114 :
1115 344 : const double dfOutYOff = (dfDstULY - dfYOff) * dfScaleWinToBufY;
1116 344 : if (dfOutYOff <= 0)
1117 170 : *pnOutYOff = 0;
1118 174 : else if (dfOutYOff > INT_MAX)
1119 0 : *pnOutYOff = INT_MAX;
1120 : else
1121 174 : *pnOutYOff = static_cast<int>(dfOutYOff + EPS);
1122 :
1123 : // Apply correction on floating-point source window
1124 : {
1125 344 : double dfDstDeltaY =
1126 344 : (dfOutYOff - *pnOutYOff) / dfScaleWinToBufY;
1127 344 : double dfSrcDeltaY = dfDstDeltaY / m_dfDstYSize * m_dfSrcYSize;
1128 344 : *pdfReqYOff -= dfSrcDeltaY;
1129 688 : *pdfReqYSize = std::min(*pdfReqYSize + dfSrcDeltaY,
1130 344 : static_cast<double>(INT_MAX));
1131 : }
1132 :
1133 344 : double dfOutTopYOff = (dfDstLRY - dfYOff) * dfScaleWinToBufY;
1134 344 : if (dfOutTopYOff < dfOutYOff)
1135 0 : return FALSE;
1136 344 : if (dfOutTopYOff > INT_MAX)
1137 0 : dfOutTopYOff = INT_MAX;
1138 344 : const int nOutTopYOff = static_cast<int>(ceil(dfOutTopYOff - EPS));
1139 344 : *pnOutYSize = nOutTopYOff - *pnOutYOff;
1140 :
1141 344 : if (*pnOutYSize > INT_MAX - *pnOutYOff ||
1142 344 : *pnOutYOff + *pnOutYSize > nBufYSize)
1143 0 : *pnOutYSize = nBufYSize - *pnOutYOff;
1144 :
1145 : // Apply correction on floating-point source window
1146 : {
1147 344 : double dfDstDeltaY =
1148 344 : (nOutTopYOff - dfOutTopYOff) / dfScaleWinToBufY;
1149 344 : double dfSrcDeltaY = dfDstDeltaY / m_dfDstYSize * m_dfSrcYSize;
1150 688 : *pdfReqYSize = std::min(*pdfReqYSize + dfSrcDeltaY,
1151 344 : static_cast<double>(INT_MAX));
1152 : }
1153 : }
1154 :
1155 2684 : if (*pnOutXSize < 1 || *pnOutYSize < 1)
1156 0 : return FALSE;
1157 : }
1158 :
1159 45150 : *pdfReqXOff = RoundIfCloseToInt(*pdfReqXOff);
1160 45150 : *pdfReqYOff = RoundIfCloseToInt(*pdfReqYOff);
1161 45150 : *pdfReqXSize = RoundIfCloseToInt(*pdfReqXSize);
1162 45150 : *pdfReqYSize = RoundIfCloseToInt(*pdfReqYSize);
1163 :
1164 45150 : return TRUE;
1165 : }
1166 :
1167 : /************************************************************************/
1168 : /* NeedMaxValAdjustment() */
1169 : /************************************************************************/
1170 :
1171 39266 : int VRTSimpleSource::NeedMaxValAdjustment() const
1172 : {
1173 39266 : if (!m_nMaxValue)
1174 39244 : return FALSE;
1175 :
1176 22 : auto l_band = GetRasterBand();
1177 22 : if (!l_band)
1178 0 : return FALSE;
1179 22 : const char *pszNBITS = l_band->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
1180 22 : const int nBits = (pszNBITS) ? atoi(pszNBITS) : 0;
1181 22 : if (nBits >= 1 && nBits <= 31)
1182 : {
1183 0 : const int nBandMaxValue = static_cast<int>((1U << nBits) - 1);
1184 0 : return nBandMaxValue > m_nMaxValue;
1185 : }
1186 22 : return TRUE;
1187 : }
1188 :
1189 : /************************************************************************/
1190 : /* RasterIO() */
1191 : /************************************************************************/
1192 :
1193 46975 : CPLErr VRTSimpleSource::RasterIO(GDALDataType eVRTBandDataType, int nXOff,
1194 : int nYOff, int nXSize, int nYSize, void *pData,
1195 : int nBufXSize, int nBufYSize,
1196 : GDALDataType eBufType, GSpacing nPixelSpace,
1197 : GSpacing nLineSpace,
1198 : GDALRasterIOExtraArg *psExtraArgIn,
1199 : WorkingState & /*oWorkingState*/)
1200 :
1201 : {
1202 : GDALRasterIOExtraArg sExtraArg;
1203 46975 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1204 46975 : GDALRasterIOExtraArg *psExtraArg = &sExtraArg;
1205 :
1206 46975 : double dfXOff = nXOff;
1207 46975 : double dfYOff = nYOff;
1208 46975 : double dfXSize = nXSize;
1209 46975 : double dfYSize = nYSize;
1210 46975 : if (psExtraArgIn != nullptr && psExtraArgIn->bFloatingPointWindowValidity)
1211 : {
1212 50 : dfXOff = psExtraArgIn->dfXOff;
1213 50 : dfYOff = psExtraArgIn->dfYOff;
1214 50 : dfXSize = psExtraArgIn->dfXSize;
1215 50 : dfYSize = psExtraArgIn->dfYSize;
1216 : }
1217 :
1218 : // The window we will actually request from the source raster band.
1219 46975 : double dfReqXOff = 0.0;
1220 46975 : double dfReqYOff = 0.0;
1221 46975 : double dfReqXSize = 0.0;
1222 46975 : double dfReqYSize = 0.0;
1223 46975 : int nReqXOff = 0;
1224 46975 : int nReqYOff = 0;
1225 46975 : int nReqXSize = 0;
1226 46975 : int nReqYSize = 0;
1227 :
1228 : // The window we will actual set _within_ the pData buffer.
1229 46975 : int nOutXOff = 0;
1230 46975 : int nOutYOff = 0;
1231 46975 : int nOutXSize = 0;
1232 46975 : int nOutYSize = 0;
1233 :
1234 46975 : bool bError = false;
1235 46975 : if (!GetSrcDstWindow(dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
1236 : &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
1237 : &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
1238 : &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
1239 : {
1240 9243 : return bError ? CE_Failure : CE_None;
1241 : }
1242 : #if DEBUG_VERBOSE
1243 : CPLDebug("VRT",
1244 : "nXOff=%d, nYOff=%d, nXSize=%d, nYSize=%d, nBufXSize=%d, "
1245 : "nBufYSize=%d,\n"
1246 : "dfReqXOff=%g, dfReqYOff=%g, dfReqXSize=%g, dfReqYSize=%g,\n"
1247 : "nReqXOff=%d, nReqYOff=%d, nReqXSize=%d, nReqYSize=%d,\n"
1248 : "nOutXOff=%d, nOutYOff=%d, nOutXSize=%d, nOutYSize=%d",
1249 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, dfReqXOff,
1250 : dfReqYOff, dfReqXSize, dfReqYSize, nReqXOff, nReqYOff, nReqXSize,
1251 : nReqYSize, nOutXOff, nOutYOff, nOutXSize, nOutYSize);
1252 : #endif
1253 :
1254 : /* -------------------------------------------------------------------- */
1255 : /* Actually perform the IO request. */
1256 : /* -------------------------------------------------------------------- */
1257 37732 : if (!m_osResampling.empty())
1258 : {
1259 2030 : psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
1260 : }
1261 35702 : else if (psExtraArgIn != nullptr)
1262 : {
1263 35702 : psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg;
1264 : }
1265 37732 : psExtraArg->bFloatingPointWindowValidity = TRUE;
1266 37732 : psExtraArg->dfXOff = dfReqXOff;
1267 37732 : psExtraArg->dfYOff = dfReqYOff;
1268 37732 : psExtraArg->dfXSize = dfReqXSize;
1269 37732 : psExtraArg->dfYSize = dfReqYSize;
1270 :
1271 37732 : GByte *pabyOut = static_cast<unsigned char *>(pData) +
1272 37732 : nOutXOff * nPixelSpace +
1273 37732 : static_cast<GPtrDiff_t>(nOutYOff) * nLineSpace;
1274 :
1275 37732 : auto l_band = GetRasterBand();
1276 37732 : if (!l_band)
1277 0 : return CE_Failure;
1278 :
1279 37732 : CPLErr eErr = CE_Failure;
1280 37732 : if (GDALDataTypeIsConversionLossy(l_band->GetRasterDataType(),
1281 37732 : eVRTBandDataType))
1282 : {
1283 33 : const int nBandDTSize = GDALGetDataTypeSizeBytes(eVRTBandDataType);
1284 33 : void *pTemp = VSI_MALLOC3_VERBOSE(nOutXSize, nOutYSize, nBandDTSize);
1285 33 : if (pTemp)
1286 : {
1287 33 : eErr = l_band->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize,
1288 : nReqYSize, pTemp, nOutXSize, nOutYSize,
1289 : eVRTBandDataType, 0, 0, psExtraArg);
1290 33 : if (eErr == CE_None)
1291 : {
1292 33 : GByte *pabyTemp = static_cast<GByte *>(pTemp);
1293 592 : for (int iY = 0; iY < nOutYSize; iY++)
1294 : {
1295 559 : GDALCopyWords(
1296 559 : pabyTemp +
1297 559 : static_cast<size_t>(iY) * nBandDTSize * nOutXSize,
1298 : eVRTBandDataType, nBandDTSize,
1299 559 : pabyOut + static_cast<GPtrDiff_t>(iY * nLineSpace),
1300 : eBufType, static_cast<int>(nPixelSpace), nOutXSize);
1301 : }
1302 : }
1303 33 : VSIFree(pTemp);
1304 : }
1305 : }
1306 : else
1307 : {
1308 37699 : eErr = l_band->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize,
1309 : nReqYSize, pabyOut, nOutXSize, nOutYSize,
1310 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
1311 : }
1312 :
1313 37732 : if (NeedMaxValAdjustment())
1314 : {
1315 213 : for (int j = 0; j < nOutYSize; j++)
1316 : {
1317 4200 : for (int i = 0; i < nOutXSize; i++)
1318 : {
1319 4000 : int nVal = 0;
1320 4000 : GDALCopyWords(pabyOut + j * nLineSpace + i * nPixelSpace,
1321 : eBufType, 0, &nVal, GDT_Int32, 0, 1);
1322 4000 : if (nVal > m_nMaxValue)
1323 400 : nVal = m_nMaxValue;
1324 4000 : GDALCopyWords(&nVal, GDT_Int32, 0,
1325 4000 : pabyOut + j * nLineSpace + i * nPixelSpace,
1326 : eBufType, 0, 1);
1327 : }
1328 : }
1329 : }
1330 :
1331 37732 : return eErr;
1332 : }
1333 :
1334 : /************************************************************************/
1335 : /* GetMinimum() */
1336 : /************************************************************************/
1337 :
1338 52 : double VRTSimpleSource::GetMinimum(int nXSize, int nYSize, int *pbSuccess)
1339 : {
1340 : // The window we will actually request from the source raster band.
1341 52 : double dfReqXOff = 0.0;
1342 52 : double dfReqYOff = 0.0;
1343 52 : double dfReqXSize = 0.0;
1344 52 : double dfReqYSize = 0.0;
1345 52 : int nReqXOff = 0;
1346 52 : int nReqYOff = 0;
1347 52 : int nReqXSize = 0;
1348 52 : int nReqYSize = 0;
1349 :
1350 : // The window we will actual set _within_ the pData buffer.
1351 52 : int nOutXOff = 0;
1352 52 : int nOutYOff = 0;
1353 52 : int nOutXSize = 0;
1354 52 : int nOutYSize = 0;
1355 :
1356 52 : bool bError = false;
1357 52 : auto l_band = GetRasterBand();
1358 104 : if (!l_band ||
1359 52 : !GetSrcDstWindow(0, 0, nXSize, nYSize, nXSize, nYSize, &dfReqXOff,
1360 : &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
1361 : &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff,
1362 52 : &nOutYOff, &nOutXSize, &nOutYSize, bError) ||
1363 156 : nReqXOff != 0 || nReqYOff != 0 || nReqXSize != l_band->GetXSize() ||
1364 52 : nReqYSize != l_band->GetYSize())
1365 : {
1366 0 : *pbSuccess = FALSE;
1367 0 : return 0;
1368 : }
1369 :
1370 52 : const double dfVal = l_band->GetMinimum(pbSuccess);
1371 52 : if (NeedMaxValAdjustment() && dfVal > m_nMaxValue)
1372 2 : return m_nMaxValue;
1373 50 : return dfVal;
1374 : }
1375 :
1376 : /************************************************************************/
1377 : /* GetMaximum() */
1378 : /************************************************************************/
1379 :
1380 51 : double VRTSimpleSource::GetMaximum(int nXSize, int nYSize, int *pbSuccess)
1381 : {
1382 : // The window we will actually request from the source raster band.
1383 51 : double dfReqXOff = 0.0;
1384 51 : double dfReqYOff = 0.0;
1385 51 : double dfReqXSize = 0.0;
1386 51 : double dfReqYSize = 0.0;
1387 51 : int nReqXOff = 0;
1388 51 : int nReqYOff = 0;
1389 51 : int nReqXSize = 0;
1390 51 : int nReqYSize = 0;
1391 :
1392 : // The window we will actual set _within_ the pData buffer.
1393 51 : int nOutXOff = 0;
1394 51 : int nOutYOff = 0;
1395 51 : int nOutXSize = 0;
1396 51 : int nOutYSize = 0;
1397 :
1398 51 : bool bError = false;
1399 51 : auto l_band = GetRasterBand();
1400 102 : if (!l_band ||
1401 51 : !GetSrcDstWindow(0, 0, nXSize, nYSize, nXSize, nYSize, &dfReqXOff,
1402 : &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
1403 : &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff,
1404 51 : &nOutYOff, &nOutXSize, &nOutYSize, bError) ||
1405 153 : nReqXOff != 0 || nReqYOff != 0 || nReqXSize != l_band->GetXSize() ||
1406 51 : nReqYSize != l_band->GetYSize())
1407 : {
1408 0 : *pbSuccess = FALSE;
1409 0 : return 0;
1410 : }
1411 :
1412 51 : const double dfVal = l_band->GetMaximum(pbSuccess);
1413 51 : if (NeedMaxValAdjustment() && dfVal > m_nMaxValue)
1414 2 : return m_nMaxValue;
1415 49 : return dfVal;
1416 : }
1417 :
1418 : /************************************************************************/
1419 : /* GetHistogram() */
1420 : /************************************************************************/
1421 :
1422 4 : CPLErr VRTSimpleSource::GetHistogram(int nXSize, int nYSize, double dfMin,
1423 : double dfMax, int nBuckets,
1424 : GUIntBig *panHistogram,
1425 : int bIncludeOutOfRange, int bApproxOK,
1426 : GDALProgressFunc pfnProgress,
1427 : void *pProgressData)
1428 : {
1429 : // The window we will actually request from the source raster band.
1430 4 : double dfReqXOff = 0.0;
1431 4 : double dfReqYOff = 0.0;
1432 4 : double dfReqXSize = 0.0;
1433 4 : double dfReqYSize = 0.0;
1434 4 : int nReqXOff = 0;
1435 4 : int nReqYOff = 0;
1436 4 : int nReqXSize = 0;
1437 4 : int nReqYSize = 0;
1438 :
1439 : // The window we will actual set _within_ the pData buffer.
1440 4 : int nOutXOff = 0;
1441 4 : int nOutYOff = 0;
1442 4 : int nOutXSize = 0;
1443 4 : int nOutYSize = 0;
1444 :
1445 4 : bool bError = false;
1446 4 : auto l_band = GetRasterBand();
1447 4 : if (!l_band || NeedMaxValAdjustment() ||
1448 4 : !GetSrcDstWindow(0, 0, nXSize, nYSize, nXSize, nYSize, &dfReqXOff,
1449 : &dfReqYOff, &dfReqXSize, &dfReqYSize, &nReqXOff,
1450 : &nReqYOff, &nReqXSize, &nReqYSize, &nOutXOff,
1451 4 : &nOutYOff, &nOutXSize, &nOutYSize, bError) ||
1452 12 : nReqXOff != 0 || nReqYOff != 0 || nReqXSize != l_band->GetXSize() ||
1453 4 : nReqYSize != l_band->GetYSize())
1454 : {
1455 0 : return CE_Failure;
1456 : }
1457 :
1458 4 : return l_band->GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
1459 : bIncludeOutOfRange, bApproxOK, pfnProgress,
1460 4 : pProgressData);
1461 : }
1462 :
1463 : /************************************************************************/
1464 : /* DatasetRasterIO() */
1465 : /************************************************************************/
1466 :
1467 1386 : CPLErr VRTSimpleSource::DatasetRasterIO(
1468 : GDALDataType eVRTBandDataType, int nXOff, int nYOff, int nXSize, int nYSize,
1469 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1470 : int nBandCount, int *panBandMap, GSpacing nPixelSpace, GSpacing nLineSpace,
1471 : GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArgIn)
1472 : {
1473 1386 : if (!EQUAL(GetType(), "SimpleSource"))
1474 : {
1475 0 : CPLError(CE_Failure, CPLE_NotSupported,
1476 0 : "DatasetRasterIO() not implemented for %s", GetType());
1477 0 : return CE_Failure;
1478 : }
1479 :
1480 : GDALRasterIOExtraArg sExtraArg;
1481 1386 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1482 1386 : GDALRasterIOExtraArg *psExtraArg = &sExtraArg;
1483 :
1484 1386 : double dfXOff = nXOff;
1485 1386 : double dfYOff = nYOff;
1486 1386 : double dfXSize = nXSize;
1487 1386 : double dfYSize = nYSize;
1488 1386 : if (psExtraArgIn != nullptr && psExtraArgIn->bFloatingPointWindowValidity)
1489 : {
1490 117 : dfXOff = psExtraArgIn->dfXOff;
1491 117 : dfYOff = psExtraArgIn->dfYOff;
1492 117 : dfXSize = psExtraArgIn->dfXSize;
1493 117 : dfYSize = psExtraArgIn->dfYSize;
1494 : }
1495 :
1496 : // The window we will actually request from the source raster band.
1497 1386 : double dfReqXOff = 0.0;
1498 1386 : double dfReqYOff = 0.0;
1499 1386 : double dfReqXSize = 0.0;
1500 1386 : double dfReqYSize = 0.0;
1501 1386 : int nReqXOff = 0;
1502 1386 : int nReqYOff = 0;
1503 1386 : int nReqXSize = 0;
1504 1386 : int nReqYSize = 0;
1505 :
1506 : // The window we will actual set _within_ the pData buffer.
1507 1386 : int nOutXOff = 0;
1508 1386 : int nOutYOff = 0;
1509 1386 : int nOutXSize = 0;
1510 1386 : int nOutYSize = 0;
1511 :
1512 1386 : bool bError = false;
1513 1386 : if (!GetSrcDstWindow(dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
1514 : &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
1515 : &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
1516 : &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
1517 : {
1518 15 : return bError ? CE_Failure : CE_None;
1519 : }
1520 :
1521 1371 : auto l_band = GetRasterBand();
1522 1371 : if (!l_band)
1523 0 : return CE_Failure;
1524 :
1525 1371 : GDALDataset *poDS = l_band->GetDataset();
1526 1371 : if (poDS == nullptr)
1527 0 : return CE_Failure;
1528 :
1529 1371 : if (!m_osResampling.empty())
1530 : {
1531 26 : psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
1532 : }
1533 1345 : else if (psExtraArgIn != nullptr)
1534 : {
1535 1345 : psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg;
1536 : }
1537 1371 : psExtraArg->bFloatingPointWindowValidity = TRUE;
1538 1371 : psExtraArg->dfXOff = dfReqXOff;
1539 1371 : psExtraArg->dfYOff = dfReqYOff;
1540 1371 : psExtraArg->dfXSize = dfReqXSize;
1541 1371 : psExtraArg->dfYSize = dfReqYSize;
1542 :
1543 1371 : GByte *pabyOut = static_cast<unsigned char *>(pData) +
1544 1371 : nOutXOff * nPixelSpace +
1545 1371 : static_cast<GPtrDiff_t>(nOutYOff) * nLineSpace;
1546 :
1547 1371 : CPLErr eErr = CE_Failure;
1548 :
1549 1371 : if (GDALDataTypeIsConversionLossy(l_band->GetRasterDataType(),
1550 1371 : eVRTBandDataType))
1551 : {
1552 12 : const int nBandDTSize = GDALGetDataTypeSizeBytes(eVRTBandDataType);
1553 12 : void *pTemp = VSI_MALLOC3_VERBOSE(
1554 : nOutXSize, nOutYSize, cpl::fits_on<int>(nBandDTSize * nBandCount));
1555 12 : if (pTemp)
1556 : {
1557 12 : eErr = poDS->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize,
1558 : nReqYSize, pTemp, nOutXSize, nOutYSize,
1559 : eVRTBandDataType, nBandCount, panBandMap, 0,
1560 : 0, 0, psExtraArg);
1561 12 : if (eErr == CE_None)
1562 : {
1563 12 : GByte *pabyTemp = static_cast<GByte *>(pTemp);
1564 12 : const size_t nSrcBandSpace =
1565 12 : static_cast<size_t>(nOutYSize) * nOutXSize * nBandDTSize;
1566 24 : for (int iBand = 0; iBand < nBandCount; iBand++)
1567 : {
1568 103 : for (int iY = 0; iY < nOutYSize; iY++)
1569 : {
1570 91 : GDALCopyWords(
1571 91 : pabyTemp + iBand * nSrcBandSpace +
1572 91 : static_cast<size_t>(iY) * nBandDTSize *
1573 91 : nOutXSize,
1574 : eVRTBandDataType, nBandDTSize,
1575 91 : pabyOut + static_cast<GPtrDiff_t>(
1576 91 : iY * nLineSpace + iBand * nBandSpace),
1577 : eBufType, static_cast<int>(nPixelSpace), nOutXSize);
1578 : }
1579 : }
1580 : }
1581 12 : VSIFree(pTemp);
1582 : }
1583 : }
1584 : else
1585 : {
1586 1359 : eErr = poDS->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
1587 : pabyOut, nOutXSize, nOutYSize, eBufType,
1588 : nBandCount, panBandMap, nPixelSpace, nLineSpace,
1589 : nBandSpace, psExtraArg);
1590 : }
1591 :
1592 1371 : if (NeedMaxValAdjustment())
1593 : {
1594 2 : for (int k = 0; k < nBandCount; k++)
1595 : {
1596 21 : for (int j = 0; j < nOutYSize; j++)
1597 : {
1598 420 : for (int i = 0; i < nOutXSize; i++)
1599 : {
1600 400 : int nVal = 0;
1601 400 : GDALCopyWords(pabyOut + k * nBandSpace + j * nLineSpace +
1602 400 : i * nPixelSpace,
1603 : eBufType, 0, &nVal, GDT_Int32, 0, 1);
1604 :
1605 400 : if (nVal > m_nMaxValue)
1606 400 : nVal = m_nMaxValue;
1607 :
1608 400 : GDALCopyWords(&nVal, GDT_Int32, 0,
1609 400 : pabyOut + k * nBandSpace + j * nLineSpace +
1610 400 : i * nPixelSpace,
1611 : eBufType, 0, 1);
1612 : }
1613 : }
1614 : }
1615 : }
1616 :
1617 1371 : return eErr;
1618 : }
1619 :
1620 : /************************************************************************/
1621 : /* SetResampling() */
1622 : /************************************************************************/
1623 :
1624 1545 : void VRTSimpleSource::SetResampling(const char *pszResampling)
1625 : {
1626 1545 : m_osResampling = (pszResampling) ? pszResampling : "";
1627 1545 : }
1628 :
1629 : /************************************************************************/
1630 : /* ==================================================================== */
1631 : /* VRTAveragedSource */
1632 : /* ==================================================================== */
1633 : /************************************************************************/
1634 :
1635 : /************************************************************************/
1636 : /* VRTAveragedSource() */
1637 : /************************************************************************/
1638 :
1639 16 : VRTAveragedSource::VRTAveragedSource()
1640 : {
1641 16 : }
1642 :
1643 : /************************************************************************/
1644 : /* SerializeToXML() */
1645 : /************************************************************************/
1646 :
1647 0 : CPLXMLNode *VRTAveragedSource::SerializeToXML(const char *pszVRTPath)
1648 :
1649 : {
1650 0 : CPLXMLNode *const psSrc = VRTSimpleSource::SerializeToXML(pszVRTPath);
1651 :
1652 0 : if (psSrc == nullptr)
1653 0 : return nullptr;
1654 :
1655 0 : CPLFree(psSrc->pszValue);
1656 0 : psSrc->pszValue = CPLStrdup("AveragedSource");
1657 :
1658 0 : return psSrc;
1659 : }
1660 :
1661 : /************************************************************************/
1662 : /* SetNoDataValue() */
1663 : /************************************************************************/
1664 :
1665 0 : void VRTAveragedSource::SetNoDataValue(double dfNewNoDataValue)
1666 :
1667 : {
1668 0 : if (dfNewNoDataValue == VRT_NODATA_UNSET)
1669 : {
1670 0 : m_bNoDataSet = FALSE;
1671 0 : m_dfNoDataValue = VRT_NODATA_UNSET;
1672 0 : return;
1673 : }
1674 :
1675 0 : m_bNoDataSet = TRUE;
1676 0 : m_dfNoDataValue = dfNewNoDataValue;
1677 : }
1678 :
1679 : /************************************************************************/
1680 : /* RasterIO() */
1681 : /************************************************************************/
1682 :
1683 33 : CPLErr VRTAveragedSource::RasterIO(GDALDataType /*eVRTBandDataType*/, int nXOff,
1684 : int nYOff, int nXSize, int nYSize,
1685 : void *pData, int nBufXSize, int nBufYSize,
1686 : GDALDataType eBufType, GSpacing nPixelSpace,
1687 : GSpacing nLineSpace,
1688 : GDALRasterIOExtraArg *psExtraArgIn,
1689 : WorkingState & /*oWorkingState*/)
1690 :
1691 : {
1692 : GDALRasterIOExtraArg sExtraArg;
1693 33 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1694 33 : GDALRasterIOExtraArg *psExtraArg = &sExtraArg;
1695 :
1696 33 : double dfXOff = nXOff;
1697 33 : double dfYOff = nYOff;
1698 33 : double dfXSize = nXSize;
1699 33 : double dfYSize = nYSize;
1700 33 : if (psExtraArgIn != nullptr && psExtraArgIn->bFloatingPointWindowValidity)
1701 : {
1702 0 : dfXOff = psExtraArgIn->dfXOff;
1703 0 : dfYOff = psExtraArgIn->dfYOff;
1704 0 : dfXSize = psExtraArgIn->dfXSize;
1705 0 : dfYSize = psExtraArgIn->dfYSize;
1706 : }
1707 :
1708 : // The window we will actually request from the source raster band.
1709 33 : double dfReqXOff = 0.0;
1710 33 : double dfReqYOff = 0.0;
1711 33 : double dfReqXSize = 0.0;
1712 33 : double dfReqYSize = 0.0;
1713 33 : int nReqXOff = 0;
1714 33 : int nReqYOff = 0;
1715 33 : int nReqXSize = 0;
1716 33 : int nReqYSize = 0;
1717 :
1718 : // The window we will actual set _within_ the pData buffer.
1719 33 : int nOutXOff = 0;
1720 33 : int nOutYOff = 0;
1721 33 : int nOutXSize = 0;
1722 33 : int nOutYSize = 0;
1723 :
1724 33 : bool bError = false;
1725 33 : if (!GetSrcDstWindow(dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
1726 : &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
1727 : &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
1728 : &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
1729 : {
1730 0 : return bError ? CE_Failure : CE_None;
1731 : }
1732 :
1733 33 : auto l_band = GetRasterBand();
1734 33 : if (!l_band)
1735 0 : return CE_Failure;
1736 :
1737 : /* -------------------------------------------------------------------- */
1738 : /* Allocate a temporary buffer to whole the full resolution */
1739 : /* data from the area of interest. */
1740 : /* -------------------------------------------------------------------- */
1741 : float *const pafSrc = static_cast<float *>(
1742 33 : VSI_MALLOC3_VERBOSE(sizeof(float), nReqXSize, nReqYSize));
1743 33 : if (pafSrc == nullptr)
1744 : {
1745 0 : return CE_Failure;
1746 : }
1747 :
1748 : /* -------------------------------------------------------------------- */
1749 : /* Load it. */
1750 : /* -------------------------------------------------------------------- */
1751 33 : if (!m_osResampling.empty())
1752 : {
1753 28 : psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
1754 : }
1755 5 : else if (psExtraArgIn != nullptr)
1756 : {
1757 5 : psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg;
1758 : }
1759 :
1760 33 : psExtraArg->bFloatingPointWindowValidity = TRUE;
1761 33 : psExtraArg->dfXOff = dfReqXOff;
1762 33 : psExtraArg->dfYOff = dfReqYOff;
1763 33 : psExtraArg->dfXSize = dfReqXSize;
1764 33 : psExtraArg->dfYSize = dfReqYSize;
1765 :
1766 33 : const CPLErr eErr = l_band->RasterIO(
1767 : GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize, pafSrc, nReqXSize,
1768 : nReqYSize, GDT_Float32, 0, 0, psExtraArg);
1769 :
1770 33 : if (eErr != CE_None)
1771 : {
1772 0 : VSIFree(pafSrc);
1773 0 : return eErr;
1774 : }
1775 :
1776 : /* -------------------------------------------------------------------- */
1777 : /* Do the averaging. */
1778 : /* -------------------------------------------------------------------- */
1779 5956 : for (int iBufLine = nOutYOff; iBufLine < nOutYOff + nOutYSize; iBufLine++)
1780 : {
1781 5923 : const double dfYDst =
1782 5923 : (iBufLine / static_cast<double>(nBufYSize)) * nYSize + nYOff;
1783 :
1784 2017080 : for (int iBufPixel = nOutXOff; iBufPixel < nOutXOff + nOutXSize;
1785 : iBufPixel++)
1786 : {
1787 : double dfXSrcStart, dfXSrcEnd, dfYSrcStart, dfYSrcEnd;
1788 : int iXSrcStart, iYSrcStart, iXSrcEnd, iYSrcEnd;
1789 :
1790 2011160 : const double dfXDst =
1791 2011160 : (iBufPixel / static_cast<double>(nBufXSize)) * nXSize + nXOff;
1792 :
1793 : // Compute the source image rectangle needed for this pixel.
1794 2011160 : DstToSrc(dfXDst, dfYDst, dfXSrcStart, dfYSrcStart);
1795 2011160 : DstToSrc(dfXDst + 1.0, dfYDst + 1.0, dfXSrcEnd, dfYSrcEnd);
1796 :
1797 : // Convert to integers, assuming that the center of the source
1798 : // pixel must be in our rect to get included.
1799 2011160 : if (dfXSrcEnd >= dfXSrcStart + 1)
1800 : {
1801 1049560 : iXSrcStart = static_cast<int>(floor(dfXSrcStart + 0.5));
1802 1049560 : iXSrcEnd = static_cast<int>(floor(dfXSrcEnd + 0.5));
1803 : }
1804 : else
1805 : {
1806 : /* If the resampling factor is less than 100%, the distance */
1807 : /* between the source pixel is < 1, so we stick to nearest */
1808 : /* neighbour */
1809 961600 : iXSrcStart = static_cast<int>(floor(dfXSrcStart));
1810 961600 : iXSrcEnd = iXSrcStart + 1;
1811 : }
1812 2011160 : if (dfYSrcEnd >= dfYSrcStart + 1)
1813 : {
1814 1049560 : iYSrcStart = static_cast<int>(floor(dfYSrcStart + 0.5));
1815 1049560 : iYSrcEnd = static_cast<int>(floor(dfYSrcEnd + 0.5));
1816 : }
1817 : else
1818 : {
1819 961600 : iYSrcStart = static_cast<int>(floor(dfYSrcStart));
1820 961600 : iYSrcEnd = iYSrcStart + 1;
1821 : }
1822 :
1823 : // Transform into the coordinate system of the source *buffer*
1824 2011160 : iXSrcStart -= nReqXOff;
1825 2011160 : iYSrcStart -= nReqYOff;
1826 2011160 : iXSrcEnd -= nReqXOff;
1827 2011160 : iYSrcEnd -= nReqYOff;
1828 :
1829 2011160 : double dfSum = 0.0;
1830 2011160 : int nPixelCount = 0;
1831 :
1832 4022510 : for (int iY = iYSrcStart; iY < iYSrcEnd; iY++)
1833 : {
1834 2011360 : if (iY < 0 || iY >= nReqYSize)
1835 0 : continue;
1836 :
1837 4023130 : for (int iX = iXSrcStart; iX < iXSrcEnd; iX++)
1838 : {
1839 2011780 : if (iX < 0 || iX >= nReqXSize)
1840 0 : continue;
1841 :
1842 2011780 : const float fSampledValue =
1843 2011780 : pafSrc[iX + static_cast<size_t>(iY) * nReqXSize];
1844 2011780 : if (CPLIsNan(fSampledValue))
1845 0 : continue;
1846 :
1847 0 : if (m_bNoDataSet &&
1848 2011780 : GDALIsValueInRange<float>(m_dfNoDataValue) &&
1849 0 : ARE_REAL_EQUAL(fSampledValue,
1850 0 : static_cast<float>(m_dfNoDataValue)))
1851 0 : continue;
1852 :
1853 2011780 : nPixelCount++;
1854 2011780 : dfSum += pafSrc[iX + static_cast<size_t>(iY) * nReqXSize];
1855 : }
1856 : }
1857 :
1858 2011160 : if (nPixelCount == 0)
1859 0 : continue;
1860 :
1861 : // Compute output value.
1862 2011160 : const float dfOutputValue = static_cast<float>(dfSum / nPixelCount);
1863 :
1864 : // Put it in the output buffer.
1865 2011160 : GByte *pDstLocation =
1866 2011160 : static_cast<GByte *>(pData) + nPixelSpace * iBufPixel +
1867 2011160 : static_cast<GPtrDiff_t>(nLineSpace) * iBufLine;
1868 :
1869 2011160 : if (eBufType == GDT_Byte)
1870 2008660 : *pDstLocation = static_cast<GByte>(
1871 2008660 : std::min(255.0, std::max(0.0, dfOutputValue + 0.5)));
1872 : else
1873 2500 : GDALCopyWords(&dfOutputValue, GDT_Float32, 4, pDstLocation,
1874 : eBufType, 8, 1);
1875 : }
1876 : }
1877 :
1878 33 : VSIFree(pafSrc);
1879 :
1880 33 : return CE_None;
1881 : }
1882 :
1883 : /************************************************************************/
1884 : /* GetMinimum() */
1885 : /************************************************************************/
1886 :
1887 0 : double VRTAveragedSource::GetMinimum(int /* nXSize */, int /* nYSize */,
1888 : int *pbSuccess)
1889 : {
1890 0 : *pbSuccess = FALSE;
1891 0 : return 0.0;
1892 : }
1893 :
1894 : /************************************************************************/
1895 : /* GetMaximum() */
1896 : /************************************************************************/
1897 :
1898 0 : double VRTAveragedSource::GetMaximum(int /* nXSize */, int /* nYSize */,
1899 : int *pbSuccess)
1900 : {
1901 0 : *pbSuccess = FALSE;
1902 0 : return 0.0;
1903 : }
1904 :
1905 : /************************************************************************/
1906 : /* GetHistogram() */
1907 : /************************************************************************/
1908 :
1909 0 : CPLErr VRTAveragedSource::GetHistogram(
1910 : int /* nXSize */, int /* nYSize */, double /* dfMin */, double /* dfMax */,
1911 : int /* nBuckets */, GUIntBig * /* panHistogram */,
1912 : int /* bIncludeOutOfRange */, int /* bApproxOK */,
1913 : GDALProgressFunc /* pfnProgress */, void * /* pProgressData */)
1914 : {
1915 0 : return CE_Failure;
1916 : }
1917 :
1918 : /************************************************************************/
1919 : /* ==================================================================== */
1920 : /* VRTNoDataFromMaskSource */
1921 : /* ==================================================================== */
1922 : /************************************************************************/
1923 :
1924 : /************************************************************************/
1925 : /* VRTNoDataFromMaskSource() */
1926 : /************************************************************************/
1927 :
1928 23 : VRTNoDataFromMaskSource::VRTNoDataFromMaskSource()
1929 : {
1930 23 : }
1931 :
1932 : /************************************************************************/
1933 : /* XMLInit() */
1934 : /************************************************************************/
1935 :
1936 8 : CPLErr VRTNoDataFromMaskSource::XMLInit(
1937 : const CPLXMLNode *psSrc, const char *pszVRTPath,
1938 : std::map<CPLString, GDALDataset *> &oMapSharedSources)
1939 :
1940 : {
1941 : /* -------------------------------------------------------------------- */
1942 : /* Do base initialization. */
1943 : /* -------------------------------------------------------------------- */
1944 : {
1945 : const CPLErr eErr =
1946 8 : VRTSimpleSource::XMLInit(psSrc, pszVRTPath, oMapSharedSources);
1947 8 : if (eErr != CE_None)
1948 0 : return eErr;
1949 : }
1950 :
1951 8 : if (const char *pszNODATA = CPLGetXMLValue(psSrc, "NODATA", nullptr))
1952 : {
1953 8 : m_bNoDataSet = true;
1954 8 : m_dfNoDataValue = CPLAtofM(pszNODATA);
1955 : }
1956 :
1957 8 : m_dfMaskValueThreshold =
1958 8 : CPLAtofM(CPLGetXMLValue(psSrc, "MaskValueThreshold", "0"));
1959 :
1960 8 : if (const char *pszRemappedValue =
1961 8 : CPLGetXMLValue(psSrc, "RemappedValue", nullptr))
1962 : {
1963 0 : m_bHasRemappedValue = true;
1964 0 : m_dfRemappedValue = CPLAtofM(pszRemappedValue);
1965 : }
1966 :
1967 8 : return CE_None;
1968 : }
1969 :
1970 : /************************************************************************/
1971 : /* SerializeToXML() */
1972 : /************************************************************************/
1973 :
1974 8 : CPLXMLNode *VRTNoDataFromMaskSource::SerializeToXML(const char *pszVRTPath)
1975 :
1976 : {
1977 8 : CPLXMLNode *const psSrc = VRTSimpleSource::SerializeToXML(pszVRTPath);
1978 :
1979 8 : if (psSrc == nullptr)
1980 0 : return nullptr;
1981 :
1982 8 : CPLFree(psSrc->pszValue);
1983 8 : psSrc->pszValue = CPLStrdup("NoDataFromMaskSource");
1984 :
1985 8 : if (m_bNoDataSet)
1986 : {
1987 8 : CPLSetXMLValue(psSrc, "MaskValueThreshold",
1988 : CPLSPrintf("%.18g", m_dfMaskValueThreshold));
1989 :
1990 8 : GDALDataType eBandDT = GDT_Unknown;
1991 8 : double dfNoDataValue = m_dfNoDataValue;
1992 8 : const auto kMaxFloat = std::numeric_limits<float>::max();
1993 8 : if (std::fabs(std::fabs(m_dfNoDataValue) - kMaxFloat) <
1994 : 1e-10 * kMaxFloat)
1995 : {
1996 0 : auto l_band = GetRasterBand();
1997 0 : if (l_band)
1998 : {
1999 0 : eBandDT = l_band->GetRasterDataType();
2000 0 : if (eBandDT == GDT_Float32)
2001 : {
2002 : dfNoDataValue =
2003 0 : GDALAdjustNoDataCloseToFloatMax(m_dfNoDataValue);
2004 : }
2005 : }
2006 : }
2007 8 : CPLSetXMLValue(psSrc, "NODATA",
2008 16 : VRTSerializeNoData(dfNoDataValue, eBandDT, 18).c_str());
2009 : }
2010 :
2011 8 : if (m_bHasRemappedValue)
2012 : {
2013 0 : CPLSetXMLValue(psSrc, "RemappedValue",
2014 : CPLSPrintf("%.18g", m_dfRemappedValue));
2015 : }
2016 :
2017 8 : return psSrc;
2018 : }
2019 :
2020 : /************************************************************************/
2021 : /* SetParameters() */
2022 : /************************************************************************/
2023 :
2024 15 : void VRTNoDataFromMaskSource::SetParameters(double dfNoDataValue,
2025 : double dfMaskValueThreshold)
2026 : {
2027 15 : m_bNoDataSet = true;
2028 15 : m_dfNoDataValue = dfNoDataValue;
2029 15 : m_dfMaskValueThreshold = dfMaskValueThreshold;
2030 15 : if (!m_bHasRemappedValue)
2031 15 : m_dfRemappedValue = m_dfNoDataValue;
2032 15 : }
2033 :
2034 : /************************************************************************/
2035 : /* SetParameters() */
2036 : /************************************************************************/
2037 :
2038 0 : void VRTNoDataFromMaskSource::SetParameters(double dfNoDataValue,
2039 : double dfMaskValueThreshold,
2040 : double dfRemappedValue)
2041 : {
2042 0 : SetParameters(dfNoDataValue, dfMaskValueThreshold);
2043 0 : m_bHasRemappedValue = true;
2044 0 : m_dfRemappedValue = dfRemappedValue;
2045 0 : }
2046 :
2047 : /************************************************************************/
2048 : /* RasterIO() */
2049 : /************************************************************************/
2050 :
2051 13 : CPLErr VRTNoDataFromMaskSource::RasterIO(
2052 : GDALDataType eVRTBandDataType, int nXOff, int nYOff, int nXSize, int nYSize,
2053 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
2054 : GSpacing nPixelSpace, GSpacing nLineSpace,
2055 : GDALRasterIOExtraArg *psExtraArgIn, WorkingState &oWorkingState)
2056 :
2057 : {
2058 13 : if (!m_bNoDataSet)
2059 : {
2060 0 : return VRTSimpleSource::RasterIO(eVRTBandDataType, nXOff, nYOff, nXSize,
2061 : nYSize, pData, nBufXSize, nBufYSize,
2062 : eBufType, nPixelSpace, nLineSpace,
2063 0 : psExtraArgIn, oWorkingState);
2064 : }
2065 :
2066 : GDALRasterIOExtraArg sExtraArg;
2067 13 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
2068 13 : GDALRasterIOExtraArg *psExtraArg = &sExtraArg;
2069 :
2070 13 : double dfXOff = nXOff;
2071 13 : double dfYOff = nYOff;
2072 13 : double dfXSize = nXSize;
2073 13 : double dfYSize = nYSize;
2074 13 : if (psExtraArgIn != nullptr && psExtraArgIn->bFloatingPointWindowValidity)
2075 : {
2076 0 : dfXOff = psExtraArgIn->dfXOff;
2077 0 : dfYOff = psExtraArgIn->dfYOff;
2078 0 : dfXSize = psExtraArgIn->dfXSize;
2079 0 : dfYSize = psExtraArgIn->dfYSize;
2080 : }
2081 :
2082 : // The window we will actually request from the source raster band.
2083 13 : double dfReqXOff = 0.0;
2084 13 : double dfReqYOff = 0.0;
2085 13 : double dfReqXSize = 0.0;
2086 13 : double dfReqYSize = 0.0;
2087 13 : int nReqXOff = 0;
2088 13 : int nReqYOff = 0;
2089 13 : int nReqXSize = 0;
2090 13 : int nReqYSize = 0;
2091 :
2092 : // The window we will actual set _within_ the pData buffer.
2093 13 : int nOutXOff = 0;
2094 13 : int nOutYOff = 0;
2095 13 : int nOutXSize = 0;
2096 13 : int nOutYSize = 0;
2097 :
2098 13 : bool bError = false;
2099 13 : if (!GetSrcDstWindow(dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
2100 : &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
2101 : &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
2102 : &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
2103 : {
2104 0 : return bError ? CE_Failure : CE_None;
2105 : }
2106 :
2107 13 : auto l_band = GetRasterBand();
2108 13 : if (!l_band)
2109 0 : return CE_Failure;
2110 :
2111 : /* -------------------------------------------------------------------- */
2112 : /* Allocate temporary buffer(s). */
2113 : /* -------------------------------------------------------------------- */
2114 13 : const auto eSrcBandDT = l_band->GetRasterDataType();
2115 13 : const int nSrcBandDTSize = GDALGetDataTypeSizeBytes(eSrcBandDT);
2116 13 : const auto eSrcMaskBandDT = l_band->GetMaskBand()->GetRasterDataType();
2117 13 : const int nSrcMaskBandDTSize = GDALGetDataTypeSizeBytes(eSrcMaskBandDT);
2118 13 : double dfRemappedValue = m_dfRemappedValue;
2119 13 : if (!m_bHasRemappedValue)
2120 : {
2121 19 : if (eSrcBandDT == GDT_Byte &&
2122 6 : m_dfNoDataValue >= std::numeric_limits<GByte>::min() &&
2123 25 : m_dfNoDataValue <= std::numeric_limits<GByte>::max() &&
2124 6 : static_cast<int>(m_dfNoDataValue) == m_dfNoDataValue)
2125 : {
2126 6 : if (m_dfNoDataValue == std::numeric_limits<GByte>::max())
2127 1 : dfRemappedValue = m_dfNoDataValue - 1;
2128 : else
2129 5 : dfRemappedValue = m_dfNoDataValue + 1;
2130 : }
2131 10 : else if (eSrcBandDT == GDT_UInt16 &&
2132 3 : m_dfNoDataValue >= std::numeric_limits<uint16_t>::min() &&
2133 13 : m_dfNoDataValue <= std::numeric_limits<uint16_t>::max() &&
2134 3 : static_cast<int>(m_dfNoDataValue) == m_dfNoDataValue)
2135 : {
2136 3 : if (m_dfNoDataValue == std::numeric_limits<uint16_t>::max())
2137 1 : dfRemappedValue = m_dfNoDataValue - 1;
2138 : else
2139 2 : dfRemappedValue = m_dfNoDataValue + 1;
2140 : }
2141 6 : else if (eSrcBandDT == GDT_Int16 &&
2142 2 : m_dfNoDataValue >= std::numeric_limits<int16_t>::min() &&
2143 8 : m_dfNoDataValue <= std::numeric_limits<int16_t>::max() &&
2144 2 : static_cast<int>(m_dfNoDataValue) == m_dfNoDataValue)
2145 : {
2146 2 : if (m_dfNoDataValue == std::numeric_limits<int16_t>::max())
2147 1 : dfRemappedValue = m_dfNoDataValue - 1;
2148 : else
2149 1 : dfRemappedValue = m_dfNoDataValue + 1;
2150 : }
2151 : else
2152 : {
2153 2 : constexpr double EPS = 1e-3;
2154 2 : if (m_dfNoDataValue == 0)
2155 1 : dfRemappedValue = EPS;
2156 : else
2157 1 : dfRemappedValue = m_dfNoDataValue * (1 + EPS);
2158 : }
2159 : }
2160 13 : const bool bByteOptim =
2161 6 : (eSrcBandDT == GDT_Byte && eBufType == GDT_Byte &&
2162 5 : eSrcMaskBandDT == GDT_Byte && m_dfMaskValueThreshold >= 0 &&
2163 5 : m_dfMaskValueThreshold <= 255 &&
2164 5 : static_cast<int>(m_dfMaskValueThreshold) == m_dfMaskValueThreshold &&
2165 4 : m_dfNoDataValue >= 0 && m_dfNoDataValue <= 255 &&
2166 4 : static_cast<int>(m_dfNoDataValue) == m_dfNoDataValue &&
2167 23 : dfRemappedValue >= 0 && dfRemappedValue <= 255 &&
2168 4 : static_cast<int>(dfRemappedValue) == dfRemappedValue);
2169 : GByte *pabyWrkBuffer;
2170 : try
2171 : {
2172 13 : if (bByteOptim && nOutXOff == 0 && nOutYOff == 0 &&
2173 4 : nOutXSize == nBufXSize && nOutYSize == nBufYSize &&
2174 4 : eSrcBandDT == eBufType && nPixelSpace == nSrcBandDTSize &&
2175 4 : nLineSpace == nPixelSpace * nBufXSize)
2176 : {
2177 4 : pabyWrkBuffer = static_cast<GByte *>(pData);
2178 : }
2179 : else
2180 : {
2181 9 : oWorkingState.m_abyWrkBuffer.resize(static_cast<size_t>(nOutXSize) *
2182 9 : nOutYSize * nSrcBandDTSize);
2183 : pabyWrkBuffer =
2184 9 : reinterpret_cast<GByte *>(oWorkingState.m_abyWrkBuffer.data());
2185 : }
2186 13 : oWorkingState.m_abyWrkBufferMask.resize(static_cast<size_t>(nOutXSize) *
2187 13 : nOutYSize * nSrcMaskBandDTSize);
2188 : }
2189 0 : catch (const std::exception &)
2190 : {
2191 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2192 : "Out of memory when allocating buffers");
2193 0 : return CE_Failure;
2194 : }
2195 :
2196 : /* -------------------------------------------------------------------- */
2197 : /* Load data. */
2198 : /* -------------------------------------------------------------------- */
2199 13 : if (!m_osResampling.empty())
2200 : {
2201 0 : psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
2202 : }
2203 13 : else if (psExtraArgIn != nullptr)
2204 : {
2205 13 : psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg;
2206 : }
2207 :
2208 13 : psExtraArg->bFloatingPointWindowValidity = TRUE;
2209 13 : psExtraArg->dfXOff = dfReqXOff;
2210 13 : psExtraArg->dfYOff = dfReqYOff;
2211 13 : psExtraArg->dfXSize = dfReqXSize;
2212 13 : psExtraArg->dfYSize = dfReqYSize;
2213 :
2214 13 : if (l_band->RasterIO(GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
2215 : pabyWrkBuffer, nOutXSize, nOutYSize, eSrcBandDT, 0, 0,
2216 13 : psExtraArg) != CE_None)
2217 : {
2218 0 : return CE_Failure;
2219 : }
2220 :
2221 26 : if (l_band->GetMaskBand()->RasterIO(
2222 : GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
2223 13 : oWorkingState.m_abyWrkBufferMask.data(), nOutXSize, nOutYSize,
2224 13 : eSrcMaskBandDT, 0, 0, psExtraArg) != CE_None)
2225 : {
2226 0 : return CE_Failure;
2227 : }
2228 :
2229 : /* -------------------------------------------------------------------- */
2230 : /* Do the processing. */
2231 : /* -------------------------------------------------------------------- */
2232 :
2233 13 : GByte *const pabyOut = static_cast<GByte *>(pData) +
2234 13 : nPixelSpace * nOutXOff +
2235 13 : static_cast<GPtrDiff_t>(nLineSpace) * nOutYOff;
2236 13 : if (bByteOptim)
2237 : {
2238 : // Special case when everything fits on Byte
2239 4 : const GByte nMaskValueThreshold =
2240 4 : static_cast<GByte>(m_dfMaskValueThreshold);
2241 4 : const GByte nNoDataValue = static_cast<GByte>(m_dfNoDataValue);
2242 4 : const GByte nRemappedValue = static_cast<GByte>(dfRemappedValue);
2243 4 : size_t nSrcIdx = 0;
2244 8 : for (int iY = 0; iY < nOutYSize; iY++)
2245 : {
2246 4 : GSpacing nDstOffset = iY * nLineSpace;
2247 12 : for (int iX = 0; iX < nOutXSize; iX++)
2248 : {
2249 : const GByte nMaskVal =
2250 8 : oWorkingState.m_abyWrkBufferMask[nSrcIdx];
2251 8 : if (nMaskVal <= nMaskValueThreshold)
2252 : {
2253 4 : pabyOut[static_cast<GPtrDiff_t>(nDstOffset)] = nNoDataValue;
2254 : }
2255 : else
2256 : {
2257 4 : if (pabyWrkBuffer[nSrcIdx] == nNoDataValue)
2258 : {
2259 2 : pabyOut[static_cast<GPtrDiff_t>(nDstOffset)] =
2260 : nRemappedValue;
2261 : }
2262 : else
2263 : {
2264 2 : pabyOut[static_cast<GPtrDiff_t>(nDstOffset)] =
2265 2 : pabyWrkBuffer[nSrcIdx];
2266 : }
2267 : }
2268 8 : nDstOffset += nPixelSpace;
2269 8 : nSrcIdx++;
2270 : }
2271 : }
2272 : }
2273 : else
2274 : {
2275 9 : size_t nSrcIdx = 0;
2276 9 : double dfMaskVal = 0;
2277 9 : const int nBufDTSize = GDALGetDataTypeSizeBytes(eBufType);
2278 18 : std::vector<GByte> abyDstNoData(nBufDTSize);
2279 9 : GDALCopyWords(&m_dfNoDataValue, GDT_Float64, 0, abyDstNoData.data(),
2280 : eBufType, 0, 1);
2281 18 : std::vector<GByte> abyRemappedValue(nBufDTSize);
2282 9 : GDALCopyWords(&dfRemappedValue, GDT_Float64, 0, abyRemappedValue.data(),
2283 : eBufType, 0, 1);
2284 18 : for (int iY = 0; iY < nOutYSize; iY++)
2285 : {
2286 9 : GSpacing nDstOffset = iY * nLineSpace;
2287 28 : for (int iX = 0; iX < nOutXSize; iX++)
2288 : {
2289 19 : if (eSrcMaskBandDT == GDT_Byte)
2290 : {
2291 19 : dfMaskVal = oWorkingState.m_abyWrkBufferMask[nSrcIdx];
2292 : }
2293 : else
2294 : {
2295 0 : GDALCopyWords(oWorkingState.m_abyWrkBufferMask.data() +
2296 0 : nSrcIdx * nSrcMaskBandDTSize,
2297 : eSrcMaskBandDT, 0, &dfMaskVal, GDT_Float64, 0,
2298 : 1);
2299 : }
2300 19 : void *const pDst =
2301 19 : pabyOut + static_cast<GPtrDiff_t>(nDstOffset);
2302 19 : if (!(dfMaskVal > m_dfMaskValueThreshold))
2303 : {
2304 9 : memcpy(pDst, abyDstNoData.data(), nBufDTSize);
2305 : }
2306 : else
2307 : {
2308 10 : const void *const pSrc =
2309 10 : pabyWrkBuffer + nSrcIdx * nSrcBandDTSize;
2310 10 : if (eSrcBandDT == eBufType)
2311 : {
2312 : // coverity[overrun-buffer-arg]
2313 8 : memcpy(pDst, pSrc, nBufDTSize);
2314 : }
2315 : else
2316 : {
2317 2 : GDALCopyWords(pSrc, eSrcBandDT, 0, pDst, eBufType, 0,
2318 : 1);
2319 : }
2320 10 : if (memcmp(pDst, abyDstNoData.data(), nBufDTSize) == 0)
2321 9 : memcpy(pDst, abyRemappedValue.data(), nBufDTSize);
2322 : }
2323 19 : nDstOffset += nPixelSpace;
2324 19 : nSrcIdx++;
2325 : }
2326 : }
2327 : }
2328 :
2329 13 : return CE_None;
2330 : }
2331 :
2332 : /************************************************************************/
2333 : /* GetMinimum() */
2334 : /************************************************************************/
2335 :
2336 0 : double VRTNoDataFromMaskSource::GetMinimum(int /* nXSize */, int /* nYSize */,
2337 : int *pbSuccess)
2338 : {
2339 0 : *pbSuccess = FALSE;
2340 0 : return 0.0;
2341 : }
2342 :
2343 : /************************************************************************/
2344 : /* GetMaximum() */
2345 : /************************************************************************/
2346 :
2347 0 : double VRTNoDataFromMaskSource::GetMaximum(int /* nXSize */, int /* nYSize */,
2348 : int *pbSuccess)
2349 : {
2350 0 : *pbSuccess = FALSE;
2351 0 : return 0.0;
2352 : }
2353 :
2354 : /************************************************************************/
2355 : /* GetHistogram() */
2356 : /************************************************************************/
2357 :
2358 0 : CPLErr VRTNoDataFromMaskSource::GetHistogram(
2359 : int /* nXSize */, int /* nYSize */, double /* dfMin */, double /* dfMax */,
2360 : int /* nBuckets */, GUIntBig * /* panHistogram */,
2361 : int /* bIncludeOutOfRange */, int /* bApproxOK */,
2362 : GDALProgressFunc /* pfnProgress */, void * /* pProgressData */)
2363 : {
2364 0 : return CE_Failure;
2365 : }
2366 :
2367 : /************************************************************************/
2368 : /* ==================================================================== */
2369 : /* VRTComplexSource */
2370 : /* ==================================================================== */
2371 : /************************************************************************/
2372 :
2373 : /************************************************************************/
2374 : /* VRTComplexSource() */
2375 : /************************************************************************/
2376 :
2377 5 : VRTComplexSource::VRTComplexSource(const VRTComplexSource *poSrcSource,
2378 5 : double dfXDstRatio, double dfYDstRatio)
2379 : : VRTSimpleSource(poSrcSource, dfXDstRatio, dfYDstRatio),
2380 5 : m_nProcessingFlags(poSrcSource->m_nProcessingFlags),
2381 5 : m_dfNoDataValue(poSrcSource->m_dfNoDataValue),
2382 5 : m_osNoDataValueOri(poSrcSource->m_osNoDataValueOri),
2383 5 : m_dfScaleOff(poSrcSource->m_dfScaleOff),
2384 5 : m_dfScaleRatio(poSrcSource->m_dfScaleRatio),
2385 5 : m_bSrcMinMaxDefined(poSrcSource->m_bSrcMinMaxDefined),
2386 5 : m_dfSrcMin(poSrcSource->m_dfSrcMin), m_dfSrcMax(poSrcSource->m_dfSrcMax),
2387 5 : m_dfDstMin(poSrcSource->m_dfDstMin), m_dfDstMax(poSrcSource->m_dfDstMax),
2388 5 : m_dfExponent(poSrcSource->m_dfExponent),
2389 5 : m_nColorTableComponent(poSrcSource->m_nColorTableComponent),
2390 5 : m_adfLUTInputs(poSrcSource->m_adfLUTInputs),
2391 5 : m_adfLUTOutputs(poSrcSource->m_adfLUTOutputs)
2392 : {
2393 5 : }
2394 :
2395 : /************************************************************************/
2396 : /* SetNoDataValue() */
2397 : /************************************************************************/
2398 :
2399 43 : void VRTComplexSource::SetNoDataValue(double dfNewNoDataValue)
2400 :
2401 : {
2402 43 : if (dfNewNoDataValue == VRT_NODATA_UNSET)
2403 : {
2404 0 : m_nProcessingFlags &= ~PROCESSING_FLAG_NODATA;
2405 0 : m_dfNoDataValue = VRT_NODATA_UNSET;
2406 0 : return;
2407 : }
2408 :
2409 43 : m_nProcessingFlags |= PROCESSING_FLAG_NODATA;
2410 43 : m_dfNoDataValue = dfNewNoDataValue;
2411 : }
2412 :
2413 : /************************************************************************/
2414 : /* GetAdjustedNoDataValue() */
2415 : /************************************************************************/
2416 :
2417 4619 : double VRTComplexSource::GetAdjustedNoDataValue() const
2418 : {
2419 4619 : if ((m_nProcessingFlags & PROCESSING_FLAG_NODATA) != 0)
2420 : {
2421 7 : auto l_band = GetRasterBand();
2422 7 : if (l_band && l_band->GetRasterDataType() == GDT_Float32)
2423 : {
2424 5 : return GDALAdjustNoDataCloseToFloatMax(m_dfNoDataValue);
2425 : }
2426 : }
2427 4614 : return m_dfNoDataValue;
2428 : }
2429 :
2430 : /************************************************************************/
2431 : /* SerializeToXML() */
2432 : /************************************************************************/
2433 :
2434 42 : CPLXMLNode *VRTComplexSource::SerializeToXML(const char *pszVRTPath)
2435 :
2436 : {
2437 42 : CPLXMLNode *psSrc = VRTSimpleSource::SerializeToXML(pszVRTPath);
2438 :
2439 42 : if (psSrc == nullptr)
2440 0 : return nullptr;
2441 :
2442 42 : CPLFree(psSrc->pszValue);
2443 42 : psSrc->pszValue = CPLStrdup("ComplexSource");
2444 :
2445 42 : if ((m_nProcessingFlags & PROCESSING_FLAG_USE_MASK_BAND) != 0)
2446 : {
2447 14 : CPLSetXMLValue(psSrc, "UseMaskBand", "true");
2448 : }
2449 :
2450 42 : if ((m_nProcessingFlags & PROCESSING_FLAG_NODATA) != 0)
2451 : {
2452 19 : if (!m_osNoDataValueOri.empty() && GetRasterBandNoOpen() == nullptr)
2453 : {
2454 2 : CPLSetXMLValue(psSrc, "NODATA", m_osNoDataValueOri.c_str());
2455 : }
2456 : else
2457 : {
2458 17 : GDALDataType eBandDT = GDT_Unknown;
2459 17 : double dfNoDataValue = m_dfNoDataValue;
2460 17 : const auto kMaxFloat = std::numeric_limits<float>::max();
2461 17 : if (std::fabs(std::fabs(m_dfNoDataValue) - kMaxFloat) <
2462 : 1e-10 * kMaxFloat)
2463 : {
2464 1 : auto l_band = GetRasterBand();
2465 1 : if (l_band)
2466 : {
2467 1 : dfNoDataValue = GetAdjustedNoDataValue();
2468 1 : eBandDT = l_band->GetRasterDataType();
2469 : }
2470 : }
2471 17 : CPLSetXMLValue(
2472 : psSrc, "NODATA",
2473 34 : VRTSerializeNoData(dfNoDataValue, eBandDT, 18).c_str());
2474 : }
2475 : }
2476 :
2477 42 : if ((m_nProcessingFlags & PROCESSING_FLAG_SCALING_LINEAR) != 0)
2478 : {
2479 1 : CPLSetXMLValue(psSrc, "ScaleOffset", CPLSPrintf("%g", m_dfScaleOff));
2480 1 : CPLSetXMLValue(psSrc, "ScaleRatio", CPLSPrintf("%g", m_dfScaleRatio));
2481 : }
2482 41 : else if ((m_nProcessingFlags & PROCESSING_FLAG_SCALING_EXPONENTIAL) != 0)
2483 : {
2484 0 : CPLSetXMLValue(psSrc, "Exponent", CPLSPrintf("%g", m_dfExponent));
2485 0 : if (m_bSrcMinMaxDefined)
2486 : {
2487 0 : CPLSetXMLValue(psSrc, "SrcMin", CPLSPrintf("%g", m_dfSrcMin));
2488 0 : CPLSetXMLValue(psSrc, "SrcMax", CPLSPrintf("%g", m_dfSrcMax));
2489 : }
2490 0 : CPLSetXMLValue(psSrc, "DstMin", CPLSPrintf("%g", m_dfDstMin));
2491 0 : CPLSetXMLValue(psSrc, "DstMax", CPLSPrintf("%g", m_dfDstMax));
2492 : }
2493 :
2494 42 : if (!m_adfLUTInputs.empty())
2495 : {
2496 : // Make sure we print with sufficient precision to address really close
2497 : // entries (#6422).
2498 0 : CPLString osLUT;
2499 0 : if (m_adfLUTInputs.size() >= 2 &&
2500 0 : CPLString().Printf("%g", m_adfLUTInputs[0]) ==
2501 0 : CPLString().Printf("%g", m_adfLUTInputs[1]))
2502 : {
2503 0 : osLUT = CPLString().Printf("%.18g:%g", m_adfLUTInputs[0],
2504 0 : m_adfLUTOutputs[0]);
2505 : }
2506 : else
2507 : {
2508 0 : osLUT = CPLString().Printf("%g:%g", m_adfLUTInputs[0],
2509 0 : m_adfLUTOutputs[0]);
2510 : }
2511 0 : for (size_t i = 1; i < m_adfLUTInputs.size(); i++)
2512 : {
2513 0 : if (CPLString().Printf("%g", m_adfLUTInputs[i]) ==
2514 0 : CPLString().Printf("%g", m_adfLUTInputs[i - 1]) ||
2515 0 : (i + 1 < m_adfLUTInputs.size() &&
2516 0 : CPLString().Printf("%g", m_adfLUTInputs[i]) ==
2517 0 : CPLString().Printf("%g", m_adfLUTInputs[i + 1])))
2518 : {
2519 : // TODO(schwehr): An explanation of the 18 would be helpful.
2520 : // Can someone distill the issue down to a quick comment?
2521 : // https://trac.osgeo.org/gdal/ticket/6422
2522 0 : osLUT += CPLString().Printf(",%.18g:%g", m_adfLUTInputs[i],
2523 0 : m_adfLUTOutputs[i]);
2524 : }
2525 : else
2526 : {
2527 0 : osLUT += CPLString().Printf(",%g:%g", m_adfLUTInputs[i],
2528 0 : m_adfLUTOutputs[i]);
2529 : }
2530 : }
2531 0 : CPLSetXMLValue(psSrc, "LUT", osLUT);
2532 : }
2533 :
2534 42 : if (m_nColorTableComponent)
2535 : {
2536 7 : CPLSetXMLValue(psSrc, "ColorTableComponent",
2537 : CPLSPrintf("%d", m_nColorTableComponent));
2538 : }
2539 :
2540 42 : return psSrc;
2541 : }
2542 :
2543 : /************************************************************************/
2544 : /* XMLInit() */
2545 : /************************************************************************/
2546 :
2547 : CPLErr
2548 193 : VRTComplexSource::XMLInit(const CPLXMLNode *psSrc, const char *pszVRTPath,
2549 : std::map<CPLString, GDALDataset *> &oMapSharedSources)
2550 :
2551 : {
2552 : /* -------------------------------------------------------------------- */
2553 : /* Do base initialization. */
2554 : /* -------------------------------------------------------------------- */
2555 : {
2556 : const CPLErr eErr =
2557 193 : VRTSimpleSource::XMLInit(psSrc, pszVRTPath, oMapSharedSources);
2558 193 : if (eErr != CE_None)
2559 0 : return eErr;
2560 : }
2561 :
2562 : /* -------------------------------------------------------------------- */
2563 : /* Complex parameters. */
2564 : /* -------------------------------------------------------------------- */
2565 193 : const char *pszScaleOffset = CPLGetXMLValue(psSrc, "ScaleOffset", nullptr);
2566 193 : const char *pszScaleRatio = CPLGetXMLValue(psSrc, "ScaleRatio", nullptr);
2567 193 : if (pszScaleOffset || pszScaleRatio)
2568 : {
2569 18 : m_nProcessingFlags |= PROCESSING_FLAG_SCALING_LINEAR;
2570 18 : if (pszScaleOffset)
2571 18 : m_dfScaleOff = CPLAtof(pszScaleOffset);
2572 18 : if (pszScaleRatio)
2573 15 : m_dfScaleRatio = CPLAtof(pszScaleRatio);
2574 : }
2575 175 : else if (CPLGetXMLValue(psSrc, "Exponent", nullptr) != nullptr &&
2576 176 : CPLGetXMLValue(psSrc, "DstMin", nullptr) != nullptr &&
2577 1 : CPLGetXMLValue(psSrc, "DstMax", nullptr) != nullptr)
2578 : {
2579 1 : m_nProcessingFlags |= PROCESSING_FLAG_SCALING_EXPONENTIAL;
2580 1 : m_dfExponent = CPLAtof(CPLGetXMLValue(psSrc, "Exponent", "1.0"));
2581 :
2582 1 : const char *pszSrcMin = CPLGetXMLValue(psSrc, "SrcMin", nullptr);
2583 1 : const char *pszSrcMax = CPLGetXMLValue(psSrc, "SrcMax", nullptr);
2584 1 : if (pszSrcMin && pszSrcMax)
2585 : {
2586 0 : m_dfSrcMin = CPLAtof(pszSrcMin);
2587 0 : m_dfSrcMax = CPLAtof(pszSrcMax);
2588 0 : m_bSrcMinMaxDefined = true;
2589 : }
2590 :
2591 1 : m_dfDstMin = CPLAtof(CPLGetXMLValue(psSrc, "DstMin", "0.0"));
2592 1 : m_dfDstMax = CPLAtof(CPLGetXMLValue(psSrc, "DstMax", "0.0"));
2593 : }
2594 :
2595 193 : if (const char *pszNODATA = CPLGetXMLValue(psSrc, "NODATA", nullptr))
2596 : {
2597 80 : m_nProcessingFlags |= PROCESSING_FLAG_NODATA;
2598 80 : m_osNoDataValueOri = pszNODATA;
2599 80 : m_dfNoDataValue = CPLAtofM(m_osNoDataValueOri.c_str());
2600 : }
2601 :
2602 193 : const char *pszUseMaskBand = CPLGetXMLValue(psSrc, "UseMaskBand", nullptr);
2603 193 : if (pszUseMaskBand && CPLTestBool(pszUseMaskBand))
2604 : {
2605 22 : m_nProcessingFlags |= PROCESSING_FLAG_USE_MASK_BAND;
2606 : }
2607 :
2608 193 : const char *pszLUT = CPLGetXMLValue(psSrc, "LUT", nullptr);
2609 193 : if (pszLUT)
2610 : {
2611 : const CPLStringList aosValues(
2612 22 : CSLTokenizeString2(pszLUT, ",:", CSLT_ALLOWEMPTYTOKENS));
2613 :
2614 22 : const int nLUTItemCount = aosValues.size() / 2;
2615 : try
2616 : {
2617 22 : m_adfLUTInputs.resize(nLUTItemCount);
2618 22 : m_adfLUTOutputs.resize(nLUTItemCount);
2619 : }
2620 0 : catch (const std::bad_alloc &e)
2621 : {
2622 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
2623 0 : m_adfLUTInputs.clear();
2624 0 : m_adfLUTOutputs.clear();
2625 0 : return CE_Failure;
2626 : }
2627 :
2628 198 : for (int nIndex = 0; nIndex < nLUTItemCount; nIndex++)
2629 : {
2630 176 : m_adfLUTInputs[nIndex] = CPLAtof(aosValues[nIndex * 2]);
2631 176 : m_adfLUTOutputs[nIndex] = CPLAtof(aosValues[nIndex * 2 + 1]);
2632 :
2633 : // Enforce the requirement that the LUT input array is
2634 : // monotonically non-decreasing.
2635 330 : if (nIndex > 0 &&
2636 154 : m_adfLUTInputs[nIndex] < m_adfLUTInputs[nIndex - 1])
2637 : {
2638 0 : m_adfLUTInputs.clear();
2639 0 : m_adfLUTOutputs.clear();
2640 0 : return CE_Failure;
2641 : }
2642 : }
2643 :
2644 22 : m_nProcessingFlags |= PROCESSING_FLAG_LUT;
2645 : }
2646 :
2647 : const char *pszColorTableComponent =
2648 193 : CPLGetXMLValue(psSrc, "ColorTableComponent", nullptr);
2649 193 : if (pszColorTableComponent)
2650 : {
2651 15 : m_nColorTableComponent = atoi(pszColorTableComponent);
2652 15 : m_nProcessingFlags |= PROCESSING_FLAG_COLOR_TABLE_EXPANSION;
2653 : }
2654 :
2655 193 : return CE_None;
2656 : }
2657 :
2658 : /************************************************************************/
2659 : /* LookupValue() */
2660 : /************************************************************************/
2661 :
2662 583258 : double VRTComplexSource::LookupValue(double dfInput)
2663 : {
2664 : // Find the index of the first element in the LUT input array that
2665 : // is not smaller than the input value.
2666 : int i = static_cast<int>(
2667 583258 : std::lower_bound(m_adfLUTInputs.data(),
2668 583258 : m_adfLUTInputs.data() + m_adfLUTInputs.size(),
2669 583258 : dfInput) -
2670 583258 : m_adfLUTInputs.data());
2671 :
2672 583258 : if (i == 0)
2673 129025 : return m_adfLUTOutputs[0];
2674 :
2675 : // If the index is beyond the end of the LUT input array, the input
2676 : // value is larger than all the values in the array.
2677 454233 : if (i == static_cast<int>(m_adfLUTInputs.size()))
2678 4 : return m_adfLUTOutputs.back();
2679 :
2680 454229 : if (m_adfLUTInputs[i] == dfInput)
2681 179251 : return m_adfLUTOutputs[i];
2682 :
2683 : // Otherwise, interpolate.
2684 274978 : return m_adfLUTOutputs[i - 1] +
2685 274978 : (dfInput - m_adfLUTInputs[i - 1]) *
2686 274978 : ((m_adfLUTOutputs[i] - m_adfLUTOutputs[i - 1]) /
2687 274978 : (m_adfLUTInputs[i] - m_adfLUTInputs[i - 1]));
2688 : }
2689 :
2690 : /************************************************************************/
2691 : /* SetLinearScaling() */
2692 : /************************************************************************/
2693 :
2694 78 : void VRTComplexSource::SetLinearScaling(double dfOffset, double dfScale)
2695 : {
2696 78 : m_nProcessingFlags &= ~PROCESSING_FLAG_SCALING_EXPONENTIAL;
2697 78 : m_nProcessingFlags |= PROCESSING_FLAG_SCALING_LINEAR;
2698 78 : m_dfScaleOff = dfOffset;
2699 78 : m_dfScaleRatio = dfScale;
2700 78 : }
2701 :
2702 : /************************************************************************/
2703 : /* SetPowerScaling() */
2704 : /************************************************************************/
2705 :
2706 8 : void VRTComplexSource::SetPowerScaling(double dfExponentIn, double dfSrcMinIn,
2707 : double dfSrcMaxIn, double dfDstMinIn,
2708 : double dfDstMaxIn)
2709 : {
2710 8 : m_nProcessingFlags &= ~PROCESSING_FLAG_SCALING_LINEAR;
2711 8 : m_nProcessingFlags |= PROCESSING_FLAG_SCALING_EXPONENTIAL;
2712 8 : m_dfExponent = dfExponentIn;
2713 8 : m_dfSrcMin = dfSrcMinIn;
2714 8 : m_dfSrcMax = dfSrcMaxIn;
2715 8 : m_dfDstMin = dfDstMinIn;
2716 8 : m_dfDstMax = dfDstMaxIn;
2717 8 : m_bSrcMinMaxDefined = true;
2718 8 : }
2719 :
2720 : /************************************************************************/
2721 : /* SetColorTableComponent() */
2722 : /************************************************************************/
2723 :
2724 154 : void VRTComplexSource::SetColorTableComponent(int nComponent)
2725 : {
2726 154 : m_nProcessingFlags |= PROCESSING_FLAG_COLOR_TABLE_EXPANSION;
2727 154 : m_nColorTableComponent = nComponent;
2728 154 : }
2729 :
2730 : /************************************************************************/
2731 : /* RasterIO() */
2732 : /************************************************************************/
2733 :
2734 15640 : CPLErr VRTComplexSource::RasterIO(GDALDataType eVRTBandDataType, int nXOff,
2735 : int nYOff, int nXSize, int nYSize,
2736 : void *pData, int nBufXSize, int nBufYSize,
2737 : GDALDataType eBufType, GSpacing nPixelSpace,
2738 : GSpacing nLineSpace,
2739 : GDALRasterIOExtraArg *psExtraArgIn,
2740 : WorkingState &oWorkingState)
2741 :
2742 : {
2743 : GDALRasterIOExtraArg sExtraArg;
2744 15640 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
2745 15640 : GDALRasterIOExtraArg *psExtraArg = &sExtraArg;
2746 :
2747 15640 : double dfXOff = nXOff;
2748 15640 : double dfYOff = nYOff;
2749 15640 : double dfXSize = nXSize;
2750 15640 : double dfYSize = nYSize;
2751 15640 : if (psExtraArgIn != nullptr && psExtraArgIn->bFloatingPointWindowValidity)
2752 : {
2753 87 : dfXOff = psExtraArgIn->dfXOff;
2754 87 : dfYOff = psExtraArgIn->dfYOff;
2755 87 : dfXSize = psExtraArgIn->dfXSize;
2756 87 : dfYSize = psExtraArgIn->dfYSize;
2757 : }
2758 :
2759 : // The window we will actually request from the source raster band.
2760 15640 : double dfReqXOff = 0.0;
2761 15640 : double dfReqYOff = 0.0;
2762 15640 : double dfReqXSize = 0.0;
2763 15640 : double dfReqYSize = 0.0;
2764 15640 : int nReqXOff = 0;
2765 15640 : int nReqYOff = 0;
2766 15640 : int nReqXSize = 0;
2767 15640 : int nReqYSize = 0;
2768 :
2769 : // The window we will actual set _within_ the pData buffer.
2770 15640 : int nOutXOff = 0;
2771 15640 : int nOutYOff = 0;
2772 15640 : int nOutXSize = 0;
2773 15640 : int nOutYSize = 0;
2774 :
2775 15640 : bool bError = false;
2776 15640 : if (!GetSrcDstWindow(dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
2777 : &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
2778 : &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
2779 : &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
2780 : {
2781 10946 : return bError ? CE_Failure : CE_None;
2782 : }
2783 : #if DEBUG_VERBOSE
2784 : CPLDebug("VRT",
2785 : "nXOff=%d, nYOff=%d, nXSize=%d, nYSize=%d, nBufXSize=%d, "
2786 : "nBufYSize=%d,\n"
2787 : "dfReqXOff=%g, dfReqYOff=%g, dfReqXSize=%g, dfReqYSize=%g,\n"
2788 : "nReqXOff=%d, nReqYOff=%d, nReqXSize=%d, nReqYSize=%d,\n"
2789 : "nOutXOff=%d, nOutYOff=%d, nOutXSize=%d, nOutYSize=%d",
2790 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, dfReqXOff,
2791 : dfReqYOff, dfReqXSize, dfReqYSize, nReqXOff, nReqYOff, nReqXSize,
2792 : nReqYSize, nOutXOff, nOutYOff, nOutXSize, nOutYSize);
2793 : #endif
2794 :
2795 4694 : auto poSourceBand = GetRasterBand();
2796 4694 : if (!poSourceBand)
2797 0 : return CE_Failure;
2798 :
2799 4694 : if (!m_osResampling.empty())
2800 : {
2801 0 : psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
2802 : }
2803 4694 : else if (psExtraArgIn != nullptr)
2804 : {
2805 4694 : psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg;
2806 : }
2807 4694 : psExtraArg->bFloatingPointWindowValidity = TRUE;
2808 4694 : psExtraArg->dfXOff = dfReqXOff;
2809 4694 : psExtraArg->dfYOff = dfReqYOff;
2810 4694 : psExtraArg->dfXSize = dfReqXSize;
2811 4694 : psExtraArg->dfYSize = dfReqYSize;
2812 :
2813 4694 : GByte *const pabyOut = static_cast<GByte *>(pData) +
2814 4694 : nPixelSpace * nOutXOff +
2815 4694 : static_cast<GPtrDiff_t>(nLineSpace) * nOutYOff;
2816 4694 : if (m_nProcessingFlags == PROCESSING_FLAG_NODATA)
2817 : {
2818 : // Optimization if doing only nodata processing
2819 92 : const auto eSourceType = poSourceBand->GetRasterDataType();
2820 92 : if (eSourceType == GDT_Byte)
2821 : {
2822 46 : if (!GDALIsValueInRange<GByte>(m_dfNoDataValue))
2823 : {
2824 1 : return VRTSimpleSource::RasterIO(
2825 : eVRTBandDataType, nXOff, nYOff, nXSize, nYSize, pData,
2826 : nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace,
2827 1 : psExtraArgIn, oWorkingState);
2828 : }
2829 :
2830 45 : return RasterIOProcessNoData<GByte, GDT_Byte>(
2831 : poSourceBand, eVRTBandDataType, nReqXOff, nReqYOff, nReqXSize,
2832 : nReqYSize, pabyOut, nOutXSize, nOutYSize, eBufType, nPixelSpace,
2833 45 : nLineSpace, psExtraArg, oWorkingState);
2834 : }
2835 46 : else if (eSourceType == GDT_Int16)
2836 : {
2837 19 : if (!GDALIsValueInRange<int16_t>(m_dfNoDataValue))
2838 : {
2839 1 : return VRTSimpleSource::RasterIO(
2840 : eVRTBandDataType, nXOff, nYOff, nXSize, nYSize, pData,
2841 : nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace,
2842 1 : psExtraArgIn, oWorkingState);
2843 : }
2844 :
2845 18 : return RasterIOProcessNoData<int16_t, GDT_Int16>(
2846 : poSourceBand, eVRTBandDataType, nReqXOff, nReqYOff, nReqXSize,
2847 : nReqYSize, pabyOut, nOutXSize, nOutYSize, eBufType, nPixelSpace,
2848 18 : nLineSpace, psExtraArg, oWorkingState);
2849 : }
2850 27 : else if (eSourceType == GDT_UInt16)
2851 : {
2852 25 : if (!GDALIsValueInRange<uint16_t>(m_dfNoDataValue))
2853 : {
2854 1 : return VRTSimpleSource::RasterIO(
2855 : eVRTBandDataType, nXOff, nYOff, nXSize, nYSize, pData,
2856 : nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace,
2857 1 : psExtraArgIn, oWorkingState);
2858 : }
2859 :
2860 24 : return RasterIOProcessNoData<uint16_t, GDT_UInt16>(
2861 : poSourceBand, eVRTBandDataType, nReqXOff, nReqYOff, nReqXSize,
2862 : nReqYSize, pabyOut, nOutXSize, nOutYSize, eBufType, nPixelSpace,
2863 24 : nLineSpace, psExtraArg, oWorkingState);
2864 : }
2865 : }
2866 :
2867 : const bool bIsComplex =
2868 4604 : CPL_TO_BOOL(GDALDataTypeIsComplex(eVRTBandDataType));
2869 : CPLErr eErr;
2870 : // For Int32, float32 isn't sufficiently precise as working data type
2871 4604 : if (eVRTBandDataType == GDT_CInt32 || eVRTBandDataType == GDT_CFloat64 ||
2872 4603 : eVRTBandDataType == GDT_Int32 || eVRTBandDataType == GDT_UInt32 ||
2873 : eVRTBandDataType == GDT_Float64)
2874 : {
2875 2 : eErr = RasterIOInternal<double>(
2876 : poSourceBand, eVRTBandDataType, nReqXOff, nReqYOff, nReqXSize,
2877 : nReqYSize, pabyOut, nOutXSize, nOutYSize, eBufType, nPixelSpace,
2878 : nLineSpace, psExtraArg, bIsComplex ? GDT_CFloat64 : GDT_Float64,
2879 : oWorkingState);
2880 : }
2881 : else
2882 : {
2883 4602 : eErr = RasterIOInternal<float>(
2884 : poSourceBand, eVRTBandDataType, nReqXOff, nReqYOff, nReqXSize,
2885 : nReqYSize, pabyOut, nOutXSize, nOutYSize, eBufType, nPixelSpace,
2886 : nLineSpace, psExtraArg, bIsComplex ? GDT_CFloat32 : GDT_Float32,
2887 : oWorkingState);
2888 : }
2889 :
2890 4604 : return eErr;
2891 : }
2892 :
2893 : /************************************************************************/
2894 : /* hasZeroByte() */
2895 : /************************************************************************/
2896 :
2897 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
2898 4718080 : static inline bool hasZeroByte(uint32_t v)
2899 : {
2900 : // Cf https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
2901 4718080 : return (((v)-0x01010101U) & ~(v)&0x80808080U) != 0;
2902 : }
2903 :
2904 : /************************************************************************/
2905 : /* RasterIOProcessNoData() */
2906 : /************************************************************************/
2907 :
2908 : // This method is an optimization of the generic RasterIOInternal()
2909 : // that deals with a VRTComplexSource with only a NODATA value in it, and
2910 : // no other processing flags.
2911 :
2912 : // nReqXOff, nReqYOff, nReqXSize, nReqYSize are expressed in source band
2913 : // referential.
2914 : template <class SourceDT, GDALDataType eSourceType>
2915 87 : CPLErr VRTComplexSource::RasterIOProcessNoData(
2916 : GDALRasterBand *poSourceBand, GDALDataType eVRTBandDataType, int nReqXOff,
2917 : int nReqYOff, int nReqXSize, int nReqYSize, void *pData, int nOutXSize,
2918 : int nOutYSize, GDALDataType eBufType, GSpacing nPixelSpace,
2919 : GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg,
2920 : WorkingState &oWorkingState)
2921 : {
2922 87 : CPLAssert(m_nProcessingFlags == PROCESSING_FLAG_NODATA);
2923 87 : CPLAssert(GDALIsValueInRange<SourceDT>(m_dfNoDataValue));
2924 :
2925 : /* -------------------------------------------------------------------- */
2926 : /* Read into a temporary buffer. */
2927 : /* -------------------------------------------------------------------- */
2928 : try
2929 : {
2930 : // Cannot overflow since pData should at least have that number of
2931 : // elements
2932 87 : const size_t nPixelCount = static_cast<size_t>(nOutXSize) * nOutYSize;
2933 87 : if (nPixelCount >
2934 87 : static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max()) /
2935 : sizeof(SourceDT))
2936 : {
2937 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2938 : "Too large temporary buffer");
2939 0 : return CE_Failure;
2940 : }
2941 87 : oWorkingState.m_abyWrkBuffer.resize(sizeof(SourceDT) * nPixelCount);
2942 : }
2943 0 : catch (const std::bad_alloc &e)
2944 : {
2945 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
2946 0 : return CE_Failure;
2947 : }
2948 : const auto paSrcData =
2949 87 : reinterpret_cast<const SourceDT *>(oWorkingState.m_abyWrkBuffer.data());
2950 :
2951 87 : const GDALRIOResampleAlg eResampleAlgBack = psExtraArg->eResampleAlg;
2952 87 : if (!m_osResampling.empty())
2953 : {
2954 0 : psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(m_osResampling);
2955 : }
2956 :
2957 87 : const CPLErr eErr = poSourceBand->RasterIO(
2958 : GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
2959 87 : oWorkingState.m_abyWrkBuffer.data(), nOutXSize, nOutYSize, eSourceType,
2960 42 : sizeof(SourceDT), sizeof(SourceDT) * static_cast<GSpacing>(nOutXSize),
2961 : psExtraArg);
2962 87 : if (!m_osResampling.empty())
2963 0 : psExtraArg->eResampleAlg = eResampleAlgBack;
2964 :
2965 87 : if (eErr != CE_None)
2966 : {
2967 0 : return eErr;
2968 : }
2969 :
2970 87 : const auto nNoDataValue = static_cast<SourceDT>(m_dfNoDataValue);
2971 87 : size_t idxBuffer = 0;
2972 130 : if (eSourceType == eBufType &&
2973 43 : !GDALDataTypeIsConversionLossy(eSourceType, eVRTBandDataType))
2974 : {
2975 : // Most optimized case: the output type is the same as the source type,
2976 : // and conversion from the source type to the VRT band data type is
2977 : // not lossy
2978 19682 : for (int iY = 0; iY < nOutYSize; iY++)
2979 : {
2980 19647 : GByte *pDstLocation = static_cast<GByte *>(pData) +
2981 19647 : static_cast<GPtrDiff_t>(nLineSpace) * iY;
2982 :
2983 19647 : int iX = 0;
2984 19623 : if (sizeof(SourceDT) == 1 && nPixelSpace == 1)
2985 : {
2986 : // Optimization to detect more quickly if source pixels are
2987 : // at nodata.
2988 19623 : const GByte byNoDataValue = static_cast<GByte>(nNoDataValue);
2989 19623 : const uint32_t wordNoData =
2990 19623 : (static_cast<uint32_t>(byNoDataValue) << 24) |
2991 19623 : (byNoDataValue << 16) | (byNoDataValue << 8) |
2992 19623 : byNoDataValue;
2993 :
2994 : // Warning: hasZeroByte() assumes WORD_SIZE = 4
2995 19623 : constexpr int WORD_SIZE = 4;
2996 4737700 : for (; iX < nOutXSize - (WORD_SIZE - 1); iX += WORD_SIZE)
2997 : {
2998 : uint32_t v;
2999 : static_assert(sizeof(v) == WORD_SIZE,
3000 : "sizeof(v) == WORD_SIZE");
3001 4718080 : memcpy(&v, paSrcData + idxBuffer, sizeof(v));
3002 : // Cf https://graphics.stanford.edu/~seander/bithacks.html#ValueInWord
3003 4718080 : if (!hasZeroByte(v ^ wordNoData))
3004 : {
3005 : // No bytes are at nodata
3006 4586980 : memcpy(pDstLocation, &v, WORD_SIZE);
3007 4586980 : idxBuffer += WORD_SIZE;
3008 4586980 : pDstLocation += WORD_SIZE;
3009 : }
3010 131102 : else if (v == wordNoData)
3011 : {
3012 : // All bytes are at nodata
3013 131078 : idxBuffer += WORD_SIZE;
3014 131078 : pDstLocation += WORD_SIZE;
3015 : }
3016 : else
3017 : {
3018 : // There are both bytes at nodata and valid bytes
3019 120 : for (int k = 0; k < WORD_SIZE; ++k)
3020 : {
3021 96 : if (paSrcData[idxBuffer] != nNoDataValue)
3022 : {
3023 72 : memcpy(pDstLocation, &paSrcData[idxBuffer],
3024 : sizeof(SourceDT));
3025 : }
3026 96 : idxBuffer++;
3027 96 : pDstLocation += nPixelSpace;
3028 : }
3029 : }
3030 : }
3031 : }
3032 :
3033 23434 : for (; iX < nOutXSize;
3034 3787 : iX++, pDstLocation += nPixelSpace, idxBuffer++)
3035 : {
3036 3787 : if (paSrcData[idxBuffer] != nNoDataValue)
3037 : {
3038 3731 : memcpy(pDstLocation, &paSrcData[idxBuffer],
3039 : sizeof(SourceDT));
3040 : }
3041 : }
3042 : }
3043 : }
3044 52 : else if (!GDALDataTypeIsConversionLossy(eSourceType, eVRTBandDataType))
3045 : {
3046 : // Conversion from the source type to the VRT band data type is
3047 : // not lossy, so we can directly convert from the source type to
3048 : // the the output type
3049 228 : for (int iY = 0; iY < nOutYSize; iY++)
3050 : {
3051 200 : GByte *pDstLocation = static_cast<GByte *>(pData) +
3052 200 : static_cast<GPtrDiff_t>(nLineSpace) * iY;
3053 :
3054 2200 : for (int iX = 0; iX < nOutXSize;
3055 2000 : iX++, pDstLocation += nPixelSpace, idxBuffer++)
3056 : {
3057 2000 : if (paSrcData[idxBuffer] != nNoDataValue)
3058 : {
3059 1018 : GDALCopyWords(&paSrcData[idxBuffer], eSourceType, 0,
3060 : pDstLocation, eBufType, 0, 1);
3061 : }
3062 : }
3063 : }
3064 : }
3065 : else
3066 : {
3067 : GByte abyTemp[2 * sizeof(double)];
3068 168 : for (int iY = 0; iY < nOutYSize; iY++)
3069 : {
3070 144 : GByte *pDstLocation = static_cast<GByte *>(pData) +
3071 144 : static_cast<GPtrDiff_t>(nLineSpace) * iY;
3072 :
3073 864 : for (int iX = 0; iX < nOutXSize;
3074 720 : iX++, pDstLocation += nPixelSpace, idxBuffer++)
3075 : {
3076 720 : if (paSrcData[idxBuffer] != nNoDataValue)
3077 : {
3078 : // Convert first to the VRTRasterBand data type
3079 : // to get its clamping, before outputting to buffer data type
3080 480 : GDALCopyWords(&paSrcData[idxBuffer], eSourceType, 0,
3081 : abyTemp, eVRTBandDataType, 0, 1);
3082 480 : GDALCopyWords(abyTemp, eVRTBandDataType, 0, pDstLocation,
3083 : eBufType, 0, 1);
3084 : }
3085 : }
3086 : }
3087 : }
3088 :
3089 87 : return CE_None;
3090 : }
3091 :
3092 : /************************************************************************/
3093 : /* RasterIOInternal() */
3094 : /************************************************************************/
3095 :
3096 : // nReqXOff, nReqYOff, nReqXSize, nReqYSize are expressed in source band
3097 : // referential.
3098 : template <class WorkingDT>
3099 4618 : CPLErr VRTComplexSource::RasterIOInternal(
3100 : GDALRasterBand *poSourceBand, GDALDataType eVRTBandDataType, int nReqXOff,
3101 : int nReqYOff, int nReqXSize, int nReqYSize, void *pData, int nOutXSize,
3102 : int nOutYSize, GDALDataType eBufType, GSpacing nPixelSpace,
3103 : GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg,
3104 : GDALDataType eWrkDataType, WorkingState &oWorkingState)
3105 : {
3106 4618 : const GDALColorTable *poColorTable = nullptr;
3107 4618 : const bool bIsComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eBufType));
3108 4618 : const int nWordSize = GDALGetDataTypeSizeBytes(eWrkDataType);
3109 4618 : assert(nWordSize != 0);
3110 :
3111 : // If no explicit <NODATA> is set, but UseMaskBand is set, and the band
3112 : // has a nodata value, then use it as if it was set as <NODATA>
3113 4618 : int bNoDataSet = (m_nProcessingFlags & PROCESSING_FLAG_NODATA) != 0;
3114 4618 : double dfNoDataValue = GetAdjustedNoDataValue();
3115 :
3116 4693 : if ((m_nProcessingFlags & PROCESSING_FLAG_USE_MASK_BAND) != 0 &&
3117 75 : poSourceBand->GetMaskFlags() == GMF_NODATA)
3118 : {
3119 0 : dfNoDataValue = poSourceBand->GetNoDataValue(&bNoDataSet);
3120 : }
3121 :
3122 4618 : const bool bNoDataSetIsNan = bNoDataSet && CPLIsNan(dfNoDataValue);
3123 4618 : const bool bNoDataSetAndNotNan =
3124 4622 : bNoDataSet && !CPLIsNan(dfNoDataValue) &&
3125 4 : GDALIsValueInRange<WorkingDT>(dfNoDataValue);
3126 4618 : const auto fWorkingDataTypeNoData = static_cast<WorkingDT>(dfNoDataValue);
3127 :
3128 4618 : const GByte *pabyMask = nullptr;
3129 4618 : const WorkingDT *pafData = nullptr;
3130 4618 : if ((m_nProcessingFlags & PROCESSING_FLAG_SCALING_LINEAR) != 0 &&
3131 4394 : m_dfScaleRatio == 0 && bNoDataSet == FALSE &&
3132 4040 : (m_nProcessingFlags & PROCESSING_FLAG_USE_MASK_BAND) == 0)
3133 : {
3134 : /* ------------------------------------------------------------------ */
3135 : /* Optimization when writing a constant value */
3136 : /* (used by the -addalpha option of gdalbuildvrt) */
3137 : /* ------------------------------------------------------------------ */
3138 : // Already set to NULL when defined.
3139 : // pafData = NULL;
3140 : }
3141 : else
3142 : {
3143 : /* ---------------------------------------------------------------- */
3144 : /* Read into a temporary buffer. */
3145 : /* ---------------------------------------------------------------- */
3146 578 : const size_t nPixelCount = static_cast<size_t>(nOutXSize) * nOutYSize;
3147 : try
3148 : {
3149 : // Cannot overflow since pData should at least have that number of
3150 : // elements
3151 578 : if (nPixelCount >
3152 578 : static_cast<size_t>(std::numeric_limits<ptrdiff_t>::max()) /
3153 578 : static_cast<size_t>(nWordSize))
3154 : {
3155 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
3156 : "Too large temporary buffer");
3157 0 : return CE_Failure;
3158 : }
3159 578 : oWorkingState.m_abyWrkBuffer.resize(nWordSize * nPixelCount);
3160 : }
3161 0 : catch (const std::bad_alloc &e)
3162 : {
3163 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
3164 0 : return CE_Failure;
3165 : }
3166 : pafData = reinterpret_cast<const WorkingDT *>(
3167 578 : oWorkingState.m_abyWrkBuffer.data());
3168 :
3169 578 : const GDALRIOResampleAlg eResampleAlgBack = psExtraArg->eResampleAlg;
3170 578 : if (!m_osResampling.empty())
3171 : {
3172 0 : psExtraArg->eResampleAlg =
3173 0 : GDALRasterIOGetResampleAlg(m_osResampling);
3174 : }
3175 :
3176 578 : const CPLErr eErr = poSourceBand->RasterIO(
3177 : GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
3178 578 : oWorkingState.m_abyWrkBuffer.data(), nOutXSize, nOutYSize,
3179 : eWrkDataType, nWordSize,
3180 578 : nWordSize * static_cast<GSpacing>(nOutXSize), psExtraArg);
3181 578 : if (!m_osResampling.empty())
3182 0 : psExtraArg->eResampleAlg = eResampleAlgBack;
3183 :
3184 578 : if (eErr != CE_None)
3185 : {
3186 0 : return eErr;
3187 : }
3188 :
3189 : // Allocate and read mask band if needed
3190 1728 : if (!bNoDataSet &&
3191 653 : (m_nProcessingFlags & PROCESSING_FLAG_USE_MASK_BAND) != 0 &&
3192 75 : (poSourceBand->GetMaskFlags() != GMF_ALL_VALID ||
3193 31 : poSourceBand->GetColorInterpretation() == GCI_AlphaBand ||
3194 19 : GetMaskBandMainBand() != nullptr))
3195 : {
3196 : try
3197 : {
3198 75 : oWorkingState.m_abyWrkBufferMask.resize(nPixelCount);
3199 : }
3200 0 : catch (const std::exception &)
3201 : {
3202 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
3203 : "Out of memory when allocating mask buffer");
3204 0 : return CE_Failure;
3205 : }
3206 : pabyMask = reinterpret_cast<const GByte *>(
3207 75 : oWorkingState.m_abyWrkBufferMask.data());
3208 31 : auto poMaskBand =
3209 75 : (poSourceBand->GetColorInterpretation() == GCI_AlphaBand ||
3210 63 : GetMaskBandMainBand() != nullptr)
3211 : ? poSourceBand
3212 44 : : poSourceBand->GetMaskBand();
3213 75 : if (poMaskBand->RasterIO(
3214 : GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize,
3215 75 : oWorkingState.m_abyWrkBufferMask.data(), nOutXSize,
3216 : nOutYSize, GDT_Byte, 1, static_cast<GSpacing>(nOutXSize),
3217 75 : psExtraArg) != CE_None)
3218 : {
3219 0 : return CE_Failure;
3220 : }
3221 : }
3222 :
3223 578 : if (m_nColorTableComponent != 0)
3224 : {
3225 64 : poColorTable = poSourceBand->GetColorTable();
3226 64 : if (poColorTable == nullptr)
3227 : {
3228 0 : CPLError(CE_Failure, CPLE_AppDefined,
3229 : "Source band has no color table.");
3230 0 : return CE_Failure;
3231 : }
3232 : }
3233 : }
3234 :
3235 : /* -------------------------------------------------------------------- */
3236 : /* Selectively copy into output buffer with nodata masking, */
3237 : /* and/or scaling. */
3238 : /* -------------------------------------------------------------------- */
3239 4618 : size_t idxBuffer = 0;
3240 73864 : for (int iY = 0; iY < nOutYSize; iY++)
3241 : {
3242 69246 : GByte *pDstLocation = static_cast<GByte *>(pData) +
3243 69246 : static_cast<GPtrDiff_t>(nLineSpace) * iY;
3244 :
3245 11703304 : for (int iX = 0; iX < nOutXSize;
3246 11634102 : iX++, pDstLocation += nPixelSpace, idxBuffer++)
3247 : {
3248 : WorkingDT afResult[2];
3249 11634102 : if (pafData && !bIsComplex)
3250 : {
3251 6025640 : WorkingDT fResult = pafData[idxBuffer];
3252 6025640 : if (bNoDataSetIsNan && CPLIsNan(fResult))
3253 1175040 : continue;
3254 6026420 : if (bNoDataSetAndNotNan &&
3255 802 : ARE_REAL_EQUAL(fResult, fWorkingDataTypeNoData))
3256 114 : continue;
3257 6025500 : if (pabyMask && pabyMask[idxBuffer] == 0)
3258 1174910 : continue;
3259 :
3260 4850600 : if (poColorTable)
3261 : {
3262 : const GDALColorEntry *poEntry =
3263 1925900 : poColorTable->GetColorEntry(static_cast<int>(fResult));
3264 1925900 : if (poEntry)
3265 : {
3266 1925900 : if (m_nColorTableComponent == 1)
3267 683585 : fResult = poEntry->c1;
3268 1242320 : else if (m_nColorTableComponent == 2)
3269 529008 : fResult = poEntry->c2;
3270 713309 : else if (m_nColorTableComponent == 3)
3271 529008 : fResult = poEntry->c3;
3272 184301 : else if (m_nColorTableComponent == 4)
3273 184301 : fResult = poEntry->c4;
3274 : }
3275 : else
3276 : {
3277 : static bool bHasWarned = false;
3278 0 : if (!bHasWarned)
3279 : {
3280 0 : bHasWarned = true;
3281 0 : CPLError(CE_Failure, CPLE_AppDefined,
3282 : "No entry %d.", static_cast<int>(fResult));
3283 : }
3284 0 : continue;
3285 : }
3286 : }
3287 :
3288 4850600 : if ((m_nProcessingFlags & PROCESSING_FLAG_SCALING_LINEAR) != 0)
3289 : {
3290 1407050 : fResult = static_cast<WorkingDT>(fResult * m_dfScaleRatio +
3291 1407050 : m_dfScaleOff);
3292 : }
3293 3443550 : else if ((m_nProcessingFlags &
3294 : PROCESSING_FLAG_SCALING_EXPONENTIAL) != 0)
3295 : {
3296 3543 : if (!m_bSrcMinMaxDefined)
3297 : {
3298 1 : int bSuccessMin = FALSE;
3299 1 : int bSuccessMax = FALSE;
3300 2 : double adfMinMax[2] = {
3301 1 : poSourceBand->GetMinimum(&bSuccessMin),
3302 1 : poSourceBand->GetMaximum(&bSuccessMax)};
3303 2 : if ((bSuccessMin && bSuccessMax) ||
3304 1 : poSourceBand->ComputeRasterMinMax(
3305 : TRUE, adfMinMax) == CE_None)
3306 : {
3307 1 : m_dfSrcMin = adfMinMax[0];
3308 1 : m_dfSrcMax = adfMinMax[1];
3309 1 : m_bSrcMinMaxDefined = true;
3310 : }
3311 : else
3312 : {
3313 0 : CPLError(CE_Failure, CPLE_AppDefined,
3314 : "Cannot determine source min/max value");
3315 0 : return CE_Failure;
3316 : }
3317 : }
3318 :
3319 3543 : double dfPowVal =
3320 3543 : (fResult - m_dfSrcMin) / (m_dfSrcMax - m_dfSrcMin);
3321 3543 : if (dfPowVal < 0.0)
3322 0 : dfPowVal = 0.0;
3323 3543 : else if (dfPowVal > 1.0)
3324 699 : dfPowVal = 1.0;
3325 3543 : fResult =
3326 3543 : static_cast<WorkingDT>((m_dfDstMax - m_dfDstMin) *
3327 3543 : pow(dfPowVal, m_dfExponent) +
3328 3543 : m_dfDstMin);
3329 : }
3330 :
3331 4850600 : if (!m_adfLUTInputs.empty())
3332 583258 : fResult = static_cast<WorkingDT>(LookupValue(fResult));
3333 :
3334 4850600 : if (m_nMaxValue != 0 && fResult > m_nMaxValue)
3335 800 : fResult = static_cast<WorkingDT>(m_nMaxValue);
3336 :
3337 4850600 : afResult[0] = fResult;
3338 4850600 : afResult[1] = 0;
3339 : }
3340 5608452 : else if (pafData && bIsComplex)
3341 : {
3342 211 : afResult[0] = pafData[2 * idxBuffer];
3343 211 : afResult[1] = pafData[2 * idxBuffer + 1];
3344 :
3345 : // Do not use color table.
3346 211 : if ((m_nProcessingFlags & PROCESSING_FLAG_SCALING_LINEAR) != 0)
3347 : {
3348 1 : afResult[0] = static_cast<WorkingDT>(
3349 1 : afResult[0] * m_dfScaleRatio + m_dfScaleOff);
3350 1 : afResult[1] = static_cast<WorkingDT>(
3351 1 : afResult[1] * m_dfScaleRatio + m_dfScaleOff);
3352 : }
3353 :
3354 : /* Do not use LUT */
3355 : }
3356 : else
3357 : {
3358 5608242 : afResult[0] = static_cast<WorkingDT>(m_dfScaleOff);
3359 5608242 : afResult[1] = 0;
3360 :
3361 5608242 : if (!m_adfLUTInputs.empty())
3362 0 : afResult[0] =
3363 0 : static_cast<WorkingDT>(LookupValue(afResult[0]));
3364 :
3365 5608242 : if (m_nMaxValue != 0 && afResult[0] > m_nMaxValue)
3366 0 : afResult[0] = static_cast<WorkingDT>(m_nMaxValue);
3367 : }
3368 :
3369 10459002 : if (eBufType == GDT_Byte && eVRTBandDataType == GDT_Byte)
3370 : {
3371 4283120 : *pDstLocation = static_cast<GByte>(std::min(
3372 8566240 : 255.0f,
3373 4283120 : std::max(0.0f, static_cast<float>(afResult[0]) + 0.5f)));
3374 : }
3375 6175932 : else if (eBufType == eVRTBandDataType)
3376 : {
3377 1303832 : GDALCopyWords(afResult, eWrkDataType, 0, pDstLocation, eBufType,
3378 : 0, 1);
3379 : }
3380 : else
3381 : {
3382 : GByte abyTemp[2 * sizeof(double)];
3383 : // Convert first to the VRTRasterBand data type
3384 : // to get its clamping, before outputting to buffer data type
3385 4872100 : GDALCopyWords(afResult, eWrkDataType, 0, abyTemp,
3386 : eVRTBandDataType, 0, 1);
3387 4872100 : GDALCopyWords(abyTemp, eVRTBandDataType, 0, pDstLocation,
3388 : eBufType, 0, 1);
3389 : }
3390 : }
3391 : }
3392 :
3393 4618 : return CE_None;
3394 : }
3395 :
3396 : // Explicitly instantiate template method, as it is used in another file.
3397 : template CPLErr VRTComplexSource::RasterIOInternal<float>(
3398 : GDALRasterBand *poSourceBand, GDALDataType eVRTBandDataType, int nReqXOff,
3399 : int nReqYOff, int nReqXSize, int nReqYSize, void *pData, int nOutXSize,
3400 : int nOutYSize, GDALDataType eBufType, GSpacing nPixelSpace,
3401 : GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg,
3402 : GDALDataType eWrkDataType, WorkingState &oWorkingState);
3403 :
3404 : /************************************************************************/
3405 : /* AreValuesUnchanged() */
3406 : /************************************************************************/
3407 :
3408 9 : bool VRTComplexSource::AreValuesUnchanged() const
3409 : {
3410 10 : return m_dfScaleOff == 0.0 && m_dfScaleRatio == 1.0 &&
3411 24 : m_adfLUTInputs.empty() && m_nColorTableComponent == 0 &&
3412 14 : (m_nProcessingFlags & PROCESSING_FLAG_SCALING_EXPONENTIAL) == 0;
3413 : }
3414 :
3415 : /************************************************************************/
3416 : /* GetMinimum() */
3417 : /************************************************************************/
3418 :
3419 2 : double VRTComplexSource::GetMinimum(int nXSize, int nYSize, int *pbSuccess)
3420 : {
3421 2 : if (AreValuesUnchanged())
3422 : {
3423 1 : return VRTSimpleSource::GetMinimum(nXSize, nYSize, pbSuccess);
3424 : }
3425 :
3426 1 : *pbSuccess = FALSE;
3427 1 : return 0;
3428 : }
3429 :
3430 : /************************************************************************/
3431 : /* GetMaximum() */
3432 : /************************************************************************/
3433 :
3434 2 : double VRTComplexSource::GetMaximum(int nXSize, int nYSize, int *pbSuccess)
3435 : {
3436 2 : if (AreValuesUnchanged())
3437 : {
3438 1 : return VRTSimpleSource::GetMaximum(nXSize, nYSize, pbSuccess);
3439 : }
3440 :
3441 1 : *pbSuccess = FALSE;
3442 1 : return 0;
3443 : }
3444 :
3445 : /************************************************************************/
3446 : /* GetHistogram() */
3447 : /************************************************************************/
3448 :
3449 0 : CPLErr VRTComplexSource::GetHistogram(int nXSize, int nYSize, double dfMin,
3450 : double dfMax, int nBuckets,
3451 : GUIntBig *panHistogram,
3452 : int bIncludeOutOfRange, int bApproxOK,
3453 : GDALProgressFunc pfnProgress,
3454 : void *pProgressData)
3455 : {
3456 0 : if (AreValuesUnchanged())
3457 : {
3458 0 : return VRTSimpleSource::GetHistogram(
3459 : nXSize, nYSize, dfMin, dfMax, nBuckets, panHistogram,
3460 0 : bIncludeOutOfRange, bApproxOK, pfnProgress, pProgressData);
3461 : }
3462 :
3463 0 : return CE_Failure;
3464 : }
3465 :
3466 : /************************************************************************/
3467 : /* ==================================================================== */
3468 : /* VRTFuncSource */
3469 : /* ==================================================================== */
3470 : /************************************************************************/
3471 :
3472 : /************************************************************************/
3473 : /* VRTFuncSource() */
3474 : /************************************************************************/
3475 :
3476 0 : VRTFuncSource::VRTFuncSource()
3477 : : pfnReadFunc(nullptr), pCBData(nullptr), eType(GDT_Byte),
3478 0 : fNoDataValue(static_cast<float>(VRT_NODATA_UNSET))
3479 : {
3480 0 : }
3481 :
3482 : /************************************************************************/
3483 : /* ~VRTFuncSource() */
3484 : /************************************************************************/
3485 :
3486 0 : VRTFuncSource::~VRTFuncSource()
3487 : {
3488 0 : }
3489 :
3490 : /************************************************************************/
3491 : /* SerializeToXML() */
3492 : /************************************************************************/
3493 :
3494 0 : CPLXMLNode *VRTFuncSource::SerializeToXML(CPL_UNUSED const char *pszVRTPath)
3495 : {
3496 0 : return nullptr;
3497 : }
3498 :
3499 : /************************************************************************/
3500 : /* RasterIO() */
3501 : /************************************************************************/
3502 :
3503 0 : CPLErr VRTFuncSource::RasterIO(GDALDataType /*eVRTBandDataType*/, int nXOff,
3504 : int nYOff, int nXSize, int nYSize, void *pData,
3505 : int nBufXSize, int nBufYSize,
3506 : GDALDataType eBufType, GSpacing nPixelSpace,
3507 : GSpacing nLineSpace,
3508 : GDALRasterIOExtraArg * /* psExtraArg */,
3509 : WorkingState & /* oWorkingState */)
3510 : {
3511 0 : if (nPixelSpace * 8 == GDALGetDataTypeSize(eBufType) &&
3512 0 : nLineSpace == nPixelSpace * nXSize && nBufXSize == nXSize &&
3513 0 : nBufYSize == nYSize && eBufType == eType)
3514 : {
3515 0 : return pfnReadFunc(pCBData, nXOff, nYOff, nXSize, nYSize, pData);
3516 : }
3517 : else
3518 : {
3519 0 : CPLError(CE_Failure, CPLE_AppDefined,
3520 : "VRTFuncSource::RasterIO() - Irregular request.");
3521 0 : CPLDebug("VRT", "Irregular request: %d,%d %d,%d, %d,%d %d,%d %d,%d",
3522 : static_cast<int>(nPixelSpace) * 8,
3523 : GDALGetDataTypeSize(eBufType), static_cast<int>(nLineSpace),
3524 : static_cast<int>(nPixelSpace) * nXSize, nBufXSize, nXSize,
3525 : nBufYSize, nYSize, static_cast<int>(eBufType),
3526 0 : static_cast<int>(eType));
3527 :
3528 0 : return CE_Failure;
3529 : }
3530 : }
3531 :
3532 : /************************************************************************/
3533 : /* GetMinimum() */
3534 : /************************************************************************/
3535 :
3536 0 : double VRTFuncSource::GetMinimum(int /* nXSize */, int /* nYSize */,
3537 : int *pbSuccess)
3538 : {
3539 0 : *pbSuccess = FALSE;
3540 0 : return 0;
3541 : }
3542 :
3543 : /************************************************************************/
3544 : /* GetMaximum() */
3545 : /************************************************************************/
3546 :
3547 0 : double VRTFuncSource::GetMaximum(int /* nXSize */, int /* nYSize */,
3548 : int *pbSuccess)
3549 : {
3550 0 : *pbSuccess = FALSE;
3551 0 : return 0;
3552 : }
3553 :
3554 : /************************************************************************/
3555 : /* GetHistogram() */
3556 : /************************************************************************/
3557 :
3558 0 : CPLErr VRTFuncSource::GetHistogram(
3559 : int /* nXSize */, int /* nYSize */, double /* dfMin */, double /* dfMax */,
3560 : int /* nBuckets */, GUIntBig * /* panHistogram */,
3561 : int /* bIncludeOutOfRange */, int /* bApproxOK */,
3562 : GDALProgressFunc /* pfnProgress */, void * /* pProgressData */)
3563 : {
3564 0 : return CE_Failure;
3565 : }
3566 :
3567 : /************************************************************************/
3568 : /* VRTParseCoreSources() */
3569 : /************************************************************************/
3570 :
3571 : VRTSource *
3572 1459 : VRTParseCoreSources(const CPLXMLNode *psChild, const char *pszVRTPath,
3573 : std::map<CPLString, GDALDataset *> &oMapSharedSources)
3574 :
3575 : {
3576 1459 : VRTSource *poSource = nullptr;
3577 :
3578 2911 : if (EQUAL(psChild->pszValue, "AveragedSource") ||
3579 1452 : (EQUAL(psChild->pszValue, "SimpleSource") &&
3580 1264 : STARTS_WITH_CI(CPLGetXMLValue(psChild, "Resampling", "Nearest"),
3581 : "Aver")))
3582 : {
3583 16 : poSource = new VRTAveragedSource();
3584 : }
3585 1443 : else if (EQUAL(psChild->pszValue, "SimpleSource"))
3586 : {
3587 1255 : poSource = new VRTSimpleSource();
3588 : }
3589 188 : else if (EQUAL(psChild->pszValue, "ComplexSource"))
3590 : {
3591 180 : poSource = new VRTComplexSource();
3592 : }
3593 8 : else if (EQUAL(psChild->pszValue, "NoDataFromMaskSource"))
3594 : {
3595 8 : poSource = new VRTNoDataFromMaskSource();
3596 : }
3597 : else
3598 : {
3599 0 : CPLError(CE_Failure, CPLE_AppDefined,
3600 : "VRTParseCoreSources() - Unknown source : %s",
3601 0 : psChild->pszValue);
3602 0 : return nullptr;
3603 : }
3604 :
3605 1459 : if (poSource->XMLInit(psChild, pszVRTPath, oMapSharedSources) == CE_None)
3606 1456 : return poSource;
3607 :
3608 3 : delete poSource;
3609 3 : return nullptr;
3610 : }
3611 :
3612 : /*! @endcond */
|