Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: High Performance Image Reprojector
4 : * Purpose: Implementation of the GDALWarpOperation class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2007-2012, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "gdalwarper.h"
16 :
17 : #include <cctype>
18 : #include <climits>
19 : #include <cmath>
20 : #include <cstddef>
21 : #include <cstdlib>
22 : #include <cstring>
23 :
24 : #include <algorithm>
25 : #include <limits>
26 : #include <map>
27 : #include <memory>
28 : #include <mutex>
29 :
30 : #include "cpl_config.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_error.h"
33 : #include "cpl_error_internal.h"
34 : #include "cpl_mask.h"
35 : #include "cpl_multiproc.h"
36 : #include "cpl_string.h"
37 : #include "cpl_vsi.h"
38 : #include "gdal.h"
39 : #include "gdal_priv.h"
40 : #include "gdal_alg_priv.h"
41 : #include "ogr_api.h"
42 : #include "ogr_core.h"
43 :
44 : struct _GDALWarpChunk
45 : {
46 : int dx, dy, dsx, dsy;
47 : int sx, sy, ssx, ssy;
48 : double sExtraSx, sExtraSy;
49 : };
50 :
51 : struct GDALWarpPrivateData
52 : {
53 : int nStepCount = 0;
54 : std::vector<int> abSuccess{};
55 : std::vector<double> adfDstX{};
56 : std::vector<double> adfDstY{};
57 : };
58 :
59 : static std::mutex gMutex{};
60 : static std::map<GDALWarpOperation *, std::unique_ptr<GDALWarpPrivateData>>
61 : gMapPrivate{};
62 :
63 : static GDALWarpPrivateData *
64 1260 : GetWarpPrivateData(GDALWarpOperation *poWarpOperation)
65 : {
66 2520 : std::lock_guard<std::mutex> oLock(gMutex);
67 1260 : auto oItem = gMapPrivate.find(poWarpOperation);
68 1260 : if (oItem != gMapPrivate.end())
69 : {
70 948 : return oItem->second.get();
71 : }
72 : else
73 : {
74 312 : gMapPrivate[poWarpOperation] =
75 624 : std::unique_ptr<GDALWarpPrivateData>(new GDALWarpPrivateData());
76 312 : return gMapPrivate[poWarpOperation].get();
77 : }
78 : }
79 :
80 : /************************************************************************/
81 : /* ==================================================================== */
82 : /* GDALWarpOperation */
83 : /* ==================================================================== */
84 : /************************************************************************/
85 :
86 : /**
87 : * \class GDALWarpOperation "gdalwarper.h"
88 : *
89 : * High level image warping class.
90 :
91 : <h2>Warper Design</h2>
92 :
93 : The overall GDAL high performance image warper is split into a few components.
94 :
95 : - The transformation between input and output file coordinates is handled
96 : via GDALTransformerFunc() implementations such as the one returned by
97 : GDALCreateGenImgProjTransformer(). The transformers are ultimately responsible
98 : for translating pixel/line locations on the destination image to pixel/line
99 : locations on the source image.
100 :
101 : - In order to handle images too large to hold in RAM, the warper needs to
102 : segment large images. This is the responsibility of the GDALWarpOperation
103 : class. The GDALWarpOperation::ChunkAndWarpImage() invokes
104 : GDALWarpOperation::WarpRegion() on chunks of output and input image that
105 : are small enough to hold in the amount of memory allowed by the application.
106 : This process is described in greater detail in the <b>Image Chunking</b>
107 : section.
108 :
109 : - The GDALWarpOperation::WarpRegion() function creates and loads an output
110 : image buffer, and then calls WarpRegionToBuffer().
111 :
112 : - GDALWarpOperation::WarpRegionToBuffer() is responsible for loading the
113 : source imagery corresponding to a particular output region, and generating
114 : masks and density masks from the source and destination imagery using
115 : the generator functions found in the GDALWarpOptions structure. Binds this
116 : all into an instance of GDALWarpKernel on which the
117 : GDALWarpKernel::PerformWarp() method is called.
118 :
119 : - GDALWarpKernel does the actual image warping, but is given an input image
120 : and an output image to operate on. The GDALWarpKernel does no IO, and in
121 : fact knows nothing about GDAL. It invokes the transformation function to
122 : get sample locations, builds output values based on the resampling algorithm
123 : in use. It also takes any validity and density masks into account during
124 : this operation.
125 :
126 : <h3>Chunk Size Selection</h3>
127 :
128 : The GDALWarpOptions ChunkAndWarpImage() method is responsible for invoking
129 : the WarpRegion() method on appropriate sized output chunks such that the
130 : memory required for the output image buffer, input image buffer and any
131 : required density and validity buffers is less than or equal to the application
132 : defined maximum memory available for use.
133 :
134 : It checks the memory required by walking the edges of the output region,
135 : transforming the locations back into source pixel/line coordinates and
136 : establishing a bounding rectangle of source imagery that would be required
137 : for the output area. This is actually accomplished by the private
138 : GDALWarpOperation::ComputeSourceWindow() method.
139 :
140 : Then memory requirements are used by totaling the memory required for all
141 : output bands, input bands, validity masks and density masks. If this is
142 : greater than the GDALWarpOptions::dfWarpMemoryLimit then the destination
143 : region is divided in two (splitting the longest dimension), and
144 : ChunkAndWarpImage() recursively invoked on each destination subregion.
145 :
146 : <h3>Validity and Density Masks Generation</h3>
147 :
148 : Fill in ways in which the validity and density masks may be generated here.
149 : Note that detailed semantics of the masks should be found in
150 : GDALWarpKernel.
151 : */
152 :
153 : /************************************************************************/
154 : /* GDALWarpOperation() */
155 : /************************************************************************/
156 :
157 : GDALWarpOperation::GDALWarpOperation() = default;
158 :
159 : /************************************************************************/
160 : /* ~GDALWarpOperation() */
161 : /************************************************************************/
162 :
163 1831 : GDALWarpOperation::~GDALWarpOperation()
164 :
165 : {
166 : {
167 3662 : std::lock_guard<std::mutex> oLock(gMutex);
168 1831 : auto oItem = gMapPrivate.find(this);
169 1831 : if (oItem != gMapPrivate.end())
170 : {
171 312 : gMapPrivate.erase(oItem);
172 : }
173 : }
174 :
175 1831 : WipeOptions();
176 :
177 1831 : if (hIOMutex != nullptr)
178 : {
179 6 : CPLDestroyMutex(hIOMutex);
180 6 : CPLDestroyMutex(hWarpMutex);
181 : }
182 :
183 1831 : WipeChunkList();
184 1831 : if (psThreadData)
185 1827 : GWKThreadsEnd(psThreadData);
186 1831 : }
187 :
188 : /************************************************************************/
189 : /* GetOptions() */
190 : /************************************************************************/
191 :
192 : /** Return warp options */
193 4372 : const GDALWarpOptions *GDALWarpOperation::GetOptions()
194 :
195 : {
196 4372 : return psOptions;
197 : }
198 :
199 : /************************************************************************/
200 : /* WipeOptions() */
201 : /************************************************************************/
202 :
203 1835 : void GDALWarpOperation::WipeOptions()
204 :
205 : {
206 1835 : if (psOptions != nullptr)
207 : {
208 1831 : GDALDestroyWarpOptions(psOptions);
209 1831 : psOptions = nullptr;
210 : }
211 1835 : }
212 :
213 : /************************************************************************/
214 : /* ValidateOptions() */
215 : /************************************************************************/
216 :
217 1831 : int GDALWarpOperation::ValidateOptions()
218 :
219 : {
220 1831 : if (psOptions == nullptr)
221 : {
222 0 : CPLError(CE_Failure, CPLE_IllegalArg,
223 : "GDALWarpOptions.Validate(): "
224 : "no options currently initialized.");
225 0 : return FALSE;
226 : }
227 :
228 1831 : if (psOptions->dfWarpMemoryLimit < 100000.0)
229 : {
230 0 : CPLError(CE_Failure, CPLE_IllegalArg,
231 : "GDALWarpOptions.Validate(): "
232 : "dfWarpMemoryLimit=%g is unreasonably small.",
233 0 : psOptions->dfWarpMemoryLimit);
234 0 : return FALSE;
235 : }
236 :
237 1831 : if (psOptions->eResampleAlg != GRA_NearestNeighbour &&
238 1048 : psOptions->eResampleAlg != GRA_Bilinear &&
239 624 : psOptions->eResampleAlg != GRA_Cubic &&
240 329 : psOptions->eResampleAlg != GRA_CubicSpline &&
241 265 : psOptions->eResampleAlg != GRA_Lanczos &&
242 207 : psOptions->eResampleAlg != GRA_Average &&
243 109 : psOptions->eResampleAlg != GRA_RMS &&
244 99 : psOptions->eResampleAlg != GRA_Mode &&
245 53 : psOptions->eResampleAlg != GRA_Max &&
246 47 : psOptions->eResampleAlg != GRA_Min &&
247 42 : psOptions->eResampleAlg != GRA_Med &&
248 36 : psOptions->eResampleAlg != GRA_Q1 &&
249 26 : psOptions->eResampleAlg != GRA_Q3 && psOptions->eResampleAlg != GRA_Sum)
250 : {
251 0 : CPLError(CE_Failure, CPLE_IllegalArg,
252 : "GDALWarpOptions.Validate(): "
253 : "eResampleArg=%d is not a supported value.",
254 0 : psOptions->eResampleAlg);
255 0 : return FALSE;
256 : }
257 :
258 1831 : if (static_cast<int>(psOptions->eWorkingDataType) < 1 ||
259 1831 : static_cast<int>(psOptions->eWorkingDataType) >= GDT_TypeCount)
260 : {
261 0 : CPLError(CE_Failure, CPLE_IllegalArg,
262 : "GDALWarpOptions.Validate(): "
263 : "eWorkingDataType=%d is not a supported value.",
264 0 : psOptions->eWorkingDataType);
265 0 : return FALSE;
266 : }
267 :
268 2084 : if (GDALDataTypeIsComplex(psOptions->eWorkingDataType) != 0 &&
269 253 : (psOptions->eResampleAlg == GRA_Max ||
270 253 : psOptions->eResampleAlg == GRA_Min ||
271 253 : psOptions->eResampleAlg == GRA_Med ||
272 253 : psOptions->eResampleAlg == GRA_Q1 ||
273 253 : psOptions->eResampleAlg == GRA_Q3))
274 : {
275 :
276 0 : CPLError(CE_Failure, CPLE_NotSupported,
277 : "GDALWarpOptions.Validate(): "
278 : "min/max/qnt not supported for complex valued data.");
279 0 : return FALSE;
280 : }
281 :
282 1831 : if (psOptions->hSrcDS == nullptr)
283 : {
284 0 : CPLError(CE_Failure, CPLE_IllegalArg,
285 : "GDALWarpOptions.Validate(): "
286 : "hSrcDS is not set.");
287 0 : return FALSE;
288 : }
289 :
290 1831 : if (psOptions->nBandCount == 0)
291 : {
292 0 : CPLError(CE_Failure, CPLE_IllegalArg,
293 : "GDALWarpOptions.Validate(): "
294 : "nBandCount=0, no bands configured!");
295 0 : return FALSE;
296 : }
297 :
298 1831 : if (psOptions->panSrcBands == nullptr)
299 : {
300 0 : CPLError(CE_Failure, CPLE_IllegalArg,
301 : "GDALWarpOptions.Validate(): "
302 : "panSrcBands is NULL.");
303 0 : return FALSE;
304 : }
305 :
306 1831 : if (psOptions->hDstDS != nullptr && psOptions->panDstBands == nullptr)
307 : {
308 0 : CPLError(CE_Failure, CPLE_IllegalArg,
309 : "GDALWarpOptions.Validate(): "
310 : "panDstBands is NULL.");
311 0 : return FALSE;
312 : }
313 :
314 4509 : for (int iBand = 0; iBand < psOptions->nBandCount; iBand++)
315 : {
316 5356 : if (psOptions->panSrcBands[iBand] < 1 ||
317 2678 : psOptions->panSrcBands[iBand] >
318 2678 : GDALGetRasterCount(psOptions->hSrcDS))
319 : {
320 0 : CPLError(CE_Failure, CPLE_IllegalArg,
321 : "panSrcBands[%d] = %d ... out of range for dataset.",
322 0 : iBand, psOptions->panSrcBands[iBand]);
323 0 : return FALSE;
324 : }
325 5343 : if (psOptions->hDstDS != nullptr &&
326 2665 : (psOptions->panDstBands[iBand] < 1 ||
327 2665 : psOptions->panDstBands[iBand] >
328 2665 : GDALGetRasterCount(psOptions->hDstDS)))
329 : {
330 0 : CPLError(CE_Failure, CPLE_IllegalArg,
331 : "panDstBands[%d] = %d ... out of range for dataset.",
332 0 : iBand, psOptions->panDstBands[iBand]);
333 0 : return FALSE;
334 : }
335 :
336 5343 : if (psOptions->hDstDS != nullptr &&
337 2665 : GDALGetRasterAccess(GDALGetRasterBand(
338 2665 : psOptions->hDstDS, psOptions->panDstBands[iBand])) ==
339 : GA_ReadOnly)
340 : {
341 0 : CPLError(CE_Failure, CPLE_IllegalArg,
342 : "Destination band %d appears to be read-only.",
343 0 : psOptions->panDstBands[iBand]);
344 0 : return FALSE;
345 : }
346 : }
347 :
348 1831 : if (psOptions->nBandCount == 0)
349 : {
350 0 : CPLError(CE_Failure, CPLE_IllegalArg,
351 : "GDALWarpOptions.Validate(): "
352 : "nBandCount=0, no bands configured!");
353 0 : return FALSE;
354 : }
355 :
356 1831 : if (psOptions->pfnProgress == nullptr)
357 : {
358 0 : CPLError(CE_Failure, CPLE_IllegalArg,
359 : "GDALWarpOptions.Validate(): "
360 : "pfnProgress is NULL.");
361 0 : return FALSE;
362 : }
363 :
364 1831 : if (psOptions->pfnTransformer == nullptr)
365 : {
366 0 : CPLError(CE_Failure, CPLE_IllegalArg,
367 : "GDALWarpOptions.Validate(): "
368 : "pfnTransformer is NULL.");
369 0 : return FALSE;
370 : }
371 :
372 : {
373 3662 : CPLStringList aosWO(CSLDuplicate(psOptions->papszWarpOptions));
374 : // A few internal/undocumented options
375 1831 : aosWO.SetNameValue("EXTRA_ELTS", nullptr);
376 1831 : aosWO.SetNameValue("USE_GENERAL_CASE", nullptr);
377 1831 : aosWO.SetNameValue("ERROR_THRESHOLD", nullptr);
378 1831 : aosWO.SetNameValue("ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", nullptr);
379 1831 : aosWO.SetNameValue("MULT_FACTOR_VERTICAL_SHIFT_PIPELINE", nullptr);
380 1831 : aosWO.SetNameValue("SRC_FILL_RATIO_HEURISTICS", nullptr);
381 1831 : GDALValidateOptions(nullptr, GDALWarpGetOptionList(), aosWO.List(),
382 : "option", "warp options");
383 : }
384 :
385 : const char *pszSampleSteps =
386 1831 : CSLFetchNameValue(psOptions->papszWarpOptions, "SAMPLE_STEPS");
387 1831 : if (pszSampleSteps)
388 : {
389 8 : if (!EQUAL(pszSampleSteps, "ALL") && atoi(pszSampleSteps) < 2)
390 : {
391 0 : CPLError(CE_Failure, CPLE_IllegalArg,
392 : "GDALWarpOptions.Validate(): "
393 : "SAMPLE_STEPS warp option has illegal value.");
394 0 : return FALSE;
395 : }
396 : }
397 :
398 1831 : if (psOptions->nSrcAlphaBand > 0)
399 : {
400 214 : if (psOptions->hSrcDS == nullptr ||
401 107 : psOptions->nSrcAlphaBand > GDALGetRasterCount(psOptions->hSrcDS))
402 : {
403 0 : CPLError(CE_Failure, CPLE_IllegalArg,
404 : "nSrcAlphaBand = %d ... out of range for dataset.",
405 0 : psOptions->nSrcAlphaBand);
406 0 : return FALSE;
407 : }
408 : }
409 :
410 1831 : if (psOptions->nDstAlphaBand > 0)
411 : {
412 808 : if (psOptions->hDstDS == nullptr ||
413 404 : psOptions->nDstAlphaBand > GDALGetRasterCount(psOptions->hDstDS))
414 : {
415 0 : CPLError(CE_Failure, CPLE_IllegalArg,
416 : "nDstAlphaBand = %d ... out of range for dataset.",
417 0 : psOptions->nDstAlphaBand);
418 0 : return FALSE;
419 : }
420 : }
421 :
422 1831 : if (psOptions->nSrcAlphaBand > 0 &&
423 107 : psOptions->pfnSrcDensityMaskFunc != nullptr)
424 : {
425 0 : CPLError(CE_Failure, CPLE_IllegalArg,
426 : "GDALWarpOptions.Validate(): "
427 : "pfnSrcDensityMaskFunc provided as well as a SrcAlphaBand.");
428 0 : return FALSE;
429 : }
430 :
431 1831 : if (psOptions->nDstAlphaBand > 0 &&
432 404 : psOptions->pfnDstDensityMaskFunc != nullptr)
433 : {
434 0 : CPLError(CE_Failure, CPLE_IllegalArg,
435 : "GDALWarpOptions.Validate(): "
436 : "pfnDstDensityMaskFunc provided as well as a DstAlphaBand.");
437 0 : return FALSE;
438 : }
439 :
440 : GDALRasterBandH hSrcBand =
441 1831 : GDALGetRasterBand(psOptions->hSrcDS, psOptions->panSrcBands[0]);
442 1833 : if (GDALGetMaskFlags(hSrcBand) == GMF_PER_DATASET &&
443 2 : psOptions->padfSrcNoDataReal != nullptr)
444 : {
445 1 : CPLError(
446 : CE_Warning, CPLE_AppDefined,
447 : "Source dataset has both a per-dataset mask band and the warper "
448 : "has been also configured with a source nodata value. Only taking "
449 : "into account the latter (i.e. ignoring the per-dataset mask "
450 : "band)");
451 : }
452 :
453 3662 : const bool bErrorOutIfEmptySourceWindow = CPLFetchBool(
454 1831 : psOptions->papszWarpOptions, "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", true);
455 2219 : if (!bErrorOutIfEmptySourceWindow &&
456 388 : CSLFetchNameValue(psOptions->papszWarpOptions, "INIT_DEST") == nullptr)
457 : {
458 0 : CPLError(CE_Failure, CPLE_IllegalArg,
459 : "GDALWarpOptions.Validate(): "
460 : "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW=FALSE can only be used "
461 : "if INIT_DEST is set");
462 0 : return FALSE;
463 : }
464 :
465 1831 : return TRUE;
466 : }
467 :
468 : /************************************************************************/
469 : /* SetAlphaMax() */
470 : /************************************************************************/
471 :
472 510 : static void SetAlphaMax(GDALWarpOptions *psOptions, GDALRasterBandH hBand,
473 : const char *pszKey)
474 : {
475 : const char *pszNBits =
476 510 : GDALGetMetadataItem(hBand, GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE);
477 510 : const char *pszAlphaMax = nullptr;
478 510 : if (pszNBits)
479 : {
480 4 : pszAlphaMax = CPLSPrintf("%u", (1U << atoi(pszNBits)) - 1U);
481 : }
482 506 : else if (GDALGetRasterDataType(hBand) == GDT_Int16)
483 : {
484 20 : pszAlphaMax = "32767";
485 : }
486 486 : else if (GDALGetRasterDataType(hBand) == GDT_UInt16)
487 : {
488 22 : pszAlphaMax = "65535";
489 : }
490 :
491 510 : if (pszAlphaMax != nullptr)
492 46 : psOptions->papszWarpOptions =
493 46 : CSLSetNameValue(psOptions->papszWarpOptions, pszKey, pszAlphaMax);
494 : else
495 464 : CPLDebug("WARP", "SetAlphaMax: AlphaMax not set.");
496 510 : }
497 :
498 : /************************************************************************/
499 : /* SetTieStrategy() */
500 : /************************************************************************/
501 :
502 1831 : static void SetTieStrategy(GDALWarpOptions *psOptions, CPLErr *peErr)
503 : {
504 1831 : if (const char *pszTieStrategy =
505 1831 : CSLFetchNameValue(psOptions->papszWarpOptions, "MODE_TIES"))
506 : {
507 14 : if (EQUAL(pszTieStrategy, "FIRST"))
508 : {
509 4 : psOptions->eTieStrategy = GWKTS_First;
510 : }
511 10 : else if (EQUAL(pszTieStrategy, "MIN"))
512 : {
513 4 : psOptions->eTieStrategy = GWKTS_Min;
514 : }
515 6 : else if (EQUAL(pszTieStrategy, "MAX"))
516 : {
517 4 : psOptions->eTieStrategy = GWKTS_Max;
518 : }
519 : else
520 : {
521 2 : CPLError(CE_Failure, CPLE_IllegalArg,
522 : "Unknown value of MODE_TIES: %s", pszTieStrategy);
523 2 : *peErr = CE_Failure;
524 : }
525 : }
526 1831 : }
527 :
528 : /************************************************************************/
529 : /* Initialize() */
530 : /************************************************************************/
531 :
532 : /**
533 : * \fn CPLErr GDALWarpOperation::Initialize( const GDALWarpOptions * );
534 : *
535 : * This method initializes the GDALWarpOperation's concept of the warp
536 : * options in effect. It creates an internal copy of the GDALWarpOptions
537 : * structure and defaults a variety of additional fields in the internal
538 : * copy if not set in the provided warp options.
539 : *
540 : * Defaulting operations include:
541 : * - If the nBandCount is 0, it will be set to the number of bands in the
542 : * source image (which must match the output image) and the panSrcBands
543 : * and panDstBands will be populated.
544 : *
545 : * @param psNewOptions input set of warp options. These are copied and may
546 : * be destroyed after this call by the application.
547 : * @param pfnTransformer Transformer function that this GDALWarpOperation must use
548 : * and own, or NULL. When pfnTransformer is not NULL, this implies that
549 : * psNewOptions->pfnTransformer is NULL
550 : * @param psOwnedTransformerArg Transformer argument that this GDALWarpOperation
551 : * must use, and own, or NULL. When psOwnedTransformerArg is set, this implies that
552 : * psNewOptions->pTransformerArg is NULL
553 : *
554 : * @return CE_None on success or CE_Failure if an error occurs.
555 : */
556 :
557 : CPLErr
558 1831 : GDALWarpOperation::Initialize(const GDALWarpOptions *psNewOptions,
559 : GDALTransformerFunc pfnTransformer,
560 : GDALTransformerArgUniquePtr psOwnedTransformerArg)
561 :
562 : {
563 : /* -------------------------------------------------------------------- */
564 : /* Copy the passed in options. */
565 : /* -------------------------------------------------------------------- */
566 1831 : if (psOptions != nullptr)
567 0 : WipeOptions();
568 :
569 1831 : CPLErr eErr = CE_None;
570 :
571 1831 : psOptions = GDALCloneWarpOptions(psNewOptions);
572 :
573 1831 : if (psOptions->pfnTransformer)
574 : {
575 854 : CPLAssert(pfnTransformer == nullptr);
576 854 : CPLAssert(psOwnedTransformerArg.get() == nullptr);
577 : }
578 : else
579 : {
580 977 : m_psOwnedTransformerArg = std::move(psOwnedTransformerArg);
581 977 : psOptions->pfnTransformer = pfnTransformer;
582 977 : psOptions->pTransformerArg = m_psOwnedTransformerArg.get();
583 : }
584 :
585 3662 : psOptions->papszWarpOptions =
586 1831 : CSLSetNameValue(psOptions->papszWarpOptions, "EXTRA_ELTS",
587 : CPLSPrintf("%d", WARP_EXTRA_ELTS));
588 :
589 : /* -------------------------------------------------------------------- */
590 : /* Default band mapping if missing. */
591 : /* -------------------------------------------------------------------- */
592 0 : if (psOptions->nBandCount == 0 && psOptions->hSrcDS != nullptr &&
593 1831 : psOptions->hDstDS != nullptr &&
594 0 : GDALGetRasterCount(psOptions->hSrcDS) ==
595 0 : GDALGetRasterCount(psOptions->hDstDS))
596 : {
597 0 : GDALWarpInitDefaultBandMapping(psOptions,
598 0 : GDALGetRasterCount(psOptions->hSrcDS));
599 : }
600 :
601 1831 : GDALWarpResolveWorkingDataType(psOptions);
602 1831 : SetTieStrategy(psOptions, &eErr);
603 :
604 : /* -------------------------------------------------------------------- */
605 : /* Default memory available. */
606 : /* */
607 : /* For now we default to 64MB of RAM, but eventually we should */
608 : /* try various schemes to query physical RAM. This can */
609 : /* certainly be done on Win32 and Linux. */
610 : /* -------------------------------------------------------------------- */
611 1831 : if (psOptions->dfWarpMemoryLimit == 0.0)
612 : {
613 1622 : psOptions->dfWarpMemoryLimit = 64.0 * 1024 * 1024;
614 : }
615 :
616 : /* -------------------------------------------------------------------- */
617 : /* Are we doing timings? */
618 : /* -------------------------------------------------------------------- */
619 1831 : bReportTimings =
620 1831 : CPLFetchBool(psOptions->papszWarpOptions, "REPORT_TIMINGS", false);
621 :
622 : /* -------------------------------------------------------------------- */
623 : /* Support creating cutline from text warpoption. */
624 : /* -------------------------------------------------------------------- */
625 : const char *pszCutlineWKT =
626 1831 : CSLFetchNameValue(psOptions->papszWarpOptions, "CUTLINE");
627 :
628 1831 : if (pszCutlineWKT && psOptions->hCutline == nullptr)
629 : {
630 44 : char *pszWKTTmp = const_cast<char *>(pszCutlineWKT);
631 88 : if (OGR_G_CreateFromWkt(&pszWKTTmp, nullptr,
632 : reinterpret_cast<OGRGeometryH *>(
633 44 : &(psOptions->hCutline))) != OGRERR_NONE)
634 : {
635 2 : eErr = CE_Failure;
636 2 : CPLError(CE_Failure, CPLE_AppDefined,
637 : "Failed to parse CUTLINE geometry wkt.");
638 : }
639 : }
640 : const char *pszBD =
641 1831 : CSLFetchNameValue(psOptions->papszWarpOptions, "CUTLINE_BLEND_DIST");
642 1831 : if (pszBD)
643 0 : psOptions->dfCutlineBlendDist = CPLAtof(pszBD);
644 :
645 : /* -------------------------------------------------------------------- */
646 : /* Set SRC_ALPHA_MAX if not provided. */
647 : /* -------------------------------------------------------------------- */
648 1831 : if (psOptions->hSrcDS != nullptr && psOptions->nSrcAlphaBand > 0 &&
649 3769 : psOptions->nSrcAlphaBand <= GDALGetRasterCount(psOptions->hSrcDS) &&
650 107 : CSLFetchNameValue(psOptions->papszWarpOptions, "SRC_ALPHA_MAX") ==
651 : nullptr)
652 : {
653 : GDALRasterBandH hSrcAlphaBand =
654 107 : GDALGetRasterBand(psOptions->hSrcDS, psOptions->nSrcAlphaBand);
655 107 : SetAlphaMax(psOptions, hSrcAlphaBand, "SRC_ALPHA_MAX");
656 : }
657 :
658 : /* -------------------------------------------------------------------- */
659 : /* Set DST_ALPHA_MAX if not provided. */
660 : /* -------------------------------------------------------------------- */
661 1818 : if (psOptions->hDstDS != nullptr && psOptions->nDstAlphaBand > 0 &&
662 4053 : psOptions->nDstAlphaBand <= GDALGetRasterCount(psOptions->hDstDS) &&
663 404 : CSLFetchNameValue(psOptions->papszWarpOptions, "DST_ALPHA_MAX") ==
664 : nullptr)
665 : {
666 : GDALRasterBandH hDstAlphaBand =
667 403 : GDALGetRasterBand(psOptions->hDstDS, psOptions->nDstAlphaBand);
668 403 : SetAlphaMax(psOptions, hDstAlphaBand, "DST_ALPHA_MAX");
669 : }
670 :
671 : /* -------------------------------------------------------------------- */
672 : /* If the options don't validate, then wipe them. */
673 : /* -------------------------------------------------------------------- */
674 1831 : if (!ValidateOptions())
675 0 : eErr = CE_Failure;
676 :
677 1831 : if (eErr != CE_None)
678 : {
679 4 : WipeOptions();
680 : }
681 : else
682 : {
683 3654 : psThreadData = GWKThreadsCreate(psOptions->papszWarpOptions,
684 1827 : psOptions->pfnTransformer,
685 1827 : psOptions->pTransformerArg);
686 1827 : if (psThreadData == nullptr)
687 0 : eErr = CE_Failure;
688 :
689 : /* --------------------------------------------------------------------
690 : */
691 : /* Compute dstcoordinates of a few special points. */
692 : /* --------------------------------------------------------------------
693 : */
694 :
695 : // South and north poles. Do not exactly take +/-90 as the
696 : // round-tripping of the longitude value fails with some projections.
697 5481 : for (double dfY : {-89.9999, 89.9999})
698 : {
699 3654 : double dfX = 0;
700 3654 : if ((GDALIsTransformer(psOptions->pTransformerArg,
701 3034 : GDAL_APPROX_TRANSFORMER_CLASS_NAME) &&
702 3034 : GDALTransformLonLatToDestApproxTransformer(
703 7308 : psOptions->pTransformerArg, &dfX, &dfY)) ||
704 2090 : (GDALIsTransformer(psOptions->pTransformerArg,
705 314 : GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME) &&
706 314 : GDALTransformLonLatToDestGenImgProjTransformer(
707 314 : psOptions->pTransformerArg, &dfX, &dfY)))
708 : {
709 : aDstXYSpecialPoints.emplace_back(
710 1620 : std::pair<double, double>(dfX, dfY));
711 : }
712 : }
713 :
714 1827 : m_bIsTranslationOnPixelBoundaries =
715 3654 : GDALTransformIsTranslationOnPixelBoundaries(
716 2090 : psOptions->pfnTransformer, psOptions->pTransformerArg) &&
717 263 : CPLTestBool(
718 : CPLGetConfigOption("GDAL_WARP_USE_TRANSLATION_OPTIM", "YES"));
719 1827 : if (m_bIsTranslationOnPixelBoundaries)
720 : {
721 257 : CPLDebug("WARP",
722 : "Using translation-on-pixel-boundaries optimization");
723 : }
724 : }
725 :
726 1831 : if (eErr == CE_None && psOptions->hDstDS)
727 : {
728 : const auto oResetDestPixels =
729 : cpl::strict_parse<bool>(CSLFetchNameValueDef(
730 1814 : psOptions->papszWarpOptions, "RESET_DEST_PIXELS", "NO"));
731 :
732 1814 : if (!oResetDestPixels.has_value())
733 : {
734 1 : CPLError(CE_Failure, CPLE_IllegalArg,
735 : "Invalid value of RESET_DEST_PIXELS");
736 1 : return CE_Failure;
737 : }
738 :
739 1813 : bool bResetDestPixels = false;
740 : try
741 : {
742 1813 : bResetDestPixels = oResetDestPixels.value();
743 : }
744 0 : catch (const std::exception &)
745 : {
746 : // to make Coverity Scan happy
747 : }
748 :
749 1813 : if (bResetDestPixels)
750 : {
751 4 : for (int i = 0; eErr == CE_None && i < psOptions->nBandCount; ++i)
752 : {
753 2 : eErr =
754 5 : GDALFillRaster(GDALGetRasterBand(psOptions->hDstDS,
755 2 : psOptions->panDstBands[i]),
756 2 : psOptions->padfDstNoDataReal
757 1 : ? psOptions->padfDstNoDataReal[i]
758 : : 0.0,
759 2 : psOptions->padfDstNoDataImag
760 0 : ? psOptions->padfDstNoDataImag[i]
761 : : 0.0);
762 : }
763 : }
764 : }
765 :
766 1830 : return eErr;
767 : }
768 :
769 : /**
770 : * \fn void* GDALWarpOperation::CreateDestinationBuffer(
771 : int nDstXSize, int nDstYSize, int *pbInitialized);
772 : *
773 : * This method creates a destination buffer for use with WarpRegionToBuffer.
774 : * The output is initialized based on the INIT_DEST settings.
775 : *
776 : * @param nDstXSize Width of output window on destination buffer to be produced.
777 : * @param nDstYSize Height of output window on destination buffer to be
778 : produced.
779 : * @param pbInitialized Filled with boolean indicating if the buffer was
780 : initialized.
781 : *
782 : * @return Buffer capable for use as a warp operation output destination
783 : */
784 3386 : void *GDALWarpOperation::CreateDestinationBuffer(int nDstXSize, int nDstYSize,
785 : int *pbInitialized)
786 : {
787 :
788 : /* -------------------------------------------------------------------- */
789 : /* Allocate block of memory large enough to hold all the bands */
790 : /* for this block. */
791 : /* -------------------------------------------------------------------- */
792 3386 : const int nWordSize = GDALGetDataTypeSizeBytes(psOptions->eWorkingDataType);
793 :
794 3386 : void *pDstBuffer = VSI_MALLOC3_VERBOSE(
795 : cpl::fits_on<int>(nWordSize * psOptions->nBandCount), nDstXSize,
796 : nDstYSize);
797 3386 : if (pDstBuffer)
798 : {
799 3386 : auto eErr = InitializeDestinationBuffer(pDstBuffer, nDstXSize,
800 : nDstYSize, pbInitialized);
801 3386 : if (eErr != CE_None)
802 : {
803 2 : CPLFree(pDstBuffer);
804 2 : return nullptr;
805 : }
806 : }
807 3384 : return pDstBuffer;
808 : }
809 :
810 : /**
811 : * This method initializes a destination buffer for use with WarpRegionToBuffer.
812 : *
813 : * It is initialized based on the INIT_DEST settings.
814 : *
815 : * This method is called by CreateDestinationBuffer().
816 : * It is meant at being used by callers that have already allocated the
817 : * destination buffer without using CreateDestinationBuffer().
818 : *
819 : * @param pDstBuffer Buffer of size
820 : * GDALGetDataTypeSizeBytes(psOptions->eWorkingDataType) *
821 : * nDstXSize * nDstYSize * psOptions->nBandCount bytes.
822 : * @param nDstXSize Width of output window on destination buffer to be produced.
823 : * @param nDstYSize Height of output window on destination buffer to be
824 : * produced.
825 : * @param pbInitialized Filled with boolean indicating if the buffer was
826 : * initialized.
827 : * @since 3.10
828 : */
829 3485 : CPLErr GDALWarpOperation::InitializeDestinationBuffer(void *pDstBuffer,
830 : int nDstXSize,
831 : int nDstYSize,
832 : int *pbInitialized) const
833 : {
834 3485 : const int nWordSize = GDALGetDataTypeSizeBytes(psOptions->eWorkingDataType);
835 :
836 3485 : const GPtrDiff_t nBandSize =
837 3485 : static_cast<GPtrDiff_t>(nWordSize) * nDstXSize * nDstYSize;
838 :
839 : /* -------------------------------------------------------------------- */
840 : /* Initialize if requested in the options */
841 : /* -------------------------------------------------------------------- */
842 : const char *pszInitDest =
843 3485 : CSLFetchNameValue(psOptions->papszWarpOptions, "INIT_DEST");
844 :
845 3485 : if (pszInitDest == nullptr || EQUAL(pszInitDest, ""))
846 : {
847 384 : if (pbInitialized != nullptr)
848 : {
849 384 : *pbInitialized = FALSE;
850 : }
851 384 : return CE_None;
852 : }
853 :
854 3101 : if (pbInitialized != nullptr)
855 : {
856 1938 : *pbInitialized = TRUE;
857 : }
858 :
859 : CPLStringList aosInitValues(
860 6202 : CSLTokenizeStringComplex(pszInitDest, ",", FALSE, FALSE));
861 3101 : const int nInitCount = aosInitValues.Count();
862 :
863 8306 : for (int iBand = 0; iBand < psOptions->nBandCount; iBand++)
864 : {
865 5207 : double adfInitRealImag[2] = {0.0, 0.0};
866 : const char *pszBandInit =
867 5207 : aosInitValues[std::min(iBand, nInitCount - 1)];
868 :
869 5207 : if (EQUAL(pszBandInit, "NO_DATA"))
870 : {
871 709 : if (psOptions->padfDstNoDataReal == nullptr)
872 : {
873 1 : CPLError(CE_Failure, CPLE_AppDefined,
874 : "INIT_DEST was set to NO_DATA, but a NoData value was "
875 : "not defined.");
876 : }
877 : else
878 : {
879 708 : adfInitRealImag[0] = psOptions->padfDstNoDataReal[iBand];
880 708 : if (psOptions->padfDstNoDataImag != nullptr)
881 : {
882 601 : adfInitRealImag[1] = psOptions->padfDstNoDataImag[iBand];
883 : }
884 : }
885 : }
886 : else
887 : {
888 4498 : if (CPLStringToComplex(pszBandInit, &adfInitRealImag[0],
889 4498 : &adfInitRealImag[1]) != CE_None)
890 : {
891 2 : CPLError(CE_Failure, CPLE_AppDefined,
892 : "Error parsing INIT_DEST");
893 2 : return CE_Failure;
894 : }
895 : }
896 :
897 5205 : GByte *pBandData = static_cast<GByte *>(pDstBuffer) + iBand * nBandSize;
898 :
899 5205 : if (psOptions->eWorkingDataType == GDT_UInt8)
900 : {
901 9112 : memset(pBandData,
902 : std::max(
903 4556 : 0, std::min(255, static_cast<int>(adfInitRealImag[0]))),
904 : nBandSize);
905 : }
906 1293 : else if (!std::isnan(adfInitRealImag[0]) && adfInitRealImag[0] == 0.0 &&
907 1293 : !std::isnan(adfInitRealImag[1]) && adfInitRealImag[1] == 0.0)
908 : {
909 559 : memset(pBandData, 0, nBandSize);
910 : }
911 90 : else if (!std::isnan(adfInitRealImag[1]) && adfInitRealImag[1] == 0.0)
912 : {
913 90 : GDALCopyWords64(&adfInitRealImag, GDT_Float64, 0, pBandData,
914 90 : psOptions->eWorkingDataType, nWordSize,
915 90 : static_cast<GPtrDiff_t>(nDstXSize) * nDstYSize);
916 : }
917 : else
918 : {
919 0 : GDALCopyWords64(&adfInitRealImag, GDT_CFloat64, 0, pBandData,
920 0 : psOptions->eWorkingDataType, nWordSize,
921 0 : static_cast<GPtrDiff_t>(nDstXSize) * nDstYSize);
922 : }
923 : }
924 :
925 3099 : return CE_None;
926 : }
927 :
928 : /**
929 : * \fn void GDALWarpOperation::DestroyDestinationBuffer( void *pDstBuffer )
930 : *
931 : * This method destroys a buffer previously retrieved from
932 : * CreateDestinationBuffer
933 : *
934 : * @param pDstBuffer destination buffer to be destroyed
935 : *
936 : */
937 3384 : void GDALWarpOperation::DestroyDestinationBuffer(void *pDstBuffer)
938 : {
939 3384 : VSIFree(pDstBuffer);
940 3384 : }
941 :
942 : /************************************************************************/
943 : /* GDALCreateWarpOperation() */
944 : /************************************************************************/
945 :
946 : /**
947 : * @see GDALWarpOperation::Initialize()
948 : */
949 :
950 149 : GDALWarpOperationH GDALCreateWarpOperation(const GDALWarpOptions *psNewOptions)
951 : {
952 149 : GDALWarpOperation *poOperation = new GDALWarpOperation;
953 149 : if (poOperation->Initialize(psNewOptions) != CE_None)
954 : {
955 0 : delete poOperation;
956 0 : return nullptr;
957 : }
958 :
959 149 : return reinterpret_cast<GDALWarpOperationH>(poOperation);
960 : }
961 :
962 : /************************************************************************/
963 : /* GDALDestroyWarpOperation() */
964 : /************************************************************************/
965 :
966 : /**
967 : * @see GDALWarpOperation::~GDALWarpOperation()
968 : */
969 :
970 149 : void GDALDestroyWarpOperation(GDALWarpOperationH hOperation)
971 : {
972 149 : if (hOperation)
973 149 : delete static_cast<GDALWarpOperation *>(hOperation);
974 149 : }
975 :
976 : /************************************************************************/
977 : /* CollectChunkList() */
978 : /************************************************************************/
979 :
980 1226 : void GDALWarpOperation::CollectChunkList(int nDstXOff, int nDstYOff,
981 : int nDstXSize, int nDstYSize)
982 :
983 : {
984 : /* -------------------------------------------------------------------- */
985 : /* Collect the list of chunks to operate on. */
986 : /* -------------------------------------------------------------------- */
987 1226 : WipeChunkList();
988 1226 : CollectChunkListInternal(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
989 :
990 : // Sort chunks from top to bottom, and for equal y, from left to right.
991 1226 : if (nChunkListCount > 1)
992 : {
993 56 : std::sort(pasChunkList, pasChunkList + nChunkListCount,
994 7201 : [](const GDALWarpChunk &a, const GDALWarpChunk &b)
995 : {
996 7201 : if (a.dy < b.dy)
997 3220 : return true;
998 3981 : if (a.dy > b.dy)
999 1415 : return false;
1000 2566 : return a.dx < b.dx;
1001 : });
1002 : }
1003 :
1004 : /* -------------------------------------------------------------------- */
1005 : /* Find the global source window. */
1006 : /* -------------------------------------------------------------------- */
1007 :
1008 1226 : const int knIntMax = std::numeric_limits<int>::max();
1009 1226 : const int knIntMin = std::numeric_limits<int>::min();
1010 1226 : int nSrcXOff = knIntMax;
1011 1226 : int nSrcYOff = knIntMax;
1012 1226 : int nSrcX2Off = knIntMin;
1013 1226 : int nSrcY2Off = knIntMin;
1014 1226 : double dfApproxAccArea = 0;
1015 3548 : for (int iChunk = 0; pasChunkList != nullptr && iChunk < nChunkListCount;
1016 : iChunk++)
1017 : {
1018 2322 : GDALWarpChunk *pasThisChunk = pasChunkList + iChunk;
1019 2322 : nSrcXOff = std::min(nSrcXOff, pasThisChunk->sx);
1020 2322 : nSrcYOff = std::min(nSrcYOff, pasThisChunk->sy);
1021 2322 : nSrcX2Off = std::max(nSrcX2Off, pasThisChunk->sx + pasThisChunk->ssx);
1022 2322 : nSrcY2Off = std::max(nSrcY2Off, pasThisChunk->sy + pasThisChunk->ssy);
1023 2322 : dfApproxAccArea +=
1024 2322 : static_cast<double>(pasThisChunk->ssx) * pasThisChunk->ssy;
1025 : }
1026 1226 : if (nSrcXOff < nSrcX2Off)
1027 : {
1028 1218 : const double dfTotalArea =
1029 1218 : static_cast<double>(nSrcX2Off - nSrcXOff) * (nSrcY2Off - nSrcYOff);
1030 : // This is really a gross heuristics, but should work in most cases
1031 1218 : if (dfApproxAccArea >= dfTotalArea * 0.80)
1032 : {
1033 1218 : GDALDataset::FromHandle(psOptions->hSrcDS)
1034 1218 : ->AdviseRead(nSrcXOff, nSrcYOff, nSrcX2Off - nSrcXOff,
1035 : nSrcY2Off - nSrcYOff, nDstXSize, nDstYSize,
1036 1218 : psOptions->eWorkingDataType, psOptions->nBandCount,
1037 1218 : psOptions->panSrcBands, nullptr);
1038 : }
1039 : }
1040 1226 : }
1041 :
1042 : /************************************************************************/
1043 : /* ChunkAndWarpImage() */
1044 : /************************************************************************/
1045 :
1046 : /**
1047 : * \fn CPLErr GDALWarpOperation::ChunkAndWarpImage(
1048 : int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize );
1049 : *
1050 : * This method does a complete warp of the source image to the destination
1051 : * image for the indicated region with the current warp options in effect.
1052 : * Progress is reported to the installed progress monitor, if any.
1053 : *
1054 : * This function will subdivide the region and recursively call itself
1055 : * until the total memory required to process a region chunk will all fit
1056 : * in the memory pool defined by GDALWarpOptions::dfWarpMemoryLimit.
1057 : *
1058 : * Once an appropriate region is selected GDALWarpOperation::WarpRegion()
1059 : * is invoked to do the actual work.
1060 : *
1061 : * @param nDstXOff X offset to window of destination data to be produced.
1062 : * @param nDstYOff Y offset to window of destination data to be produced.
1063 : * @param nDstXSize Width of output window on destination file to be produced.
1064 : * @param nDstYSize Height of output window on destination file to be produced.
1065 : *
1066 : * @return CE_None on success or CE_Failure if an error occurs.
1067 : */
1068 :
1069 1220 : CPLErr GDALWarpOperation::ChunkAndWarpImage(int nDstXOff, int nDstYOff,
1070 : int nDstXSize, int nDstYSize)
1071 :
1072 : {
1073 : /* -------------------------------------------------------------------- */
1074 : /* Collect the list of chunks to operate on. */
1075 : /* -------------------------------------------------------------------- */
1076 1220 : CollectChunkList(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
1077 :
1078 : /* -------------------------------------------------------------------- */
1079 : /* Total up output pixels to process. */
1080 : /* -------------------------------------------------------------------- */
1081 1220 : double dfTotalPixels = 0.0;
1082 :
1083 3531 : for (int iChunk = 0; pasChunkList != nullptr && iChunk < nChunkListCount;
1084 : iChunk++)
1085 : {
1086 2311 : GDALWarpChunk *pasThisChunk = pasChunkList + iChunk;
1087 2311 : const double dfChunkPixels =
1088 2311 : pasThisChunk->dsx * static_cast<double>(pasThisChunk->dsy);
1089 :
1090 2311 : dfTotalPixels += dfChunkPixels;
1091 : }
1092 :
1093 : /* -------------------------------------------------------------------- */
1094 : /* Process them one at a time, updating the progress */
1095 : /* information for each region. */
1096 : /* -------------------------------------------------------------------- */
1097 1220 : double dfPixelsProcessed = 0.0;
1098 :
1099 3525 : for (int iChunk = 0; pasChunkList != nullptr && iChunk < nChunkListCount;
1100 : iChunk++)
1101 : {
1102 2311 : GDALWarpChunk *pasThisChunk = pasChunkList + iChunk;
1103 2311 : const double dfChunkPixels =
1104 2311 : pasThisChunk->dsx * static_cast<double>(pasThisChunk->dsy);
1105 :
1106 2311 : const double dfProgressBase = dfPixelsProcessed / dfTotalPixels;
1107 2311 : const double dfProgressScale = dfChunkPixels / dfTotalPixels;
1108 :
1109 2311 : CPLErr eErr = WarpRegion(
1110 : pasThisChunk->dx, pasThisChunk->dy, pasThisChunk->dsx,
1111 : pasThisChunk->dsy, pasThisChunk->sx, pasThisChunk->sy,
1112 : pasThisChunk->ssx, pasThisChunk->ssy, pasThisChunk->sExtraSx,
1113 : pasThisChunk->sExtraSy, dfProgressBase, dfProgressScale);
1114 :
1115 2311 : if (eErr != CE_None)
1116 6 : return eErr;
1117 :
1118 2305 : dfPixelsProcessed += dfChunkPixels;
1119 : }
1120 :
1121 1214 : WipeChunkList();
1122 :
1123 1214 : psOptions->pfnProgress(1.0, "", psOptions->pProgressArg);
1124 :
1125 1214 : return CE_None;
1126 : }
1127 :
1128 : /************************************************************************/
1129 : /* GDALChunkAndWarpImage() */
1130 : /************************************************************************/
1131 :
1132 : /**
1133 : * @see GDALWarpOperation::ChunkAndWarpImage()
1134 : */
1135 :
1136 149 : CPLErr GDALChunkAndWarpImage(GDALWarpOperationH hOperation, int nDstXOff,
1137 : int nDstYOff, int nDstXSize, int nDstYSize)
1138 : {
1139 149 : VALIDATE_POINTER1(hOperation, "GDALChunkAndWarpImage", CE_Failure);
1140 :
1141 : return reinterpret_cast<GDALWarpOperation *>(hOperation)
1142 149 : ->ChunkAndWarpImage(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
1143 : }
1144 :
1145 : /************************************************************************/
1146 : /* ChunkThreadMain() */
1147 : /************************************************************************/
1148 :
1149 : struct ChunkThreadData
1150 : {
1151 : GDALWarpOperation *poOperation = nullptr;
1152 : GDALWarpChunk *pasChunkInfo = nullptr;
1153 : CPLJoinableThread *hThreadHandle = nullptr;
1154 : CPLErr eErr = CE_None;
1155 : double dfProgressBase = 0;
1156 : double dfProgressScale = 0;
1157 : CPLMutex *hIOMutex = nullptr;
1158 :
1159 : CPLMutex *hCondMutex = nullptr;
1160 : volatile int bIOMutexTaken = 0;
1161 : CPLCond *hCond = nullptr;
1162 :
1163 : CPLErrorAccumulator *poErrorAccumulator = nullptr;
1164 : };
1165 :
1166 11 : static void ChunkThreadMain(void *pThreadData)
1167 :
1168 : {
1169 11 : volatile ChunkThreadData *psData =
1170 : static_cast<volatile ChunkThreadData *>(pThreadData);
1171 :
1172 11 : GDALWarpChunk *pasChunkInfo = psData->pasChunkInfo;
1173 :
1174 : /* -------------------------------------------------------------------- */
1175 : /* Acquire IO mutex. */
1176 : /* -------------------------------------------------------------------- */
1177 11 : if (!CPLAcquireMutex(psData->hIOMutex, 600.0))
1178 : {
1179 0 : CPLError(CE_Failure, CPLE_AppDefined,
1180 : "Failed to acquire IOMutex in WarpRegion().");
1181 0 : psData->eErr = CE_Failure;
1182 : }
1183 : else
1184 : {
1185 11 : if (psData->hCond != nullptr)
1186 : {
1187 6 : CPLAcquireMutex(psData->hCondMutex, 1.0);
1188 6 : psData->bIOMutexTaken = TRUE;
1189 6 : CPLCondSignal(psData->hCond);
1190 6 : CPLReleaseMutex(psData->hCondMutex);
1191 : }
1192 :
1193 : auto oAccumulator =
1194 22 : psData->poErrorAccumulator->InstallForCurrentScope();
1195 11 : CPL_IGNORE_RET_VAL(oAccumulator);
1196 :
1197 22 : psData->eErr = psData->poOperation->WarpRegion(
1198 : pasChunkInfo->dx, pasChunkInfo->dy, pasChunkInfo->dsx,
1199 : pasChunkInfo->dsy, pasChunkInfo->sx, pasChunkInfo->sy,
1200 : pasChunkInfo->ssx, pasChunkInfo->ssy, pasChunkInfo->sExtraSx,
1201 11 : pasChunkInfo->sExtraSy, psData->dfProgressBase,
1202 11 : psData->dfProgressScale);
1203 :
1204 : /* --------------------------------------------------------------------
1205 : */
1206 : /* Release the IO mutex. */
1207 : /* --------------------------------------------------------------------
1208 : */
1209 11 : CPLReleaseMutex(psData->hIOMutex);
1210 : }
1211 11 : }
1212 :
1213 : /************************************************************************/
1214 : /* ChunkAndWarpMulti() */
1215 : /************************************************************************/
1216 :
1217 : /**
1218 : * \fn CPLErr GDALWarpOperation::ChunkAndWarpMulti(
1219 : int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize );
1220 : *
1221 : * This method does a complete warp of the source image to the destination
1222 : * image for the indicated region with the current warp options in effect.
1223 : * Progress is reported to the installed progress monitor, if any.
1224 : *
1225 : * Externally this method operates the same as ChunkAndWarpImage(), but
1226 : * internally this method uses multiple threads to interleave input/output
1227 : * for one region while the processing is being done for another.
1228 : *
1229 : * @param nDstXOff X offset to window of destination data to be produced.
1230 : * @param nDstYOff Y offset to window of destination data to be produced.
1231 : * @param nDstXSize Width of output window on destination file to be produced.
1232 : * @param nDstYSize Height of output window on destination file to be produced.
1233 : *
1234 : * @return CE_None on success or CE_Failure if an error occurs.
1235 : */
1236 :
1237 6 : CPLErr GDALWarpOperation::ChunkAndWarpMulti(int nDstXOff, int nDstYOff,
1238 : int nDstXSize, int nDstYSize)
1239 :
1240 : {
1241 6 : hIOMutex = CPLCreateMutex();
1242 6 : hWarpMutex = CPLCreateMutex();
1243 :
1244 6 : CPLReleaseMutex(hIOMutex);
1245 6 : CPLReleaseMutex(hWarpMutex);
1246 :
1247 6 : CPLCond *hCond = CPLCreateCond();
1248 6 : CPLMutex *hCondMutex = CPLCreateMutex();
1249 6 : CPLReleaseMutex(hCondMutex);
1250 :
1251 : /* -------------------------------------------------------------------- */
1252 : /* Collect the list of chunks to operate on. */
1253 : /* -------------------------------------------------------------------- */
1254 6 : CollectChunkList(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
1255 :
1256 : /* -------------------------------------------------------------------- */
1257 : /* Process them one at a time, updating the progress */
1258 : /* information for each region. */
1259 : /* -------------------------------------------------------------------- */
1260 6 : ChunkThreadData volatile asThreadData[2] = {};
1261 6 : CPLErrorAccumulator oErrorAccumulator;
1262 18 : for (int i = 0; i < 2; ++i)
1263 : {
1264 12 : asThreadData[i].poOperation = this;
1265 12 : asThreadData[i].hIOMutex = hIOMutex;
1266 12 : asThreadData[i].poErrorAccumulator = &oErrorAccumulator;
1267 : }
1268 :
1269 6 : double dfPixelsProcessed = 0.0;
1270 6 : double dfTotalPixels = static_cast<double>(nDstXSize) * nDstYSize;
1271 :
1272 6 : CPLErr eErr = CE_None;
1273 22 : for (int iChunk = 0; iChunk < nChunkListCount + 1; iChunk++)
1274 : {
1275 17 : int iThread = iChunk % 2;
1276 :
1277 : /* --------------------------------------------------------------------
1278 : */
1279 : /* Launch thread for this chunk. */
1280 : /* --------------------------------------------------------------------
1281 : */
1282 17 : if (pasChunkList != nullptr && iChunk < nChunkListCount)
1283 : {
1284 11 : GDALWarpChunk *pasThisChunk = pasChunkList + iChunk;
1285 11 : const double dfChunkPixels =
1286 11 : pasThisChunk->dsx * static_cast<double>(pasThisChunk->dsy);
1287 :
1288 11 : asThreadData[iThread].dfProgressBase =
1289 11 : dfPixelsProcessed / dfTotalPixels;
1290 11 : asThreadData[iThread].dfProgressScale =
1291 11 : dfChunkPixels / dfTotalPixels;
1292 :
1293 11 : dfPixelsProcessed += dfChunkPixels;
1294 :
1295 11 : asThreadData[iThread].pasChunkInfo = pasThisChunk;
1296 :
1297 11 : if (iChunk == 0)
1298 : {
1299 6 : asThreadData[iThread].hCond = hCond;
1300 6 : asThreadData[iThread].hCondMutex = hCondMutex;
1301 : }
1302 : else
1303 : {
1304 5 : asThreadData[iThread].hCond = nullptr;
1305 5 : asThreadData[iThread].hCondMutex = nullptr;
1306 : }
1307 11 : asThreadData[iThread].bIOMutexTaken = FALSE;
1308 :
1309 11 : CPLDebug("GDAL", "Start chunk %d / %d.", iChunk, nChunkListCount);
1310 22 : asThreadData[iThread].hThreadHandle = CPLCreateJoinableThread(
1311 : ChunkThreadMain,
1312 11 : const_cast<ChunkThreadData *>(&asThreadData[iThread]));
1313 11 : if (asThreadData[iThread].hThreadHandle == nullptr)
1314 : {
1315 0 : CPLError(
1316 : CE_Failure, CPLE_AppDefined,
1317 : "CPLCreateJoinableThread() failed in ChunkAndWarpMulti()");
1318 0 : eErr = CE_Failure;
1319 0 : break;
1320 : }
1321 :
1322 : // Wait that the first thread has acquired the IO mutex before
1323 : // proceeding. This will ensure that the first thread will run
1324 : // before the second one.
1325 11 : if (iChunk == 0)
1326 : {
1327 6 : CPLAcquireMutex(hCondMutex, 1.0);
1328 11 : while (asThreadData[iThread].bIOMutexTaken == FALSE)
1329 5 : CPLCondWait(hCond, hCondMutex);
1330 6 : CPLReleaseMutex(hCondMutex);
1331 : }
1332 : }
1333 :
1334 : /* --------------------------------------------------------------------
1335 : */
1336 : /* Wait for previous chunks thread to complete. */
1337 : /* --------------------------------------------------------------------
1338 : */
1339 17 : if (iChunk > 0)
1340 : {
1341 11 : iThread = (iChunk - 1) % 2;
1342 :
1343 : // Wait for thread to finish.
1344 11 : CPLJoinThread(asThreadData[iThread].hThreadHandle);
1345 11 : asThreadData[iThread].hThreadHandle = nullptr;
1346 :
1347 11 : CPLDebug("GDAL", "Finished chunk %d / %d.", iChunk - 1,
1348 : nChunkListCount);
1349 :
1350 11 : eErr = asThreadData[iThread].eErr;
1351 :
1352 11 : if (eErr != CE_None)
1353 1 : break;
1354 : }
1355 : }
1356 :
1357 : /* -------------------------------------------------------------------- */
1358 : /* Wait for all threads to complete. */
1359 : /* -------------------------------------------------------------------- */
1360 18 : for (int iThread = 0; iThread < 2; iThread++)
1361 : {
1362 12 : if (asThreadData[iThread].hThreadHandle)
1363 0 : CPLJoinThread(asThreadData[iThread].hThreadHandle);
1364 : }
1365 :
1366 6 : CPLDestroyCond(hCond);
1367 6 : CPLDestroyMutex(hCondMutex);
1368 :
1369 6 : WipeChunkList();
1370 :
1371 6 : oErrorAccumulator.ReplayErrors();
1372 :
1373 6 : psOptions->pfnProgress(1.0, "", psOptions->pProgressArg);
1374 :
1375 12 : return eErr;
1376 : }
1377 :
1378 : /************************************************************************/
1379 : /* GDALChunkAndWarpMulti() */
1380 : /************************************************************************/
1381 :
1382 : /**
1383 : * @see GDALWarpOperation::ChunkAndWarpMulti()
1384 : */
1385 :
1386 0 : CPLErr GDALChunkAndWarpMulti(GDALWarpOperationH hOperation, int nDstXOff,
1387 : int nDstYOff, int nDstXSize, int nDstYSize)
1388 : {
1389 0 : VALIDATE_POINTER1(hOperation, "GDALChunkAndWarpMulti", CE_Failure);
1390 :
1391 : return reinterpret_cast<GDALWarpOperation *>(hOperation)
1392 0 : ->ChunkAndWarpMulti(nDstXOff, nDstYOff, nDstXSize, nDstYSize);
1393 : }
1394 :
1395 : /************************************************************************/
1396 : /* WipeChunkList() */
1397 : /************************************************************************/
1398 :
1399 4277 : void GDALWarpOperation::WipeChunkList()
1400 :
1401 : {
1402 4277 : CPLFree(pasChunkList);
1403 4277 : pasChunkList = nullptr;
1404 4277 : nChunkListCount = 0;
1405 4277 : nChunkListMax = 0;
1406 4277 : }
1407 :
1408 : /************************************************************************/
1409 : /* GetWorkingMemoryForWindow() */
1410 : /************************************************************************/
1411 :
1412 : /** Returns the amount of working memory, in bytes, required to process
1413 : * a warped window of source dimensions nSrcXSize x nSrcYSize and target
1414 : * dimensions nDstXSize x nDstYSize.
1415 : */
1416 4113 : double GDALWarpOperation::GetWorkingMemoryForWindow(int nSrcXSize,
1417 : int nSrcYSize,
1418 : int nDstXSize,
1419 : int nDstYSize) const
1420 : {
1421 : /* -------------------------------------------------------------------- */
1422 : /* Based on the types of masks in use, how many bits will each */
1423 : /* source pixel cost us? */
1424 : /* -------------------------------------------------------------------- */
1425 : int nSrcPixelCostInBits =
1426 4113 : GDALGetDataTypeSizeBits(psOptions->eWorkingDataType) *
1427 4113 : psOptions->nBandCount;
1428 :
1429 4113 : if (psOptions->pfnSrcDensityMaskFunc != nullptr)
1430 0 : nSrcPixelCostInBits += 32; // Float mask?
1431 :
1432 4113 : GDALRasterBandH hSrcBand = nullptr;
1433 4113 : if (psOptions->nBandCount > 0)
1434 : hSrcBand =
1435 4113 : GDALGetRasterBand(psOptions->hSrcDS, psOptions->panSrcBands[0]);
1436 :
1437 4113 : if (psOptions->nSrcAlphaBand > 0 || psOptions->hCutline != nullptr)
1438 118 : nSrcPixelCostInBits += 32; // UnifiedSrcDensity float mask.
1439 7990 : else if (hSrcBand != nullptr &&
1440 3995 : (GDALGetMaskFlags(hSrcBand) & GMF_PER_DATASET))
1441 6 : nSrcPixelCostInBits += 1; // UnifiedSrcValid bit mask.
1442 :
1443 4113 : if (psOptions->papfnSrcPerBandValidityMaskFunc != nullptr ||
1444 4113 : psOptions->padfSrcNoDataReal != nullptr)
1445 211 : nSrcPixelCostInBits += psOptions->nBandCount; // Bit/band mask.
1446 :
1447 4113 : if (psOptions->pfnSrcValidityMaskFunc != nullptr)
1448 0 : nSrcPixelCostInBits += 1; // Bit mask.
1449 :
1450 : /* -------------------------------------------------------------------- */
1451 : /* What about the cost for the destination. */
1452 : /* -------------------------------------------------------------------- */
1453 : int nDstPixelCostInBits =
1454 4113 : GDALGetDataTypeSizeBits(psOptions->eWorkingDataType) *
1455 4113 : psOptions->nBandCount;
1456 :
1457 4113 : if (psOptions->pfnDstDensityMaskFunc != nullptr)
1458 0 : nDstPixelCostInBits += 32;
1459 :
1460 4113 : if (psOptions->padfDstNoDataReal != nullptr ||
1461 2435 : psOptions->pfnDstValidityMaskFunc != nullptr)
1462 1678 : nDstPixelCostInBits += psOptions->nBandCount;
1463 :
1464 4113 : if (psOptions->nDstAlphaBand > 0)
1465 256 : nDstPixelCostInBits += 32; // DstDensity float mask.
1466 :
1467 4113 : const double dfTotalMemoryUse =
1468 4113 : (static_cast<double>(nSrcPixelCostInBits) * nSrcXSize * nSrcYSize +
1469 4113 : static_cast<double>(nDstPixelCostInBits) * nDstXSize * nDstYSize) /
1470 : 8.0;
1471 4113 : return dfTotalMemoryUse;
1472 : }
1473 :
1474 : /************************************************************************/
1475 : /* CollectChunkListInternal() */
1476 : /************************************************************************/
1477 :
1478 4402 : CPLErr GDALWarpOperation::CollectChunkListInternal(int nDstXOff, int nDstYOff,
1479 : int nDstXSize, int nDstYSize)
1480 :
1481 : {
1482 : /* -------------------------------------------------------------------- */
1483 : /* Compute the bounds of the input area corresponding to the */
1484 : /* output area. */
1485 : /* -------------------------------------------------------------------- */
1486 4402 : int nSrcXOff = 0;
1487 4402 : int nSrcYOff = 0;
1488 4402 : int nSrcXSize = 0;
1489 4402 : int nSrcYSize = 0;
1490 4402 : double dfSrcXExtraSize = 0.0;
1491 4402 : double dfSrcYExtraSize = 0.0;
1492 4402 : double dfSrcFillRatio = 0.0;
1493 : CPLErr eErr;
1494 : {
1495 4402 : CPLTurnFailureIntoWarningBackuper oBackuper;
1496 4402 : eErr = ComputeSourceWindow(nDstXOff, nDstYOff, nDstXSize, nDstYSize,
1497 : &nSrcXOff, &nSrcYOff, &nSrcXSize, &nSrcYSize,
1498 : &dfSrcXExtraSize, &dfSrcYExtraSize,
1499 : &dfSrcFillRatio);
1500 : }
1501 :
1502 4402 : if (eErr != CE_None)
1503 : {
1504 : const bool bErrorOutIfEmptySourceWindow =
1505 3 : CPLFetchBool(psOptions->papszWarpOptions,
1506 : "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", true);
1507 3 : if (bErrorOutIfEmptySourceWindow)
1508 : {
1509 3 : CPLError(CE_Warning, CPLE_AppDefined,
1510 : "Unable to compute source region for "
1511 : "output window %d,%d,%d,%d, skipping.",
1512 : nDstXOff, nDstYOff, nDstXSize, nDstYSize);
1513 : }
1514 : else
1515 : {
1516 0 : CPLDebug("WARP",
1517 : "Unable to compute source region for "
1518 : "output window %d,%d,%d,%d, skipping.",
1519 : nDstXOff, nDstYOff, nDstXSize, nDstYSize);
1520 : }
1521 : }
1522 :
1523 : /* -------------------------------------------------------------------- */
1524 : /* If we are allowed to drop no-source regions, do so now if */
1525 : /* appropriate. */
1526 : /* -------------------------------------------------------------------- */
1527 5527 : if ((nSrcXSize == 0 || nSrcYSize == 0) &&
1528 1125 : CPLFetchBool(psOptions->papszWarpOptions, "SKIP_NOSOURCE", false))
1529 492 : return CE_None;
1530 :
1531 : /* -------------------------------------------------------------------- */
1532 : /* Does the cost of the current rectangle exceed our memory */
1533 : /* limit? If so, split the destination along the longest */
1534 : /* dimension and recurse. */
1535 : /* -------------------------------------------------------------------- */
1536 : const double dfTotalMemoryUse =
1537 3910 : GetWorkingMemoryForWindow(nSrcXSize, nSrcYSize, nDstXSize, nDstYSize);
1538 :
1539 : // If size of working buffers need exceed the allow limit, then divide
1540 : // the target area
1541 : // Do it also if the "fill ratio" of the source is too low (#3120), but
1542 : // only if there's at least some source pixel intersecting. The
1543 : // SRC_FILL_RATIO_HEURISTICS warping option is undocumented and only here
1544 : // in case the heuristics would cause issues.
1545 : #if DEBUG_VERBOSE
1546 : CPLDebug("WARP",
1547 : "dst=(%d,%d,%d,%d) src=(%d,%d,%d,%d) srcfillratio=%.17g, "
1548 : "dfTotalMemoryUse=%.1f MB",
1549 : nDstXOff, nDstYOff, nDstXSize, nDstYSize, nSrcXOff, nSrcYOff,
1550 : nSrcXSize, nSrcYSize, dfSrcFillRatio,
1551 : dfTotalMemoryUse / (1024 * 1024));
1552 : #endif
1553 870 : if ((dfTotalMemoryUse > psOptions->dfWarpMemoryLimit &&
1554 7820 : (nDstXSize > 2 || nDstYSize > 2)) ||
1555 3040 : (dfSrcFillRatio > 0 && dfSrcFillRatio < 0.5 &&
1556 350 : (nDstXSize > 100 || nDstYSize > 100) &&
1557 720 : CPLFetchBool(psOptions->papszWarpOptions, "SRC_FILL_RATIO_HEURISTICS",
1558 : true)))
1559 : {
1560 1589 : int nBlockXSize = 1;
1561 1589 : int nBlockYSize = 1;
1562 1589 : if (psOptions->hDstDS)
1563 : {
1564 1589 : GDALGetBlockSize(GDALGetRasterBand(psOptions->hDstDS, 1),
1565 : &nBlockXSize, &nBlockYSize);
1566 : }
1567 :
1568 1589 : int bStreamableOutput = CPLFetchBool(psOptions->papszWarpOptions,
1569 1589 : "STREAMABLE_OUTPUT", false);
1570 : const char *pszOptimizeSize =
1571 1589 : CSLFetchNameValue(psOptions->papszWarpOptions, "OPTIMIZE_SIZE");
1572 1589 : const bool bOptimizeSizeAuto =
1573 1589 : !pszOptimizeSize || EQUAL(pszOptimizeSize, "AUTO");
1574 : const bool bOptimizeSize =
1575 4436 : !bStreamableOutput &&
1576 97 : ((pszOptimizeSize && !bOptimizeSizeAuto &&
1577 1588 : CPLTestBool(pszOptimizeSize)) ||
1578 : // Auto-enable optimize-size mode if output region is at least
1579 : // 2x2 blocks large and the shapes of the source and target regions
1580 : // are not excessively different. All those thresholds are a bit
1581 : // arbitrary
1582 1491 : (bOptimizeSizeAuto && nSrcXSize > 0 && nDstYSize > 0 &&
1583 1259 : (nDstXSize > nDstYSize ? fabs(double(nDstXSize) / nDstYSize -
1584 506 : double(nSrcXSize) / nSrcYSize) <
1585 506 : 5 * double(nDstXSize) / nDstYSize
1586 753 : : fabs(double(nDstYSize) / nDstXSize -
1587 753 : double(nSrcYSize) / nSrcXSize) <
1588 753 : 5 * double(nDstYSize) / nDstXSize) &&
1589 1256 : nDstXSize / 2 >= nBlockXSize && nDstYSize / 2 >= nBlockYSize));
1590 :
1591 : // If the region width is greater than the region height,
1592 : // cut in half in the width. When we want to optimize the size
1593 : // of a compressed output dataset, do this only if each half part
1594 : // is at least as wide as the block width.
1595 1589 : bool bHasDivided = false;
1596 1589 : CPLErr eErr2 = CE_None;
1597 1589 : if (nDstXSize > nDstYSize &&
1598 658 : ((!bOptimizeSize && !bStreamableOutput) ||
1599 88 : (bOptimizeSize &&
1600 89 : (nDstXSize / 2 >= nBlockXSize || nDstYSize == 1)) ||
1601 1 : (bStreamableOutput && nDstXSize / 2 >= nBlockXSize &&
1602 1 : nDstYSize == nBlockYSize)))
1603 : {
1604 611 : bHasDivided = true;
1605 611 : int nChunk1 = nDstXSize / 2;
1606 :
1607 : // In the optimize size case, try to stick on target block
1608 : // boundaries.
1609 611 : if ((bOptimizeSize || bStreamableOutput) && nChunk1 > nBlockXSize)
1610 42 : nChunk1 = (nChunk1 / nBlockXSize) * nBlockXSize;
1611 :
1612 611 : int nChunk2 = nDstXSize - nChunk1;
1613 :
1614 611 : eErr = CollectChunkListInternal(nDstXOff, nDstYOff, nChunk1,
1615 : nDstYSize);
1616 :
1617 611 : eErr2 = CollectChunkListInternal(nDstXOff + nChunk1, nDstYOff,
1618 611 : nChunk2, nDstYSize);
1619 : }
1620 978 : else if (!(bStreamableOutput && nDstYSize / 2 < nBlockYSize))
1621 : {
1622 977 : bHasDivided = true;
1623 977 : int nChunk1 = nDstYSize / 2;
1624 :
1625 : // In the optimize size case, try to stick on target block
1626 : // boundaries.
1627 977 : if ((bOptimizeSize || bStreamableOutput) && nChunk1 > nBlockYSize)
1628 77 : nChunk1 = (nChunk1 / nBlockYSize) * nBlockYSize;
1629 :
1630 977 : const int nChunk2 = nDstYSize - nChunk1;
1631 :
1632 977 : eErr = CollectChunkListInternal(nDstXOff, nDstYOff, nDstXSize,
1633 : nChunk1);
1634 :
1635 977 : eErr2 = CollectChunkListInternal(nDstXOff, nDstYOff + nChunk1,
1636 : nDstXSize, nChunk2);
1637 : }
1638 :
1639 1589 : if (bHasDivided)
1640 : {
1641 1588 : if (eErr == CE_None)
1642 1588 : return eErr2;
1643 : else
1644 0 : return eErr;
1645 : }
1646 : }
1647 :
1648 : /* -------------------------------------------------------------------- */
1649 : /* OK, everything fits, so add to the chunk list. */
1650 : /* -------------------------------------------------------------------- */
1651 2322 : if (nChunkListCount == nChunkListMax)
1652 : {
1653 1387 : nChunkListMax = nChunkListMax * 2 + 1;
1654 1387 : pasChunkList = static_cast<GDALWarpChunk *>(
1655 1387 : CPLRealloc(pasChunkList, sizeof(GDALWarpChunk) * nChunkListMax));
1656 : }
1657 :
1658 2322 : pasChunkList[nChunkListCount].dx = nDstXOff;
1659 2322 : pasChunkList[nChunkListCount].dy = nDstYOff;
1660 2322 : pasChunkList[nChunkListCount].dsx = nDstXSize;
1661 2322 : pasChunkList[nChunkListCount].dsy = nDstYSize;
1662 2322 : pasChunkList[nChunkListCount].sx = nSrcXOff;
1663 2322 : pasChunkList[nChunkListCount].sy = nSrcYOff;
1664 2322 : pasChunkList[nChunkListCount].ssx = nSrcXSize;
1665 2322 : pasChunkList[nChunkListCount].ssy = nSrcYSize;
1666 2322 : pasChunkList[nChunkListCount].sExtraSx = dfSrcXExtraSize;
1667 2322 : pasChunkList[nChunkListCount].sExtraSy = dfSrcYExtraSize;
1668 :
1669 2322 : nChunkListCount++;
1670 :
1671 2322 : return CE_None;
1672 : }
1673 :
1674 : /************************************************************************/
1675 : /* WarpRegion() */
1676 : /************************************************************************/
1677 :
1678 : /**
1679 : * This method requests the indicated region of the output file be generated.
1680 : *
1681 : * Note that WarpRegion() will produce the requested area in one low level warp
1682 : * operation without verifying that this does not exceed the stated memory
1683 : * limits for the warp operation. Applications should take care not to call
1684 : * WarpRegion() on too large a region! This function
1685 : * is normally called by ChunkAndWarpImage(), the normal entry point for
1686 : * applications. Use it instead if staying within memory constraints is
1687 : * desired.
1688 : *
1689 : * Progress is reported from dfProgressBase to dfProgressBase + dfProgressScale
1690 : * for the indicated region.
1691 : *
1692 : * @param nDstXOff X offset to window of destination data to be produced.
1693 : * @param nDstYOff Y offset to window of destination data to be produced.
1694 : * @param nDstXSize Width of output window on destination file to be produced.
1695 : * @param nDstYSize Height of output window on destination file to be produced.
1696 : * @param nSrcXOff source window X offset (computed if window all zero)
1697 : * @param nSrcYOff source window Y offset (computed if window all zero)
1698 : * @param nSrcXSize source window X size (computed if window all zero)
1699 : * @param nSrcYSize source window Y size (computed if window all zero)
1700 : * @param dfProgressBase minimum progress value reported
1701 : * @param dfProgressScale value such as dfProgressBase + dfProgressScale is the
1702 : * maximum progress value reported
1703 : *
1704 : * @return CE_None on success or CE_Failure if an error occurs.
1705 : */
1706 :
1707 0 : CPLErr GDALWarpOperation::WarpRegion(int nDstXOff, int nDstYOff, int nDstXSize,
1708 : int nDstYSize, int nSrcXOff, int nSrcYOff,
1709 : int nSrcXSize, int nSrcYSize,
1710 : double dfProgressBase,
1711 : double dfProgressScale)
1712 : {
1713 0 : return WarpRegion(nDstXOff, nDstYOff, nDstXSize, nDstYSize, nSrcXOff,
1714 : nSrcYOff, nSrcXSize, nSrcYSize, 0, 0, dfProgressBase,
1715 0 : dfProgressScale);
1716 : }
1717 :
1718 : /**
1719 : * This method requests the indicated region of the output file be generated.
1720 : *
1721 : * Note that WarpRegion() will produce the requested area in one low level warp
1722 : * operation without verifying that this does not exceed the stated memory
1723 : * limits for the warp operation. Applications should take care not to call
1724 : * WarpRegion() on too large a region! This function
1725 : * is normally called by ChunkAndWarpImage(), the normal entry point for
1726 : * applications. Use it instead if staying within memory constraints is
1727 : * desired.
1728 : *
1729 : * Progress is reported from dfProgressBase to dfProgressBase + dfProgressScale
1730 : * for the indicated region.
1731 : *
1732 : * @param nDstXOff X offset to window of destination data to be produced.
1733 : * @param nDstYOff Y offset to window of destination data to be produced.
1734 : * @param nDstXSize Width of output window on destination file to be produced.
1735 : * @param nDstYSize Height of output window on destination file to be produced.
1736 : * @param nSrcXOff source window X offset (computed if window all zero)
1737 : * @param nSrcYOff source window Y offset (computed if window all zero)
1738 : * @param nSrcXSize source window X size (computed if window all zero)
1739 : * @param nSrcYSize source window Y size (computed if window all zero)
1740 : * @param dfSrcXExtraSize Extra pixels (included in nSrcXSize) reserved
1741 : * for filter window. Should be ignored in scale computation
1742 : * @param dfSrcYExtraSize Extra pixels (included in nSrcYSize) reserved
1743 : * for filter window. Should be ignored in scale computation
1744 : * @param dfProgressBase minimum progress value reported
1745 : * @param dfProgressScale value such as dfProgressBase + dfProgressScale is the
1746 : * maximum progress value reported
1747 : *
1748 : * @return CE_None on success or CE_Failure if an error occurs.
1749 : */
1750 :
1751 2322 : CPLErr GDALWarpOperation::WarpRegion(
1752 : int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, int nSrcXOff,
1753 : int nSrcYOff, int nSrcXSize, int nSrcYSize, double dfSrcXExtraSize,
1754 : double dfSrcYExtraSize, double dfProgressBase, double dfProgressScale)
1755 :
1756 : {
1757 2322 : ReportTiming(nullptr);
1758 :
1759 : /* -------------------------------------------------------------------- */
1760 : /* Allocate the output buffer. */
1761 : /* -------------------------------------------------------------------- */
1762 2322 : int bDstBufferInitialized = FALSE;
1763 : void *pDstBuffer =
1764 2322 : CreateDestinationBuffer(nDstXSize, nDstYSize, &bDstBufferInitialized);
1765 2322 : if (pDstBuffer == nullptr)
1766 : {
1767 2 : return CE_Failure;
1768 : }
1769 :
1770 : /* -------------------------------------------------------------------- */
1771 : /* If we aren't doing fixed initialization of the output buffer */
1772 : /* then read it from disk so we can overlay on existing imagery. */
1773 : /* -------------------------------------------------------------------- */
1774 2320 : GDALDataset *poDstDS = GDALDataset::FromHandle(psOptions->hDstDS);
1775 2320 : if (!bDstBufferInitialized)
1776 : {
1777 384 : CPLErr eErr = CE_None;
1778 384 : if (psOptions->nBandCount == 1)
1779 : {
1780 : // Particular case to simplify the stack a bit.
1781 : // TODO(rouault): Need an explanation of what and why r34502 helps.
1782 360 : eErr = poDstDS->GetRasterBand(psOptions->panDstBands[0])
1783 720 : ->RasterIO(GF_Read, nDstXOff, nDstYOff, nDstXSize,
1784 : nDstYSize, pDstBuffer, nDstXSize, nDstYSize,
1785 360 : psOptions->eWorkingDataType, 0, 0, nullptr);
1786 : }
1787 : else
1788 : {
1789 24 : eErr = poDstDS->RasterIO(GF_Read, nDstXOff, nDstYOff, nDstXSize,
1790 : nDstYSize, pDstBuffer, nDstXSize,
1791 24 : nDstYSize, psOptions->eWorkingDataType,
1792 24 : psOptions->nBandCount,
1793 24 : psOptions->panDstBands, 0, 0, 0, nullptr);
1794 : }
1795 :
1796 384 : if (eErr != CE_None)
1797 : {
1798 0 : DestroyDestinationBuffer(pDstBuffer);
1799 0 : return eErr;
1800 : }
1801 :
1802 384 : ReportTiming("Output buffer read");
1803 : }
1804 :
1805 : /* -------------------------------------------------------------------- */
1806 : /* Perform the warp. */
1807 : /* -------------------------------------------------------------------- */
1808 : CPLErr eErr = nSrcXSize == 0
1809 2320 : ? CE_None
1810 1919 : : WarpRegionToBuffer(
1811 : nDstXOff, nDstYOff, nDstXSize, nDstYSize,
1812 1919 : pDstBuffer, psOptions->eWorkingDataType, nSrcXOff,
1813 : nSrcYOff, nSrcXSize, nSrcYSize, dfSrcXExtraSize,
1814 2320 : dfSrcYExtraSize, dfProgressBase, dfProgressScale);
1815 :
1816 : /* -------------------------------------------------------------------- */
1817 : /* Write the output data back to disk if all went well. */
1818 : /* -------------------------------------------------------------------- */
1819 2320 : if (eErr == CE_None)
1820 : {
1821 2315 : if (psOptions->nBandCount == 1)
1822 : {
1823 : // Particular case to simplify the stack a bit.
1824 2101 : eErr = poDstDS->GetRasterBand(psOptions->panDstBands[0])
1825 4202 : ->RasterIO(GF_Write, nDstXOff, nDstYOff, nDstXSize,
1826 : nDstYSize, pDstBuffer, nDstXSize, nDstYSize,
1827 2101 : psOptions->eWorkingDataType, 0, 0, nullptr);
1828 : }
1829 : else
1830 : {
1831 214 : eErr = poDstDS->RasterIO(GF_Write, nDstXOff, nDstYOff, nDstXSize,
1832 : nDstYSize, pDstBuffer, nDstXSize,
1833 214 : nDstYSize, psOptions->eWorkingDataType,
1834 214 : psOptions->nBandCount,
1835 214 : psOptions->panDstBands, 0, 0, 0, nullptr);
1836 : }
1837 :
1838 4630 : if (eErr == CE_None &&
1839 2315 : CPLFetchBool(psOptions->papszWarpOptions, "WRITE_FLUSH", false))
1840 : {
1841 0 : const CPLErr eOldErr = CPLGetLastErrorType();
1842 0 : const CPLString osLastErrMsg = CPLGetLastErrorMsg();
1843 0 : GDALFlushCache(psOptions->hDstDS);
1844 0 : const CPLErr eNewErr = CPLGetLastErrorType();
1845 0 : if (eNewErr != eOldErr ||
1846 0 : osLastErrMsg.compare(CPLGetLastErrorMsg()) != 0)
1847 0 : eErr = CE_Failure;
1848 : }
1849 2315 : ReportTiming("Output buffer write");
1850 : }
1851 :
1852 : /* -------------------------------------------------------------------- */
1853 : /* Cleanup and return. */
1854 : /* -------------------------------------------------------------------- */
1855 2320 : DestroyDestinationBuffer(pDstBuffer);
1856 :
1857 2320 : return eErr;
1858 : }
1859 :
1860 : /************************************************************************/
1861 : /* GDALWarpRegion() */
1862 : /************************************************************************/
1863 :
1864 : /**
1865 : * @see GDALWarpOperation::WarpRegion()
1866 : */
1867 :
1868 0 : CPLErr GDALWarpRegion(GDALWarpOperationH hOperation, int nDstXOff, int nDstYOff,
1869 : int nDstXSize, int nDstYSize, int nSrcXOff, int nSrcYOff,
1870 : int nSrcXSize, int nSrcYSize)
1871 :
1872 : {
1873 0 : VALIDATE_POINTER1(hOperation, "GDALWarpRegion", CE_Failure);
1874 :
1875 : return reinterpret_cast<GDALWarpOperation *>(hOperation)
1876 0 : ->WarpRegion(nDstXOff, nDstYOff, nDstXSize, nDstYSize, nSrcXOff,
1877 0 : nSrcYOff, nSrcXSize, nSrcYSize);
1878 : }
1879 :
1880 : /************************************************************************/
1881 : /* WarpRegionToBuffer() */
1882 : /************************************************************************/
1883 :
1884 : /**
1885 : * This method requests that a particular window of the output dataset
1886 : * be warped and the result put into the provided data buffer. The output
1887 : * dataset doesn't even really have to exist to use this method as long as
1888 : * the transformation function in the GDALWarpOptions is setup to map to
1889 : * a virtual pixel/line space.
1890 : *
1891 : * This method will do the whole region in one chunk, so be wary of the
1892 : * amount of memory that might be used.
1893 : *
1894 : * @param nDstXOff X offset to window of destination data to be produced.
1895 : * @param nDstYOff Y offset to window of destination data to be produced.
1896 : * @param nDstXSize Width of output window on destination file to be produced.
1897 : * @param nDstYSize Height of output window on destination file to be produced.
1898 : * @param pDataBuf the data buffer to place result in, of type eBufDataType.
1899 : * @param eBufDataType the type of the output data buffer. For now this
1900 : * must match GDALWarpOptions::eWorkingDataType.
1901 : * @param nSrcXOff source window X offset (computed if window all zero)
1902 : * @param nSrcYOff source window Y offset (computed if window all zero)
1903 : * @param nSrcXSize source window X size (computed if window all zero)
1904 : * @param nSrcYSize source window Y size (computed if window all zero)
1905 : * @param dfProgressBase minimum progress value reported
1906 : * @param dfProgressScale value such as dfProgressBase + dfProgressScale is the
1907 : * maximum progress value reported
1908 : *
1909 : * @return CE_None on success or CE_Failure if an error occurs.
1910 : */
1911 :
1912 1976 : CPLErr GDALWarpOperation::WarpRegionToBuffer(
1913 : int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, void *pDataBuf,
1914 : GDALDataType eBufDataType, int nSrcXOff, int nSrcYOff, int nSrcXSize,
1915 : int nSrcYSize, double dfProgressBase, double dfProgressScale)
1916 : {
1917 1976 : return WarpRegionToBuffer(nDstXOff, nDstYOff, nDstXSize, nDstYSize,
1918 : pDataBuf, eBufDataType, nSrcXOff, nSrcYOff,
1919 : nSrcXSize, nSrcYSize, 0, 0, dfProgressBase,
1920 1976 : dfProgressScale);
1921 : }
1922 :
1923 : /**
1924 : * This method requests that a particular window of the output dataset
1925 : * be warped and the result put into the provided data buffer. The output
1926 : * dataset doesn't even really have to exist to use this method as long as
1927 : * the transformation function in the GDALWarpOptions is setup to map to
1928 : * a virtual pixel/line space.
1929 : *
1930 : * This method will do the whole region in one chunk, so be wary of the
1931 : * amount of memory that might be used.
1932 : *
1933 : * @param nDstXOff X offset to window of destination data to be produced.
1934 : * @param nDstYOff Y offset to window of destination data to be produced.
1935 : * @param nDstXSize Width of output window on destination file to be produced.
1936 : * @param nDstYSize Height of output window on destination file to be produced.
1937 : * @param pDataBuf the data buffer to place result in, of type eBufDataType.
1938 : * @param eBufDataType the type of the output data buffer. For now this
1939 : * must match GDALWarpOptions::eWorkingDataType.
1940 : * @param nSrcXOff source window X offset (computed if window all zero)
1941 : * @param nSrcYOff source window Y offset (computed if window all zero)
1942 : * @param nSrcXSize source window X size (computed if window all zero)
1943 : * @param nSrcYSize source window Y size (computed if window all zero)
1944 : * @param dfSrcXExtraSize Extra pixels (included in nSrcXSize) reserved
1945 : * for filter window. Should be ignored in scale computation
1946 : * @param dfSrcYExtraSize Extra pixels (included in nSrcYSize) reserved
1947 : * for filter window. Should be ignored in scale computation
1948 : * @param dfProgressBase minimum progress value reported
1949 : * @param dfProgressScale value such as dfProgressBase + dfProgressScale is the
1950 : * maximum progress value reported
1951 : *
1952 : * @return CE_None on success or CE_Failure if an error occurs.
1953 : */
1954 :
1955 3895 : CPLErr GDALWarpOperation::WarpRegionToBuffer(
1956 : int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, void *pDataBuf,
1957 : // Only in a CPLAssert.
1958 : CPL_UNUSED GDALDataType eBufDataType, int nSrcXOff, int nSrcYOff,
1959 : int nSrcXSize, int nSrcYSize, double dfSrcXExtraSize,
1960 : double dfSrcYExtraSize, double dfProgressBase, double dfProgressScale)
1961 :
1962 : {
1963 3895 : const int nWordSize = GDALGetDataTypeSizeBytes(psOptions->eWorkingDataType);
1964 :
1965 3895 : CPLAssert(eBufDataType == psOptions->eWorkingDataType);
1966 :
1967 : /* -------------------------------------------------------------------- */
1968 : /* If not given a corresponding source window compute one now. */
1969 : /* -------------------------------------------------------------------- */
1970 3895 : if (nSrcXSize == 0 && nSrcYSize == 0)
1971 : {
1972 : // TODO: This taking of the warp mutex is suboptimal. We could get rid
1973 : // of it, but that would require making sure ComputeSourceWindow()
1974 : // uses a different pTransformerArg than the warp kernel.
1975 1777 : if (hWarpMutex != nullptr && !CPLAcquireMutex(hWarpMutex, 600.0))
1976 : {
1977 0 : CPLError(CE_Failure, CPLE_AppDefined,
1978 : "Failed to acquire WarpMutex in WarpRegion().");
1979 0 : return CE_Failure;
1980 : }
1981 : const CPLErr eErr =
1982 1777 : ComputeSourceWindow(nDstXOff, nDstYOff, nDstXSize, nDstYSize,
1983 : &nSrcXOff, &nSrcYOff, &nSrcXSize, &nSrcYSize,
1984 : &dfSrcXExtraSize, &dfSrcYExtraSize, nullptr);
1985 1777 : if (hWarpMutex != nullptr)
1986 0 : CPLReleaseMutex(hWarpMutex);
1987 1777 : if (eErr != CE_None)
1988 : {
1989 : const bool bErrorOutIfEmptySourceWindow =
1990 36 : CPLFetchBool(psOptions->papszWarpOptions,
1991 : "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", true);
1992 36 : if (!bErrorOutIfEmptySourceWindow)
1993 36 : return CE_None;
1994 0 : return eErr;
1995 : }
1996 : }
1997 :
1998 : /* -------------------------------------------------------------------- */
1999 : /* Prepare a WarpKernel object to match this operation. */
2000 : /* -------------------------------------------------------------------- */
2001 7718 : GDALWarpKernel oWK;
2002 :
2003 3859 : oWK.eResample = m_bIsTranslationOnPixelBoundaries ? GRA_NearestNeighbour
2004 3544 : : psOptions->eResampleAlg;
2005 3859 : oWK.eTieStrategy = psOptions->eTieStrategy;
2006 3859 : oWK.nBands = psOptions->nBandCount;
2007 3859 : oWK.eWorkingDataType = psOptions->eWorkingDataType;
2008 :
2009 3859 : oWK.pfnTransformer = psOptions->pfnTransformer;
2010 3859 : oWK.pTransformerArg = psOptions->pTransformerArg;
2011 :
2012 3859 : oWK.pfnProgress = psOptions->pfnProgress;
2013 3859 : oWK.pProgress = psOptions->pProgressArg;
2014 3859 : oWK.dfProgressBase = dfProgressBase;
2015 3859 : oWK.dfProgressScale = dfProgressScale;
2016 :
2017 3859 : oWK.papszWarpOptions = psOptions->papszWarpOptions;
2018 3859 : oWK.psThreadData = psThreadData;
2019 :
2020 3859 : oWK.padfDstNoDataReal = psOptions->padfDstNoDataReal;
2021 :
2022 : /* -------------------------------------------------------------------- */
2023 : /* Setup the source buffer. */
2024 : /* */
2025 : /* Eventually we may need to take advantage of pixel */
2026 : /* interleaved reading here. */
2027 : /* -------------------------------------------------------------------- */
2028 3859 : oWK.nSrcXOff = nSrcXOff;
2029 3859 : oWK.nSrcYOff = nSrcYOff;
2030 3859 : oWK.nSrcXSize = nSrcXSize;
2031 3859 : oWK.nSrcYSize = nSrcYSize;
2032 3859 : oWK.dfSrcXExtraSize = dfSrcXExtraSize;
2033 3859 : oWK.dfSrcYExtraSize = dfSrcYExtraSize;
2034 :
2035 : // Check for overflows in computation of nAlloc
2036 7115 : if (nSrcYSize > 0 &&
2037 3256 : ((static_cast<size_t>(nSrcXSize) >
2038 3256 : (std::numeric_limits<size_t>::max() - WARP_EXTRA_ELTS) / nSrcYSize) ||
2039 3256 : (static_cast<size_t>(nSrcXSize) * nSrcYSize + WARP_EXTRA_ELTS >
2040 3256 : std::numeric_limits<size_t>::max() /
2041 3256 : (nWordSize * psOptions->nBandCount))))
2042 : {
2043 0 : CPLError(CE_Failure, CPLE_AppDefined,
2044 : "WarpRegionToBuffer(): Integer overflow : nWordSize(=%d) * "
2045 : "(nSrcXSize(=%d) * nSrcYSize(=%d) + WARP_EXTRA_ELTS(=%d)) * "
2046 : "nBandCount(=%d)",
2047 : nWordSize, nSrcXSize, nSrcYSize, WARP_EXTRA_ELTS,
2048 0 : psOptions->nBandCount);
2049 0 : return CE_Failure;
2050 : }
2051 :
2052 3859 : const size_t nAlloc =
2053 3859 : nWordSize *
2054 3859 : (static_cast<size_t>(nSrcXSize) * nSrcYSize + WARP_EXTRA_ELTS) *
2055 3859 : psOptions->nBandCount;
2056 :
2057 3859 : oWK.papabySrcImage = static_cast<GByte **>(
2058 3859 : CPLCalloc(sizeof(GByte *), psOptions->nBandCount));
2059 3859 : oWK.papabySrcImage[0] = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nAlloc));
2060 :
2061 3859 : CPLErr eErr =
2062 3257 : nSrcXSize != 0 && nSrcYSize != 0 && oWK.papabySrcImage[0] == nullptr
2063 7116 : ? CE_Failure
2064 : : CE_None;
2065 :
2066 11309 : for (int i = 0; i < psOptions->nBandCount && eErr == CE_None; i++)
2067 7450 : oWK.papabySrcImage[i] =
2068 7450 : reinterpret_cast<GByte *>(oWK.papabySrcImage[0]) +
2069 7450 : nWordSize *
2070 7450 : (static_cast<GPtrDiff_t>(nSrcXSize) * nSrcYSize +
2071 7450 : WARP_EXTRA_ELTS) *
2072 7450 : i;
2073 :
2074 3859 : if (eErr == CE_None && nSrcXSize > 0 && nSrcYSize > 0)
2075 : {
2076 3246 : GDALDataset *poSrcDS = GDALDataset::FromHandle(psOptions->hSrcDS);
2077 3246 : if (psOptions->nBandCount == 1)
2078 : {
2079 : // Particular case to simplify the stack a bit.
2080 2023 : eErr = poSrcDS->GetRasterBand(psOptions->panSrcBands[0])
2081 4046 : ->RasterIO(GF_Read, nSrcXOff, nSrcYOff, nSrcXSize,
2082 2023 : nSrcYSize, oWK.papabySrcImage[0], nSrcXSize,
2083 2023 : nSrcYSize, psOptions->eWorkingDataType, 0, 0,
2084 : nullptr);
2085 : }
2086 : else
2087 : {
2088 1223 : eErr = poSrcDS->RasterIO(
2089 : GF_Read, nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize,
2090 1223 : oWK.papabySrcImage[0], nSrcXSize, nSrcYSize,
2091 1223 : psOptions->eWorkingDataType, psOptions->nBandCount,
2092 1223 : psOptions->panSrcBands, 0, 0,
2093 1223 : nWordSize * (static_cast<GPtrDiff_t>(nSrcXSize) * nSrcYSize +
2094 : WARP_EXTRA_ELTS),
2095 : nullptr);
2096 : }
2097 : }
2098 :
2099 3859 : ReportTiming("Input buffer read");
2100 :
2101 : /* -------------------------------------------------------------------- */
2102 : /* Initialize destination buffer. */
2103 : /* -------------------------------------------------------------------- */
2104 3859 : oWK.nDstXOff = nDstXOff;
2105 3859 : oWK.nDstYOff = nDstYOff;
2106 3859 : oWK.nDstXSize = nDstXSize;
2107 3859 : oWK.nDstYSize = nDstYSize;
2108 :
2109 3859 : oWK.papabyDstImage = reinterpret_cast<GByte **>(
2110 3859 : CPLCalloc(sizeof(GByte *), psOptions->nBandCount));
2111 :
2112 11301 : for (int i = 0; i < psOptions->nBandCount && eErr == CE_None; i++)
2113 : {
2114 7442 : oWK.papabyDstImage[i] =
2115 7442 : static_cast<GByte *>(pDataBuf) +
2116 7442 : i * static_cast<GPtrDiff_t>(nDstXSize) * nDstYSize * nWordSize;
2117 : }
2118 :
2119 : /* -------------------------------------------------------------------- */
2120 : /* Eventually we need handling for a whole bunch of the */
2121 : /* validity and density masks here. */
2122 : /* -------------------------------------------------------------------- */
2123 :
2124 : // TODO
2125 :
2126 : /* -------------------------------------------------------------------- */
2127 : /* Generate a source density mask if we have a source alpha band */
2128 : /* -------------------------------------------------------------------- */
2129 3859 : if (eErr == CE_None && psOptions->nSrcAlphaBand > 0 && nSrcXSize > 0 &&
2130 142 : nSrcYSize > 0)
2131 : {
2132 142 : CPLAssert(oWK.pafUnifiedSrcDensity == nullptr);
2133 :
2134 142 : eErr = CreateKernelMask(&oWK, 0 /* not used */, "UnifiedSrcDensity");
2135 :
2136 142 : if (eErr == CE_None)
2137 : {
2138 142 : int bOutAllOpaque = FALSE;
2139 284 : eErr = GDALWarpSrcAlphaMasker(
2140 142 : psOptions, psOptions->nBandCount, psOptions->eWorkingDataType,
2141 : oWK.nSrcXOff, oWK.nSrcYOff, oWK.nSrcXSize, oWK.nSrcYSize,
2142 142 : oWK.papabySrcImage, TRUE, oWK.pafUnifiedSrcDensity,
2143 : &bOutAllOpaque);
2144 142 : if (bOutAllOpaque)
2145 : {
2146 : #if DEBUG_VERBOSE
2147 : CPLDebug("WARP",
2148 : "No need for a source density mask as all values "
2149 : "are opaque");
2150 : #endif
2151 35 : CPLFree(oWK.pafUnifiedSrcDensity);
2152 35 : oWK.pafUnifiedSrcDensity = nullptr;
2153 : }
2154 : }
2155 : }
2156 :
2157 : /* -------------------------------------------------------------------- */
2158 : /* Generate a source density mask if we have a source cutline. */
2159 : /* -------------------------------------------------------------------- */
2160 3859 : if (eErr == CE_None && psOptions->hCutline != nullptr && nSrcXSize > 0 &&
2161 44 : nSrcYSize > 0)
2162 : {
2163 44 : const bool bUnifiedSrcDensityJustCreated =
2164 44 : (oWK.pafUnifiedSrcDensity == nullptr);
2165 44 : if (bUnifiedSrcDensityJustCreated)
2166 : {
2167 : eErr =
2168 44 : CreateKernelMask(&oWK, 0 /* not used */, "UnifiedSrcDensity");
2169 :
2170 44 : if (eErr == CE_None)
2171 : {
2172 44 : for (GPtrDiff_t j = 0;
2173 2929360 : j < static_cast<GPtrDiff_t>(oWK.nSrcXSize) * oWK.nSrcYSize;
2174 : j++)
2175 2929320 : oWK.pafUnifiedSrcDensity[j] = 1.0;
2176 : }
2177 : }
2178 :
2179 44 : int nValidityFlag = 0;
2180 44 : if (eErr == CE_None)
2181 44 : eErr = GDALWarpCutlineMaskerEx(
2182 44 : psOptions, psOptions->nBandCount, psOptions->eWorkingDataType,
2183 : oWK.nSrcXOff, oWK.nSrcYOff, oWK.nSrcXSize, oWK.nSrcYSize,
2184 44 : oWK.papabySrcImage, TRUE, oWK.pafUnifiedSrcDensity,
2185 : &nValidityFlag);
2186 44 : if (nValidityFlag == GCMVF_CHUNK_FULLY_WITHIN_CUTLINE &&
2187 : bUnifiedSrcDensityJustCreated)
2188 : {
2189 8 : VSIFree(oWK.pafUnifiedSrcDensity);
2190 8 : oWK.pafUnifiedSrcDensity = nullptr;
2191 : }
2192 : }
2193 :
2194 : /* -------------------------------------------------------------------- */
2195 : /* Generate a destination density mask if we have a destination */
2196 : /* alpha band. */
2197 : /* -------------------------------------------------------------------- */
2198 3859 : if (eErr == CE_None && psOptions->nDstAlphaBand > 0)
2199 : {
2200 1748 : CPLAssert(oWK.pafDstDensity == nullptr);
2201 :
2202 1748 : eErr = CreateKernelMask(&oWK, 0 /* not used */, "DstDensity");
2203 :
2204 1748 : if (eErr == CE_None)
2205 1748 : eErr = GDALWarpDstAlphaMasker(
2206 1748 : psOptions, psOptions->nBandCount, psOptions->eWorkingDataType,
2207 : oWK.nDstXOff, oWK.nDstYOff, oWK.nDstXSize, oWK.nDstYSize,
2208 1748 : oWK.papabyDstImage, TRUE, oWK.pafDstDensity);
2209 : }
2210 :
2211 : /* -------------------------------------------------------------------- */
2212 : /* If we have source nodata values create the validity mask. */
2213 : /* -------------------------------------------------------------------- */
2214 3859 : if (eErr == CE_None && psOptions->padfSrcNoDataReal != nullptr &&
2215 232 : nSrcXSize > 0 && nSrcYSize > 0)
2216 : {
2217 214 : CPLAssert(oWK.papanBandSrcValid == nullptr);
2218 :
2219 214 : bool bAllBandsAllValid = true;
2220 539 : for (int i = 0; i < psOptions->nBandCount && eErr == CE_None; i++)
2221 : {
2222 325 : eErr = CreateKernelMask(&oWK, i, "BandSrcValid");
2223 325 : if (eErr == CE_None)
2224 : {
2225 325 : double adfNoData[2] = {psOptions->padfSrcNoDataReal[i],
2226 325 : psOptions->padfSrcNoDataImag != nullptr
2227 325 : ? psOptions->padfSrcNoDataImag[i]
2228 325 : : 0.0};
2229 :
2230 325 : int bAllValid = FALSE;
2231 650 : eErr = GDALWarpNoDataMasker(
2232 325 : adfNoData, 1, psOptions->eWorkingDataType, oWK.nSrcXOff,
2233 : oWK.nSrcYOff, oWK.nSrcXSize, oWK.nSrcYSize,
2234 325 : &(oWK.papabySrcImage[i]), FALSE, oWK.papanBandSrcValid[i],
2235 : &bAllValid);
2236 325 : if (!bAllValid)
2237 188 : bAllBandsAllValid = false;
2238 : }
2239 : }
2240 :
2241 : // Optimization: if all pixels in all bands are valid,
2242 : // we don't need a mask.
2243 214 : if (bAllBandsAllValid)
2244 : {
2245 : #if DEBUG_VERBOSE
2246 : CPLDebug(
2247 : "WARP",
2248 : "No need for a source nodata mask as all values are valid");
2249 : #endif
2250 231 : for (int k = 0; k < psOptions->nBandCount; k++)
2251 136 : CPLFree(oWK.papanBandSrcValid[k]);
2252 95 : CPLFree(oWK.papanBandSrcValid);
2253 95 : oWK.papanBandSrcValid = nullptr;
2254 : }
2255 :
2256 : /* --------------------------------------------------------------------
2257 : */
2258 : /* If there's just a single band, then transfer */
2259 : /* papanBandSrcValid[0] as panUnifiedSrcValid. */
2260 : /* --------------------------------------------------------------------
2261 : */
2262 214 : if (oWK.papanBandSrcValid != nullptr && psOptions->nBandCount == 1)
2263 : {
2264 73 : oWK.panUnifiedSrcValid = oWK.papanBandSrcValid[0];
2265 73 : CPLFree(oWK.papanBandSrcValid);
2266 73 : oWK.papanBandSrcValid = nullptr;
2267 : }
2268 :
2269 : /* --------------------------------------------------------------------
2270 : */
2271 : /* Compute a unified input pixel mask if and only if all bands */
2272 : /* nodata is true. That is, we only treat a pixel as nodata if */
2273 : /* all bands match their respective nodata values. */
2274 : /* --------------------------------------------------------------------
2275 : */
2276 141 : else if (oWK.papanBandSrcValid != nullptr && eErr == CE_None)
2277 : {
2278 46 : bool bAtLeastOneBandAllValid = false;
2279 162 : for (int k = 0; k < psOptions->nBandCount; k++)
2280 : {
2281 116 : if (oWK.papanBandSrcValid[k] == nullptr)
2282 : {
2283 0 : bAtLeastOneBandAllValid = true;
2284 0 : break;
2285 : }
2286 : }
2287 :
2288 92 : const char *pszUnifiedSrcNoData = CSLFetchNameValue(
2289 46 : psOptions->papszWarpOptions, "UNIFIED_SRC_NODATA");
2290 84 : if (!bAtLeastOneBandAllValid && (pszUnifiedSrcNoData == nullptr ||
2291 38 : CPLTestBool(pszUnifiedSrcNoData)))
2292 : {
2293 45 : auto nMaskBits =
2294 45 : static_cast<GPtrDiff_t>(oWK.nSrcXSize) * oWK.nSrcYSize;
2295 :
2296 : eErr =
2297 45 : CreateKernelMask(&oWK, 0 /* not used */, "UnifiedSrcValid");
2298 :
2299 45 : if (eErr == CE_None)
2300 : {
2301 45 : CPLMaskClearAll(oWK.panUnifiedSrcValid, nMaskBits);
2302 :
2303 158 : for (int k = 0; k < psOptions->nBandCount; k++)
2304 : {
2305 113 : CPLMaskMerge(oWK.panUnifiedSrcValid,
2306 113 : oWK.papanBandSrcValid[k], nMaskBits);
2307 : }
2308 :
2309 : // If UNIFIED_SRC_NODATA is set, then we will ignore the
2310 : // individual nodata status of each band. If it is not set,
2311 : // both mechanism apply:
2312 : // - if panUnifiedSrcValid[] indicates a pixel is invalid
2313 : // (that is all its bands are at nodata), then the output
2314 : // pixel will be invalid
2315 : // - otherwise, the status band per band will be check with
2316 : // papanBandSrcValid[iBand][], and the output pixel will
2317 : // be valid
2318 45 : if (pszUnifiedSrcNoData != nullptr &&
2319 37 : !EQUAL(pszUnifiedSrcNoData, "PARTIAL"))
2320 : {
2321 123 : for (int k = 0; k < psOptions->nBandCount; k++)
2322 87 : CPLFree(oWK.papanBandSrcValid[k]);
2323 36 : CPLFree(oWK.papanBandSrcValid);
2324 36 : oWK.papanBandSrcValid = nullptr;
2325 : }
2326 : }
2327 : }
2328 : }
2329 : }
2330 :
2331 : /* -------------------------------------------------------------------- */
2332 : /* Generate a source validity mask if we have a source mask for */
2333 : /* the whole input dataset (and didn't already treat it as */
2334 : /* alpha band). */
2335 : /* -------------------------------------------------------------------- */
2336 : GDALRasterBandH hSrcBand =
2337 3859 : psOptions->nBandCount < 1
2338 3859 : ? nullptr
2339 3859 : : GDALGetRasterBand(psOptions->hSrcDS, psOptions->panSrcBands[0]);
2340 :
2341 3855 : if (eErr == CE_None && oWK.pafUnifiedSrcDensity == nullptr &&
2342 3712 : oWK.panUnifiedSrcValid == nullptr && psOptions->nSrcAlphaBand <= 0 &&
2343 3531 : (GDALGetMaskFlags(hSrcBand) & GMF_PER_DATASET)
2344 : // Need to double check for -nosrcalpha case.
2345 5 : && !(GDALGetMaskFlags(hSrcBand) & GMF_ALPHA) &&
2346 7716 : psOptions->padfSrcNoDataReal == nullptr && nSrcXSize > 0 &&
2347 2 : nSrcYSize > 0)
2348 :
2349 : {
2350 2 : eErr = CreateKernelMask(&oWK, 0 /* not used */, "UnifiedSrcValid");
2351 :
2352 2 : if (eErr == CE_None)
2353 2 : eErr = GDALWarpSrcMaskMasker(
2354 2 : psOptions, psOptions->nBandCount, psOptions->eWorkingDataType,
2355 : oWK.nSrcXOff, oWK.nSrcYOff, oWK.nSrcXSize, oWK.nSrcYSize,
2356 2 : oWK.papabySrcImage, FALSE, oWK.panUnifiedSrcValid);
2357 : }
2358 :
2359 : /* -------------------------------------------------------------------- */
2360 : /* If we have destination nodata values create the */
2361 : /* validity mask. We set the DstValid for any pixel that we */
2362 : /* do no have valid data in *any* of the source bands. */
2363 : /* */
2364 : /* Note that we don't support any concept of unified nodata on */
2365 : /* the destination image. At some point that should be added */
2366 : /* and then this logic will be significantly different. */
2367 : /* -------------------------------------------------------------------- */
2368 3859 : if (eErr == CE_None && psOptions->padfDstNoDataReal != nullptr)
2369 : {
2370 587 : CPLAssert(oWK.panDstValid == nullptr);
2371 :
2372 587 : const GPtrDiff_t nMaskBits =
2373 587 : static_cast<GPtrDiff_t>(oWK.nDstXSize) * oWK.nDstYSize;
2374 :
2375 587 : eErr = CreateKernelMask(&oWK, 0 /* not used */, "DstValid");
2376 : GUInt32 *panBandMask =
2377 587 : eErr == CE_None ? CPLMaskCreate(nMaskBits, true) : nullptr;
2378 :
2379 587 : if (eErr == CE_None && panBandMask != nullptr)
2380 : {
2381 1241 : for (int iBand = 0; iBand < psOptions->nBandCount; iBand++)
2382 : {
2383 712 : CPLMaskSetAll(panBandMask, nMaskBits);
2384 :
2385 712 : double adfNoData[2] = {psOptions->padfDstNoDataReal[iBand],
2386 712 : psOptions->padfDstNoDataImag != nullptr
2387 712 : ? psOptions->padfDstNoDataImag[iBand]
2388 712 : : 0.0};
2389 :
2390 712 : int bAllValid = FALSE;
2391 1424 : eErr = GDALWarpNoDataMasker(
2392 712 : adfNoData, 1, psOptions->eWorkingDataType, oWK.nDstXOff,
2393 : oWK.nDstYOff, oWK.nDstXSize, oWK.nDstYSize,
2394 712 : oWK.papabyDstImage + iBand, FALSE, panBandMask, &bAllValid);
2395 :
2396 : // Optimization: if there's a single band and all pixels are
2397 : // valid then we don't need a mask.
2398 712 : if (bAllValid && psOptions->nBandCount == 1)
2399 : {
2400 : #if DEBUG_VERBOSE
2401 : CPLDebug("WARP", "No need for a destination nodata mask as "
2402 : "all values are valid");
2403 : #endif
2404 58 : CPLFree(oWK.panDstValid);
2405 58 : oWK.panDstValid = nullptr;
2406 58 : break;
2407 : }
2408 :
2409 654 : CPLMaskMerge(oWK.panDstValid, panBandMask, nMaskBits);
2410 : }
2411 587 : CPLFree(panBandMask);
2412 : }
2413 : }
2414 :
2415 : /* -------------------------------------------------------------------- */
2416 : /* Release IO Mutex, and acquire warper mutex. */
2417 : /* -------------------------------------------------------------------- */
2418 3859 : if (hIOMutex != nullptr)
2419 : {
2420 11 : CPLReleaseMutex(hIOMutex);
2421 11 : if (!CPLAcquireMutex(hWarpMutex, 600.0))
2422 : {
2423 0 : CPLError(CE_Failure, CPLE_AppDefined,
2424 : "Failed to acquire WarpMutex in WarpRegion().");
2425 0 : return CE_Failure;
2426 : }
2427 : }
2428 :
2429 : /* -------------------------------------------------------------------- */
2430 : /* Optional application provided prewarp chunk processor. */
2431 : /* -------------------------------------------------------------------- */
2432 3859 : if (eErr == CE_None && psOptions->pfnPreWarpChunkProcessor != nullptr)
2433 0 : eErr = psOptions->pfnPreWarpChunkProcessor(
2434 0 : &oWK, psOptions->pPreWarpProcessorArg);
2435 :
2436 : /* -------------------------------------------------------------------- */
2437 : /* Perform the warp. */
2438 : /* -------------------------------------------------------------------- */
2439 3859 : if (eErr == CE_None)
2440 : {
2441 3855 : eErr = oWK.PerformWarp();
2442 3855 : ReportTiming("In memory warp operation");
2443 : }
2444 :
2445 : /* -------------------------------------------------------------------- */
2446 : /* Optional application provided postwarp chunk processor. */
2447 : /* -------------------------------------------------------------------- */
2448 3859 : if (eErr == CE_None && psOptions->pfnPostWarpChunkProcessor != nullptr)
2449 0 : eErr = psOptions->pfnPostWarpChunkProcessor(
2450 0 : &oWK, psOptions->pPostWarpProcessorArg);
2451 :
2452 : /* -------------------------------------------------------------------- */
2453 : /* Release Warp Mutex, and acquire io mutex. */
2454 : /* -------------------------------------------------------------------- */
2455 3859 : if (hIOMutex != nullptr)
2456 : {
2457 11 : CPLReleaseMutex(hWarpMutex);
2458 11 : if (!CPLAcquireMutex(hIOMutex, 600.0))
2459 : {
2460 0 : CPLError(CE_Failure, CPLE_AppDefined,
2461 : "Failed to acquire IOMutex in WarpRegion().");
2462 0 : return CE_Failure;
2463 : }
2464 : }
2465 :
2466 : /* -------------------------------------------------------------------- */
2467 : /* Write destination alpha if available. */
2468 : /* -------------------------------------------------------------------- */
2469 3859 : if (eErr == CE_None && psOptions->nDstAlphaBand > 0)
2470 : {
2471 1748 : eErr = GDALWarpDstAlphaMasker(
2472 1748 : psOptions, -psOptions->nBandCount, psOptions->eWorkingDataType,
2473 : oWK.nDstXOff, oWK.nDstYOff, oWK.nDstXSize, oWK.nDstYSize,
2474 1748 : oWK.papabyDstImage, TRUE, oWK.pafDstDensity);
2475 : }
2476 :
2477 : /* -------------------------------------------------------------------- */
2478 : /* Cleanup. */
2479 : /* -------------------------------------------------------------------- */
2480 3859 : CPLFree(oWK.papabySrcImage[0]);
2481 3859 : CPLFree(oWK.papabySrcImage);
2482 3859 : CPLFree(oWK.papabyDstImage);
2483 :
2484 3859 : if (oWK.papanBandSrcValid != nullptr)
2485 : {
2486 39 : for (int i = 0; i < oWK.nBands; i++)
2487 29 : CPLFree(oWK.papanBandSrcValid[i]);
2488 10 : CPLFree(oWK.papanBandSrcValid);
2489 : }
2490 3859 : CPLFree(oWK.panUnifiedSrcValid);
2491 3859 : CPLFree(oWK.pafUnifiedSrcDensity);
2492 3859 : CPLFree(oWK.panDstValid);
2493 3859 : CPLFree(oWK.pafDstDensity);
2494 :
2495 3859 : return eErr;
2496 : }
2497 :
2498 : /************************************************************************/
2499 : /* GDALWarpRegionToBuffer() */
2500 : /************************************************************************/
2501 :
2502 : /**
2503 : * @see GDALWarpOperation::WarpRegionToBuffer()
2504 : */
2505 :
2506 0 : CPLErr GDALWarpRegionToBuffer(GDALWarpOperationH hOperation, int nDstXOff,
2507 : int nDstYOff, int nDstXSize, int nDstYSize,
2508 : void *pDataBuf, GDALDataType eBufDataType,
2509 : int nSrcXOff, int nSrcYOff, int nSrcXSize,
2510 : int nSrcYSize)
2511 :
2512 : {
2513 0 : VALIDATE_POINTER1(hOperation, "GDALWarpRegionToBuffer", CE_Failure);
2514 :
2515 : return reinterpret_cast<GDALWarpOperation *>(hOperation)
2516 0 : ->WarpRegionToBuffer(nDstXOff, nDstYOff, nDstXSize, nDstYSize, pDataBuf,
2517 : eBufDataType, nSrcXOff, nSrcYOff, nSrcXSize,
2518 0 : nSrcYSize);
2519 : }
2520 :
2521 : /************************************************************************/
2522 : /* CreateKernelMask() */
2523 : /* */
2524 : /* If mask does not yet exist, create it. Supported types are */
2525 : /* the name of the variable in question. That is */
2526 : /* "BandSrcValid", "UnifiedSrcValid", "UnifiedSrcDensity", */
2527 : /* "DstValid", and "DstDensity". */
2528 : /************************************************************************/
2529 :
2530 2893 : CPLErr GDALWarpOperation::CreateKernelMask(GDALWarpKernel *poKernel, int iBand,
2531 : const char *pszType)
2532 :
2533 : {
2534 2893 : void **ppMask = nullptr;
2535 2893 : int nXSize = 0;
2536 2893 : int nYSize = 0;
2537 2893 : int nBitsPerPixel = 0;
2538 2893 : int nDefault = 0;
2539 2893 : int nExtraElts = 0;
2540 2893 : bool bDoMemset = true;
2541 :
2542 : /* -------------------------------------------------------------------- */
2543 : /* Get particulars of mask to be updated. */
2544 : /* -------------------------------------------------------------------- */
2545 2893 : if (EQUAL(pszType, "BandSrcValid"))
2546 : {
2547 325 : if (poKernel->papanBandSrcValid == nullptr)
2548 214 : poKernel->papanBandSrcValid = static_cast<GUInt32 **>(
2549 214 : CPLCalloc(sizeof(void *), poKernel->nBands));
2550 :
2551 325 : ppMask =
2552 325 : reinterpret_cast<void **>(&(poKernel->papanBandSrcValid[iBand]));
2553 325 : nExtraElts = WARP_EXTRA_ELTS;
2554 325 : nXSize = poKernel->nSrcXSize;
2555 325 : nYSize = poKernel->nSrcYSize;
2556 325 : nBitsPerPixel = 1;
2557 325 : nDefault = 0xff;
2558 : }
2559 2568 : else if (EQUAL(pszType, "UnifiedSrcValid"))
2560 : {
2561 47 : ppMask = reinterpret_cast<void **>(&(poKernel->panUnifiedSrcValid));
2562 47 : nExtraElts = WARP_EXTRA_ELTS;
2563 47 : nXSize = poKernel->nSrcXSize;
2564 47 : nYSize = poKernel->nSrcYSize;
2565 47 : nBitsPerPixel = 1;
2566 47 : nDefault = 0xff;
2567 : }
2568 2521 : else if (EQUAL(pszType, "UnifiedSrcDensity"))
2569 : {
2570 186 : ppMask = reinterpret_cast<void **>(&(poKernel->pafUnifiedSrcDensity));
2571 186 : nExtraElts = WARP_EXTRA_ELTS;
2572 186 : nXSize = poKernel->nSrcXSize;
2573 186 : nYSize = poKernel->nSrcYSize;
2574 186 : nBitsPerPixel = 32;
2575 186 : nDefault = 0;
2576 186 : bDoMemset = false;
2577 : }
2578 2335 : else if (EQUAL(pszType, "DstValid"))
2579 : {
2580 587 : ppMask = reinterpret_cast<void **>(&(poKernel->panDstValid));
2581 587 : nXSize = poKernel->nDstXSize;
2582 587 : nYSize = poKernel->nDstYSize;
2583 587 : nBitsPerPixel = 1;
2584 587 : nDefault = 0;
2585 : }
2586 1748 : else if (EQUAL(pszType, "DstDensity"))
2587 : {
2588 1748 : ppMask = reinterpret_cast<void **>(&(poKernel->pafDstDensity));
2589 1748 : nXSize = poKernel->nDstXSize;
2590 1748 : nYSize = poKernel->nDstYSize;
2591 1748 : nBitsPerPixel = 32;
2592 1748 : nDefault = 0;
2593 1748 : bDoMemset = false;
2594 : }
2595 : else
2596 : {
2597 0 : CPLError(CE_Failure, CPLE_AppDefined,
2598 : "Internal error in CreateKernelMask(%s).", pszType);
2599 0 : return CE_Failure;
2600 : }
2601 :
2602 : /* -------------------------------------------------------------------- */
2603 : /* Allocate if needed. */
2604 : /* -------------------------------------------------------------------- */
2605 2893 : if (*ppMask == nullptr)
2606 : {
2607 2893 : const GIntBig nBytes =
2608 : nBitsPerPixel == 32
2609 2893 : ? (static_cast<GIntBig>(nXSize) * nYSize + nExtraElts) * 4
2610 959 : : (static_cast<GIntBig>(nXSize) * nYSize + nExtraElts + 31) / 8;
2611 :
2612 2893 : const size_t nByteSize_t = static_cast<size_t>(nBytes);
2613 : #if SIZEOF_VOIDP == 4
2614 : if (static_cast<GIntBig>(nByteSize_t) != nBytes)
2615 : {
2616 : CPLError(CE_Failure, CPLE_OutOfMemory,
2617 : "Cannot allocate " CPL_FRMT_GIB " bytes", nBytes);
2618 : return CE_Failure;
2619 : }
2620 : #endif
2621 :
2622 2893 : *ppMask = VSI_MALLOC_VERBOSE(nByteSize_t);
2623 :
2624 2893 : if (*ppMask == nullptr)
2625 : {
2626 0 : return CE_Failure;
2627 : }
2628 :
2629 2893 : if (bDoMemset)
2630 959 : memset(*ppMask, nDefault, nByteSize_t);
2631 : }
2632 :
2633 2893 : return CE_None;
2634 : }
2635 :
2636 : /************************************************************************/
2637 : /* ComputeSourceWindowStartingFromSource() */
2638 : /************************************************************************/
2639 :
2640 : constexpr int DEFAULT_STEP_COUNT = 21;
2641 :
2642 1260 : void GDALWarpOperation::ComputeSourceWindowStartingFromSource(
2643 : int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize,
2644 : double *padfSrcMinX, double *padfSrcMinY, double *padfSrcMaxX,
2645 : double *padfSrcMaxY)
2646 : {
2647 1260 : const int nSrcRasterXSize = GDALGetRasterXSize(psOptions->hSrcDS);
2648 1260 : const int nSrcRasterYSize = GDALGetRasterYSize(psOptions->hSrcDS);
2649 1260 : if (nSrcRasterXSize == 0 || nSrcRasterYSize == 0)
2650 0 : return;
2651 :
2652 1260 : GDALWarpPrivateData *privateData = GetWarpPrivateData(this);
2653 1260 : if (privateData->nStepCount == 0)
2654 : {
2655 312 : int nStepCount = DEFAULT_STEP_COUNT;
2656 312 : std::vector<double> adfDstZ{};
2657 :
2658 : const char *pszSampleSteps =
2659 312 : CSLFetchNameValue(psOptions->papszWarpOptions, "SAMPLE_STEPS");
2660 312 : constexpr int knIntMax = std::numeric_limits<int>::max();
2661 312 : if (pszSampleSteps && !EQUAL(pszSampleSteps, "ALL"))
2662 : {
2663 0 : nStepCount = atoi(
2664 0 : CSLFetchNameValue(psOptions->papszWarpOptions, "SAMPLE_STEPS"));
2665 0 : nStepCount = std::max(2, nStepCount);
2666 : }
2667 :
2668 312 : const double dfStepSize = 1.0 / (nStepCount - 1);
2669 312 : if (nStepCount > knIntMax - 2 ||
2670 312 : (nStepCount + 2) > knIntMax / (nStepCount + 2))
2671 : {
2672 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many steps : %d",
2673 : nStepCount);
2674 0 : return;
2675 : }
2676 312 : const int nSampleMax = (nStepCount + 2) * (nStepCount + 2);
2677 :
2678 : try
2679 : {
2680 312 : privateData->abSuccess.resize(nSampleMax);
2681 312 : privateData->adfDstX.resize(nSampleMax);
2682 312 : privateData->adfDstY.resize(nSampleMax);
2683 312 : adfDstZ.resize(nSampleMax);
2684 : }
2685 0 : catch (const std::exception &)
2686 : {
2687 0 : return;
2688 : }
2689 :
2690 : /* --------------------------------------------------------------------
2691 : */
2692 : /* Setup sample points on a grid pattern throughout the source */
2693 : /* raster. */
2694 : /* --------------------------------------------------------------------
2695 : */
2696 312 : int iPoint = 0;
2697 7488 : for (int iY = 0; iY < nStepCount + 2; iY++)
2698 : {
2699 14040 : const double dfRatioY = (iY == 0) ? 0.5 / nSrcRasterYSize
2700 6864 : : (iY <= nStepCount)
2701 6864 : ? (iY - 1) * dfStepSize
2702 312 : : 1 - 0.5 / nSrcRasterYSize;
2703 172224 : for (int iX = 0; iX < nStepCount + 2; iX++)
2704 : {
2705 322920 : const double dfRatioX = (iX == 0) ? 0.5 / nSrcRasterXSize
2706 157872 : : (iX <= nStepCount)
2707 157872 : ? (iX - 1) * dfStepSize
2708 7176 : : 1 - 0.5 / nSrcRasterXSize;
2709 165048 : privateData->adfDstX[iPoint] = dfRatioX * nSrcRasterXSize;
2710 165048 : privateData->adfDstY[iPoint] = dfRatioY * nSrcRasterYSize;
2711 165048 : iPoint++;
2712 : }
2713 : }
2714 312 : CPLAssert(iPoint == nSampleMax);
2715 :
2716 : /* --------------------------------------------------------------------
2717 : */
2718 : /* Transform them to the output pixel coordinate space */
2719 : /* --------------------------------------------------------------------
2720 : */
2721 312 : psOptions->pfnTransformer(psOptions->pTransformerArg, FALSE, nSampleMax,
2722 : privateData->adfDstX.data(),
2723 : privateData->adfDstY.data(), adfDstZ.data(),
2724 : privateData->abSuccess.data());
2725 312 : privateData->nStepCount = nStepCount;
2726 : }
2727 :
2728 : /* -------------------------------------------------------------------- */
2729 : /* Collect the bounds, ignoring any failed points. */
2730 : /* -------------------------------------------------------------------- */
2731 1260 : const int nStepCount = privateData->nStepCount;
2732 1260 : const double dfStepSize = 1.0 / (nStepCount - 1);
2733 1260 : int iPoint = 0;
2734 : #ifdef DEBUG
2735 1260 : const size_t nSampleMax =
2736 1260 : static_cast<size_t>(nStepCount + 2) * (nStepCount + 2);
2737 1260 : CPL_IGNORE_RET_VAL(nSampleMax);
2738 1260 : CPLAssert(privateData->adfDstX.size() == nSampleMax);
2739 1260 : CPLAssert(privateData->adfDstY.size() == nSampleMax);
2740 1260 : CPLAssert(privateData->abSuccess.size() == nSampleMax);
2741 : #endif
2742 30240 : for (int iY = 0; iY < nStepCount + 2; iY++)
2743 : {
2744 56700 : const double dfRatioY = (iY == 0) ? 0.5 / nSrcRasterYSize
2745 : : (iY <= nStepCount)
2746 27720 : ? (iY - 1) * dfStepSize
2747 1260 : : 1 - 0.5 / nSrcRasterYSize;
2748 695520 : for (int iX = 0; iX < nStepCount + 2; iX++)
2749 : {
2750 666540 : if (privateData->abSuccess[iPoint] &&
2751 631068 : privateData->adfDstX[iPoint] >= nDstXOff &&
2752 458825 : privateData->adfDstX[iPoint] <= nDstXOff + nDstXSize &&
2753 1528080 : privateData->adfDstY[iPoint] >= nDstYOff &&
2754 230469 : privateData->adfDstY[iPoint] <= nDstYOff + nDstYSize)
2755 : {
2756 343563 : const double dfRatioX = (iX == 0) ? 0.5 / nSrcRasterXSize
2757 : : (iX <= nStepCount)
2758 168009 : ? (iX - 1) * dfStepSize
2759 7543 : : 1 - 0.5 / nSrcRasterXSize;
2760 175554 : double dfSrcX = dfRatioX * nSrcRasterXSize;
2761 175554 : double dfSrcY = dfRatioY * nSrcRasterYSize;
2762 175554 : *padfSrcMinX = std::min(*padfSrcMinX, dfSrcX);
2763 175554 : *padfSrcMinY = std::min(*padfSrcMinY, dfSrcY);
2764 175554 : *padfSrcMaxX = std::max(*padfSrcMaxX, dfSrcX);
2765 175554 : *padfSrcMaxY = std::max(*padfSrcMaxY, dfSrcY);
2766 : }
2767 666540 : iPoint++;
2768 : }
2769 : }
2770 : }
2771 :
2772 : /************************************************************************/
2773 : /* ComputeSourceWindowTransformPoints() */
2774 : /************************************************************************/
2775 :
2776 6703 : bool GDALWarpOperation::ComputeSourceWindowTransformPoints(
2777 : int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, bool bUseGrid,
2778 : bool bAll, int nStepCount, bool bTryWithCheckWithInvertProj,
2779 : double &dfMinXOut, double &dfMinYOut, double &dfMaxXOut, double &dfMaxYOut,
2780 : int &nSamplePoints, int &nFailedCount)
2781 : {
2782 6703 : nSamplePoints = 0;
2783 6703 : nFailedCount = 0;
2784 :
2785 6703 : const double dfStepSize = bAll ? 0 : 1.0 / (nStepCount - 1);
2786 6703 : constexpr int knIntMax = std::numeric_limits<int>::max();
2787 6703 : int nSampleMax = 0;
2788 6703 : if (bUseGrid)
2789 : {
2790 1322 : if (bAll)
2791 : {
2792 0 : if (nDstXSize > knIntMax - 1 ||
2793 0 : nDstYSize > knIntMax / (nDstXSize + 1) - 1)
2794 : {
2795 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many steps");
2796 0 : return false;
2797 : }
2798 0 : nSampleMax = (nDstXSize + 1) * (nDstYSize + 1);
2799 : }
2800 : else
2801 : {
2802 1322 : if (nStepCount > knIntMax - 2 ||
2803 1322 : (nStepCount + 2) > knIntMax / (nStepCount + 2))
2804 : {
2805 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many steps : %d",
2806 : nStepCount);
2807 0 : return false;
2808 : }
2809 1322 : nSampleMax = (nStepCount + 2) * (nStepCount + 2);
2810 : }
2811 : }
2812 : else
2813 : {
2814 5381 : if (bAll)
2815 : {
2816 160 : if (nDstXSize > knIntMax / 2 - nDstYSize)
2817 : {
2818 : // Extremely unlikely !
2819 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many steps");
2820 0 : return false;
2821 : }
2822 160 : nSampleMax = 2 * (nDstXSize + nDstYSize);
2823 : }
2824 : else
2825 : {
2826 5221 : if (nStepCount > knIntMax / 4)
2827 : {
2828 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too many steps : %d * 4",
2829 : nStepCount);
2830 0 : return false;
2831 : }
2832 5221 : nSampleMax = nStepCount * 4;
2833 : }
2834 : }
2835 :
2836 : int *pabSuccess =
2837 6703 : static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nSampleMax));
2838 : double *padfX = static_cast<double *>(
2839 6703 : VSI_MALLOC2_VERBOSE(sizeof(double) * 3, nSampleMax));
2840 6703 : if (pabSuccess == nullptr || padfX == nullptr)
2841 : {
2842 0 : CPLFree(padfX);
2843 0 : CPLFree(pabSuccess);
2844 0 : return false;
2845 : }
2846 6703 : double *padfY = padfX + nSampleMax;
2847 6703 : double *padfZ = padfX + static_cast<size_t>(nSampleMax) * 2;
2848 :
2849 : /* -------------------------------------------------------------------- */
2850 : /* Setup sample points on a grid pattern throughout the area. */
2851 : /* -------------------------------------------------------------------- */
2852 6703 : if (bUseGrid)
2853 : {
2854 1322 : if (bAll)
2855 : {
2856 0 : for (int iY = 0; iY <= nDstYSize; ++iY)
2857 : {
2858 0 : for (int iX = 0; iX <= nDstXSize; ++iX)
2859 : {
2860 0 : padfX[nSamplePoints] = nDstXOff + iX;
2861 0 : padfY[nSamplePoints] = nDstYOff + iY;
2862 0 : padfZ[nSamplePoints++] = 0.0;
2863 : }
2864 : }
2865 : }
2866 : else
2867 : {
2868 31728 : for (int iY = 0; iY < nStepCount + 2; iY++)
2869 : {
2870 59490 : const double dfRatioY = (iY == 0) ? 0.5 / nDstXSize
2871 : : (iY <= nStepCount)
2872 29084 : ? (iY - 1) * dfStepSize
2873 1322 : : 1 - 0.5 / nDstXSize;
2874 729744 : for (int iX = 0; iX < nStepCount + 2; iX++)
2875 : {
2876 1368270 : const double dfRatioX = (iX == 0) ? 0.5 / nDstXSize
2877 : : (iX <= nStepCount)
2878 668932 : ? (iX - 1) * dfStepSize
2879 30406 : : 1 - 0.5 / nDstXSize;
2880 699338 : padfX[nSamplePoints] = dfRatioX * nDstXSize + nDstXOff;
2881 699338 : padfY[nSamplePoints] = dfRatioY * nDstYSize + nDstYOff;
2882 699338 : padfZ[nSamplePoints++] = 0.0;
2883 : }
2884 : }
2885 : }
2886 : }
2887 : /* -------------------------------------------------------------------- */
2888 : /* Setup sample points all around the edge of the output raster. */
2889 : /* -------------------------------------------------------------------- */
2890 : else
2891 : {
2892 5381 : if (bAll)
2893 : {
2894 68927 : for (int iX = 0; iX <= nDstXSize; ++iX)
2895 : {
2896 : // Along top
2897 68767 : padfX[nSamplePoints] = nDstXOff + iX;
2898 68767 : padfY[nSamplePoints] = nDstYOff;
2899 68767 : padfZ[nSamplePoints++] = 0.0;
2900 :
2901 : // Along bottom
2902 68767 : padfX[nSamplePoints] = nDstXOff + iX;
2903 68767 : padfY[nSamplePoints] = nDstYOff + nDstYSize;
2904 68767 : padfZ[nSamplePoints++] = 0.0;
2905 : }
2906 :
2907 44154 : for (int iY = 1; iY < nDstYSize; ++iY)
2908 : {
2909 : // Along left
2910 43994 : padfX[nSamplePoints] = nDstXOff;
2911 43994 : padfY[nSamplePoints] = nDstYOff + iY;
2912 43994 : padfZ[nSamplePoints++] = 0.0;
2913 :
2914 : // Along right
2915 43994 : padfX[nSamplePoints] = nDstXOff + nDstXSize;
2916 43994 : padfY[nSamplePoints] = nDstYOff + iY;
2917 43994 : padfZ[nSamplePoints++] = 0.0;
2918 : }
2919 : }
2920 : else
2921 : {
2922 114862 : for (double dfRatio = 0.0; dfRatio <= 1.0 + dfStepSize * 0.5;
2923 109641 : dfRatio += dfStepSize)
2924 : {
2925 : // Along top
2926 109641 : padfX[nSamplePoints] = dfRatio * nDstXSize + nDstXOff;
2927 109641 : padfY[nSamplePoints] = nDstYOff;
2928 109641 : padfZ[nSamplePoints++] = 0.0;
2929 :
2930 : // Along bottom
2931 109641 : padfX[nSamplePoints] = dfRatio * nDstXSize + nDstXOff;
2932 109641 : padfY[nSamplePoints] = nDstYOff + nDstYSize;
2933 109641 : padfZ[nSamplePoints++] = 0.0;
2934 :
2935 : // Along left
2936 109641 : padfX[nSamplePoints] = nDstXOff;
2937 109641 : padfY[nSamplePoints] = dfRatio * nDstYSize + nDstYOff;
2938 109641 : padfZ[nSamplePoints++] = 0.0;
2939 :
2940 : // Along right
2941 109641 : padfX[nSamplePoints] = nDstXSize + nDstXOff;
2942 109641 : padfY[nSamplePoints] = dfRatio * nDstYSize + nDstYOff;
2943 109641 : padfZ[nSamplePoints++] = 0.0;
2944 : }
2945 : }
2946 : }
2947 :
2948 6703 : CPLAssert(nSamplePoints == nSampleMax);
2949 :
2950 : /* -------------------------------------------------------------------- */
2951 : /* Transform them to the input pixel coordinate space */
2952 : /* -------------------------------------------------------------------- */
2953 :
2954 138 : const auto RefreshTransformer = [this]()
2955 : {
2956 46 : if (GDALIsTransformer(psOptions->pTransformerArg,
2957 : GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME))
2958 : {
2959 0 : GDALRefreshGenImgProjTransformer(psOptions->pTransformerArg);
2960 : }
2961 46 : else if (GDALIsTransformer(psOptions->pTransformerArg,
2962 : GDAL_APPROX_TRANSFORMER_CLASS_NAME))
2963 : {
2964 46 : GDALRefreshApproxTransformer(psOptions->pTransformerArg);
2965 : }
2966 6749 : };
2967 :
2968 6703 : if (bTryWithCheckWithInvertProj)
2969 : {
2970 23 : CPLSetThreadLocalConfigOption("CHECK_WITH_INVERT_PROJ", "YES");
2971 23 : RefreshTransformer();
2972 : }
2973 6703 : psOptions->pfnTransformer(psOptions->pTransformerArg, TRUE, nSamplePoints,
2974 : padfX, padfY, padfZ, pabSuccess);
2975 6703 : if (bTryWithCheckWithInvertProj)
2976 : {
2977 23 : CPLSetThreadLocalConfigOption("CHECK_WITH_INVERT_PROJ", nullptr);
2978 23 : RefreshTransformer();
2979 : }
2980 :
2981 : /* -------------------------------------------------------------------- */
2982 : /* Collect the bounds, ignoring any failed points. */
2983 : /* -------------------------------------------------------------------- */
2984 1370130 : for (int i = 0; i < nSamplePoints; i++)
2985 : {
2986 1363420 : if (!pabSuccess[i])
2987 : {
2988 112179 : nFailedCount++;
2989 112179 : continue;
2990 : }
2991 :
2992 : // If this happens this is likely the symptom of a bug somewhere.
2993 1251240 : if (std::isnan(padfX[i]) || std::isnan(padfY[i]))
2994 : {
2995 : static bool bNanCoordFound = false;
2996 0 : if (!bNanCoordFound)
2997 : {
2998 0 : CPLDebug("WARP",
2999 : "ComputeSourceWindow(): "
3000 : "NaN coordinate found on point %d.",
3001 : i);
3002 0 : bNanCoordFound = true;
3003 : }
3004 0 : nFailedCount++;
3005 0 : continue;
3006 : }
3007 :
3008 1251240 : dfMinXOut = std::min(dfMinXOut, padfX[i]);
3009 1251240 : dfMinYOut = std::min(dfMinYOut, padfY[i]);
3010 1251240 : dfMaxXOut = std::max(dfMaxXOut, padfX[i]);
3011 1251240 : dfMaxYOut = std::max(dfMaxYOut, padfY[i]);
3012 : }
3013 :
3014 6703 : CPLFree(padfX);
3015 6703 : CPLFree(pabSuccess);
3016 6703 : return true;
3017 : }
3018 :
3019 : /************************************************************************/
3020 : /* ComputeSourceWindow() */
3021 : /************************************************************************/
3022 :
3023 : /** Given a target window starting at pixel (nDstOff, nDstYOff) and of
3024 : * dimension (nDstXSize, nDstYSize), compute the corresponding window in
3025 : * the source raster, and return the source position in (*pnSrcXOff, *pnSrcYOff),
3026 : * the source dimension in (*pnSrcXSize, *pnSrcYSize).
3027 : * If pdfSrcXExtraSize is not null, its pointed value will be filled with the
3028 : * number of extra source pixels in X dimension to acquire to take into account
3029 : * the size of the resampling kernel. Similarly for pdfSrcYExtraSize for the
3030 : * Y dimension.
3031 : * If pdfSrcFillRatio is not null, its pointed value will be filled with the
3032 : * the ratio of the clamped source raster window size over the unclamped source
3033 : * raster window size. When this ratio is too low, this might be an indication
3034 : * that it might be beneficial to split the target window to avoid requesting
3035 : * too many source pixels.
3036 : */
3037 6382 : CPLErr GDALWarpOperation::ComputeSourceWindow(
3038 : int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, int *pnSrcXOff,
3039 : int *pnSrcYOff, int *pnSrcXSize, int *pnSrcYSize, double *pdfSrcXExtraSize,
3040 : double *pdfSrcYExtraSize, double *pdfSrcFillRatio)
3041 :
3042 : {
3043 : /* -------------------------------------------------------------------- */
3044 : /* Figure out whether we just want to do the usual "along the */
3045 : /* edge" sampling, or using a grid. The grid usage is */
3046 : /* important in some weird "inside out" cases like WGS84 to */
3047 : /* polar stereographic around the pole. Also figure out the */
3048 : /* sampling rate. */
3049 : /* -------------------------------------------------------------------- */
3050 6382 : int nStepCount = DEFAULT_STEP_COUNT;
3051 6382 : bool bAll = false;
3052 :
3053 : bool bUseGrid =
3054 6382 : CPLFetchBool(psOptions->papszWarpOptions, "SAMPLE_GRID", false);
3055 :
3056 : const char *pszSampleSteps =
3057 6382 : CSLFetchNameValue(psOptions->papszWarpOptions, "SAMPLE_STEPS");
3058 6382 : if (pszSampleSteps)
3059 : {
3060 94 : if (EQUAL(pszSampleSteps, "ALL"))
3061 : {
3062 94 : bAll = true;
3063 : }
3064 : else
3065 : {
3066 0 : nStepCount = atoi(pszSampleSteps);
3067 0 : nStepCount = std::max(2, nStepCount);
3068 : }
3069 : }
3070 6288 : else if (!bUseGrid)
3071 : {
3072 : // Detect if at least one of the 4 corner in destination raster fails
3073 : // to project back to source.
3074 : // Helps for long-lat to orthographic on areas that are partly in
3075 : // space / partly on Earth. Cf https://github.com/OSGeo/gdal/issues/9056
3076 : double adfCornerX[4];
3077 : double adfCornerY[4];
3078 5287 : double adfCornerZ[4] = {0, 0, 0, 0};
3079 5287 : int anCornerSuccess[4] = {FALSE, FALSE, FALSE, FALSE};
3080 5287 : adfCornerX[0] = nDstXOff;
3081 5287 : adfCornerY[0] = nDstYOff;
3082 5287 : adfCornerX[1] = nDstXOff + nDstXSize;
3083 5287 : adfCornerY[1] = nDstYOff;
3084 5287 : adfCornerX[2] = nDstXOff;
3085 5287 : adfCornerY[2] = nDstYOff + nDstYSize;
3086 5287 : adfCornerX[3] = nDstXOff + nDstXSize;
3087 5287 : adfCornerY[3] = nDstYOff + nDstYSize;
3088 5287 : if (!psOptions->pfnTransformer(psOptions->pTransformerArg, TRUE, 4,
3089 : adfCornerX, adfCornerY, adfCornerZ,
3090 5221 : anCornerSuccess) ||
3091 10508 : !anCornerSuccess[0] || !anCornerSuccess[1] || !anCornerSuccess[2] ||
3092 5221 : !anCornerSuccess[3])
3093 : {
3094 66 : bAll = true;
3095 : }
3096 : }
3097 :
3098 6382 : bool bTryWithCheckWithInvertProj = false;
3099 6382 : double dfMinXOut = std::numeric_limits<double>::infinity();
3100 6382 : double dfMinYOut = std::numeric_limits<double>::infinity();
3101 6382 : double dfMaxXOut = -std::numeric_limits<double>::infinity();
3102 6382 : double dfMaxYOut = -std::numeric_limits<double>::infinity();
3103 :
3104 6382 : int nSamplePoints = 0;
3105 6382 : int nFailedCount = 0;
3106 6382 : if (!ComputeSourceWindowTransformPoints(
3107 : nDstXOff, nDstYOff, nDstXSize, nDstYSize, bUseGrid, bAll,
3108 : nStepCount, bTryWithCheckWithInvertProj, dfMinXOut, dfMinYOut,
3109 : dfMaxXOut, dfMaxYOut, nSamplePoints, nFailedCount))
3110 : {
3111 0 : return CE_Failure;
3112 : }
3113 :
3114 : // Use grid sampling as soon as a special point falls into the extent of
3115 : // the target raster.
3116 6382 : if (!bUseGrid && psOptions->hDstDS)
3117 : {
3118 13029 : for (const auto &xy : aDstXYSpecialPoints)
3119 : {
3120 17525 : if (0 <= xy.first &&
3121 1737 : GDALGetRasterXSize(psOptions->hDstDS) >= xy.first &&
3122 10427 : 0 <= xy.second &&
3123 796 : GDALGetRasterYSize(psOptions->hDstDS) >= xy.second)
3124 : {
3125 233 : bUseGrid = true;
3126 233 : bAll = false;
3127 233 : if (!ComputeSourceWindowTransformPoints(
3128 : nDstXOff, nDstYOff, nDstXSize, nDstYSize, bUseGrid,
3129 : bAll, nStepCount, bTryWithCheckWithInvertProj,
3130 : dfMinXOut, dfMinYOut, dfMaxXOut, dfMaxYOut,
3131 : nSamplePoints, nFailedCount))
3132 : {
3133 0 : return CE_Failure;
3134 : }
3135 233 : break;
3136 : }
3137 : }
3138 : }
3139 :
3140 6382 : const int nRasterXSize = GDALGetRasterXSize(psOptions->hSrcDS);
3141 6382 : const int nRasterYSize = GDALGetRasterYSize(psOptions->hSrcDS);
3142 :
3143 : // Try to detect crazy values coming from reprojection that would not
3144 : // have resulted in a PROJ error. Could happen for example with PROJ
3145 : // <= 4.9.2 with inverse UTM/tmerc (Snyder approximation without sanity
3146 : // check) when being far away from the central meridian. But might be worth
3147 : // keeping that even for later versions in case some exotic projection isn't
3148 : // properly sanitized.
3149 6308 : if (nFailedCount == 0 && !bTryWithCheckWithInvertProj &&
3150 6308 : (dfMinXOut < -1e6 || dfMinYOut < -1e6 ||
3151 12694 : dfMaxXOut > nRasterXSize + 1e6 || dfMaxYOut > nRasterYSize + 1e6) &&
3152 23 : !CPLTestBool(CPLGetConfigOption("CHECK_WITH_INVERT_PROJ", "NO")))
3153 : {
3154 23 : CPLDebug("WARP",
3155 : "ComputeSourceWindow(): bogus source dataset window "
3156 : "returned. Trying again with CHECK_WITH_INVERT_PROJ=YES");
3157 23 : bTryWithCheckWithInvertProj = true;
3158 :
3159 : // We should probably perform the coordinate transformation in the
3160 : // warp kernel under CHECK_WITH_INVERT_PROJ too...
3161 23 : if (!ComputeSourceWindowTransformPoints(
3162 : nDstXOff, nDstYOff, nDstXSize, nDstYSize, bUseGrid, bAll,
3163 : nStepCount, bTryWithCheckWithInvertProj, dfMinXOut, dfMinYOut,
3164 : dfMaxXOut, dfMaxYOut, nSamplePoints, nFailedCount))
3165 : {
3166 0 : return CE_Failure;
3167 : }
3168 : }
3169 :
3170 : /* -------------------------------------------------------------------- */
3171 : /* If we got any failures when not using a grid, we should */
3172 : /* really go back and try again with the grid. Sorry for the */
3173 : /* goto. */
3174 : /* -------------------------------------------------------------------- */
3175 6382 : if (!bUseGrid && nFailedCount > 0)
3176 : {
3177 65 : bUseGrid = true;
3178 65 : bAll = false;
3179 65 : if (!ComputeSourceWindowTransformPoints(
3180 : nDstXOff, nDstYOff, nDstXSize, nDstYSize, bUseGrid, bAll,
3181 : nStepCount, bTryWithCheckWithInvertProj, dfMinXOut, dfMinYOut,
3182 : dfMaxXOut, dfMaxYOut, nSamplePoints, nFailedCount))
3183 : {
3184 0 : return CE_Failure;
3185 : }
3186 : }
3187 :
3188 : /* -------------------------------------------------------------------- */
3189 : /* If we get hardly any points (or none) transforming, we give */
3190 : /* up. */
3191 : /* -------------------------------------------------------------------- */
3192 6382 : if (nFailedCount > nSamplePoints - 5)
3193 : {
3194 : const bool bErrorOutIfEmptySourceWindow =
3195 39 : CPLFetchBool(psOptions->papszWarpOptions,
3196 : "ERROR_OUT_IF_EMPTY_SOURCE_WINDOW", true);
3197 39 : if (bErrorOutIfEmptySourceWindow)
3198 : {
3199 3 : CPLError(CE_Failure, CPLE_AppDefined,
3200 : "Too many points (%d out of %d) failed to transform, "
3201 : "unable to compute output bounds.",
3202 : nFailedCount, nSamplePoints);
3203 : }
3204 : else
3205 : {
3206 36 : CPLDebug("WARP", "Cannot determine source window for %d,%d,%d,%d",
3207 : nDstXOff, nDstYOff, nDstXSize, nDstYSize);
3208 : }
3209 39 : return CE_Failure;
3210 : }
3211 :
3212 6343 : if (nFailedCount > 0)
3213 39 : CPLDebug("GDAL",
3214 : "GDALWarpOperation::ComputeSourceWindow() %d out of %d "
3215 : "points failed to transform.",
3216 : nFailedCount, nSamplePoints);
3217 :
3218 : /* -------------------------------------------------------------------- */
3219 : /* In some cases (see https://github.com/OSGeo/gdal/issues/862) */
3220 : /* the reverse transform does not work at some points, so try by */
3221 : /* transforming from source raster space to target raster space and */
3222 : /* see which source coordinates end up being in the AOI in the target */
3223 : /* raster space. */
3224 : /* -------------------------------------------------------------------- */
3225 6343 : if (bUseGrid)
3226 : {
3227 1260 : ComputeSourceWindowStartingFromSource(nDstXOff, nDstYOff, nDstXSize,
3228 : nDstYSize, &dfMinXOut, &dfMinYOut,
3229 : &dfMaxXOut, &dfMaxYOut);
3230 : }
3231 :
3232 : /* -------------------------------------------------------------------- */
3233 : /* Early exit to avoid crazy values to cause a huge nResWinSize that */
3234 : /* would result in a result window wrongly covering the whole raster. */
3235 : /* -------------------------------------------------------------------- */
3236 6343 : if (dfMinXOut > nRasterXSize || dfMaxXOut < 0 || dfMinYOut > nRasterYSize ||
3237 5273 : dfMaxYOut < 0)
3238 : {
3239 1531 : *pnSrcXOff = 0;
3240 1531 : *pnSrcYOff = 0;
3241 1531 : *pnSrcXSize = 0;
3242 1531 : *pnSrcYSize = 0;
3243 1531 : if (pdfSrcXExtraSize)
3244 1531 : *pdfSrcXExtraSize = 0.0;
3245 1531 : if (pdfSrcYExtraSize)
3246 1531 : *pdfSrcYExtraSize = 0.0;
3247 1531 : if (pdfSrcFillRatio)
3248 949 : *pdfSrcFillRatio = 0.0;
3249 1531 : return CE_None;
3250 : }
3251 :
3252 : // For scenarios where warping is used as a "decoration", try to clamp
3253 : // source pixel coordinates to integer when very close.
3254 19248 : const auto roundIfCloseEnough = [](double dfVal)
3255 : {
3256 19248 : const double dfRounded = std::round(dfVal);
3257 19248 : if (std::fabs(dfRounded - dfVal) < 1e-6)
3258 15213 : return dfRounded;
3259 4035 : return dfVal;
3260 : };
3261 :
3262 4812 : dfMinXOut = roundIfCloseEnough(dfMinXOut);
3263 4812 : dfMinYOut = roundIfCloseEnough(dfMinYOut);
3264 4812 : dfMaxXOut = roundIfCloseEnough(dfMaxXOut);
3265 4812 : dfMaxYOut = roundIfCloseEnough(dfMaxYOut);
3266 :
3267 4812 : if (m_bIsTranslationOnPixelBoundaries)
3268 : {
3269 376 : CPLAssert(dfMinXOut == std::round(dfMinXOut));
3270 376 : CPLAssert(dfMinYOut == std::round(dfMinYOut));
3271 376 : CPLAssert(dfMaxXOut == std::round(dfMaxXOut));
3272 376 : CPLAssert(dfMaxYOut == std::round(dfMaxYOut));
3273 376 : CPLAssert(std::round(dfMaxXOut - dfMinXOut) == nDstXSize);
3274 376 : CPLAssert(std::round(dfMaxYOut - dfMinYOut) == nDstYSize);
3275 : }
3276 :
3277 : /* -------------------------------------------------------------------- */
3278 : /* How much of a window around our source pixel might we need */
3279 : /* to collect data from based on the resampling kernel? Even */
3280 : /* if the requested central pixel falls off the source image, */
3281 : /* we may need to collect data if some portion of the */
3282 : /* resampling kernel could be on-image. */
3283 : /* -------------------------------------------------------------------- */
3284 4812 : const int nResWinSize = m_bIsTranslationOnPixelBoundaries
3285 4812 : ? 0
3286 4436 : : GWKGetFilterRadius(psOptions->eResampleAlg);
3287 :
3288 : // Take scaling into account.
3289 : // Avoid ridiculous small scaling factors to avoid potential further integer
3290 : // overflows
3291 9624 : const double dfXScale = std::max(1e-3, static_cast<double>(nDstXSize) /
3292 4812 : (dfMaxXOut - dfMinXOut));
3293 9624 : const double dfYScale = std::max(1e-3, static_cast<double>(nDstYSize) /
3294 4812 : (dfMaxYOut - dfMinYOut));
3295 4812 : int nXRadius = dfXScale < 0.95
3296 4812 : ? static_cast<int>(ceil(nResWinSize / dfXScale))
3297 : : nResWinSize;
3298 4812 : int nYRadius = dfYScale < 0.95
3299 4812 : ? static_cast<int>(ceil(nResWinSize / dfYScale))
3300 : : nResWinSize;
3301 :
3302 : /* -------------------------------------------------------------------- */
3303 : /* Allow addition of extra sample pixels to source window to */
3304 : /* avoid missing pixels due to sampling error. In fact, */
3305 : /* fallback to adding a bit to the window if any points failed */
3306 : /* to transform. */
3307 : /* -------------------------------------------------------------------- */
3308 4812 : if (const char *pszSourceExtra =
3309 4812 : CSLFetchNameValue(psOptions->papszWarpOptions, "SOURCE_EXTRA"))
3310 : {
3311 81 : int nSrcExtra = cpl::strict_parse<int>(pszSourceExtra).value_or(-1);
3312 :
3313 81 : if (nSrcExtra < 0)
3314 : {
3315 : // no point raising CE_Failure because it will get converted into
3316 : // a warning at an outer scope
3317 1 : CPLError(CE_Warning, CPLE_IllegalArg,
3318 : "SOURCE_EXTRA must be a positive integer or zero.");
3319 1 : nSrcExtra = 0;
3320 : }
3321 :
3322 81 : nXRadius += nSrcExtra;
3323 81 : nYRadius += nSrcExtra;
3324 : }
3325 4731 : else if (nFailedCount > 0)
3326 : {
3327 33 : nXRadius += 10;
3328 33 : nYRadius += 10;
3329 : }
3330 :
3331 : /* -------------------------------------------------------------------- */
3332 : /* return bounds. */
3333 : /* -------------------------------------------------------------------- */
3334 : #if DEBUG_VERBOSE
3335 : CPLDebug("WARP",
3336 : "dst=(%d,%d,%d,%d) raw "
3337 : "src=(minx=%.17g,miny=%.17g,maxx=%.17g,maxy=%.17g)",
3338 : nDstXOff, nDstYOff, nDstXSize, nDstYSize, dfMinXOut, dfMinYOut,
3339 : dfMaxXOut, dfMaxYOut);
3340 : #endif
3341 4812 : const int nMinXOutClamped = static_cast<int>(std::max(0.0, dfMinXOut));
3342 4812 : const int nMinYOutClamped = static_cast<int>(std::max(0.0, dfMinYOut));
3343 : const int nMaxXOutClamped = static_cast<int>(
3344 4812 : std::min(ceil(dfMaxXOut), static_cast<double>(nRasterXSize)));
3345 : const int nMaxYOutClamped = static_cast<int>(
3346 4812 : std::min(ceil(dfMaxYOut), static_cast<double>(nRasterYSize)));
3347 :
3348 : const double dfSrcXSizeRaw = std::max(
3349 14436 : 0.0, std::min(static_cast<double>(nRasterXSize - nMinXOutClamped),
3350 4812 : dfMaxXOut - dfMinXOut));
3351 : const double dfSrcYSizeRaw = std::max(
3352 14436 : 0.0, std::min(static_cast<double>(nRasterYSize - nMinYOutClamped),
3353 4812 : dfMaxYOut - dfMinYOut));
3354 :
3355 : // If we cover more than 90% of the width, then use it fully (helps for
3356 : // anti-meridian discontinuities)
3357 4812 : if (nMaxXOutClamped - nMinXOutClamped > 0.9 * nRasterXSize)
3358 : {
3359 1595 : *pnSrcXOff = 0;
3360 1595 : *pnSrcXSize = nRasterXSize;
3361 : }
3362 : else
3363 : {
3364 3217 : *pnSrcXOff =
3365 3217 : std::max(0, std::min(nMinXOutClamped - nXRadius, nRasterXSize));
3366 3217 : *pnSrcXSize =
3367 9651 : std::max(0, std::min(nRasterXSize - *pnSrcXOff,
3368 3217 : nMaxXOutClamped - *pnSrcXOff + nXRadius));
3369 : }
3370 :
3371 4812 : if (nMaxYOutClamped - nMinYOutClamped > 0.9 * nRasterYSize)
3372 : {
3373 1493 : *pnSrcYOff = 0;
3374 1493 : *pnSrcYSize = nRasterYSize;
3375 : }
3376 : else
3377 : {
3378 3319 : *pnSrcYOff =
3379 3319 : std::max(0, std::min(nMinYOutClamped - nYRadius, nRasterYSize));
3380 3319 : *pnSrcYSize =
3381 9957 : std::max(0, std::min(nRasterYSize - *pnSrcYOff,
3382 3319 : nMaxYOutClamped - *pnSrcYOff + nYRadius));
3383 : }
3384 :
3385 4812 : if (pdfSrcXExtraSize)
3386 4812 : *pdfSrcXExtraSize = *pnSrcXSize - dfSrcXSizeRaw;
3387 4812 : if (pdfSrcYExtraSize)
3388 4812 : *pdfSrcYExtraSize = *pnSrcYSize - dfSrcYSizeRaw;
3389 :
3390 : // Computed the ratio of the clamped source raster window size over
3391 : // the unclamped source raster window size.
3392 4812 : if (pdfSrcFillRatio)
3393 3653 : *pdfSrcFillRatio =
3394 7306 : static_cast<double>(*pnSrcXSize) * (*pnSrcYSize) /
3395 3653 : std::max(1.0, (dfMaxXOut - dfMinXOut + 2 * nXRadius) *
3396 3653 : (dfMaxYOut - dfMinYOut + 2 * nYRadius));
3397 :
3398 4812 : return CE_None;
3399 : }
3400 :
3401 : /************************************************************************/
3402 : /* ReportTiming() */
3403 : /************************************************************************/
3404 :
3405 12735 : void GDALWarpOperation::ReportTiming(const char *pszMessage)
3406 :
3407 : {
3408 12735 : if (!bReportTimings)
3409 12735 : return;
3410 :
3411 0 : const unsigned long nNewTime = VSITime(nullptr);
3412 :
3413 0 : if (pszMessage != nullptr)
3414 : {
3415 0 : CPLDebug("WARP_TIMING", "%s: %lds", pszMessage,
3416 0 : static_cast<long>(nNewTime - nLastTimeReported));
3417 : }
3418 :
3419 0 : nLastTimeReported = nNewTime;
3420 : }
|