Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Virtual GDAL Datasets
4 : * Purpose: Implementation of some filter types.
5 : * Author: Frank Warmerdam <warmerdam@pobox.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "vrtdataset.h"
16 :
17 : #include <cmath>
18 : #include <cstddef>
19 : #include <cstdlib>
20 : #include <cstring>
21 : #include <limits>
22 :
23 : #include "cpl_conv.h"
24 : #include "cpl_error.h"
25 : #include "cpl_minixml.h"
26 : #include "cpl_string.h"
27 : #include "cpl_vsi.h"
28 : #include "gdal.h"
29 : #include "gdal_priv.h"
30 :
31 : /*! @cond Doxygen_Suppress */
32 :
33 : /************************************************************************/
34 : /* ==================================================================== */
35 : /* VRTFilteredSource */
36 : /* ==================================================================== */
37 : /************************************************************************/
38 :
39 : /************************************************************************/
40 : /* VRTFilteredSource() */
41 : /************************************************************************/
42 :
43 14 : VRTFilteredSource::VRTFilteredSource()
44 14 : : m_nSupportedTypesCount(1), m_nExtraEdgePixels(0)
45 : {
46 294 : for (size_t i = 0; i < CPL_ARRAYSIZE(m_aeSupportedTypes); ++i)
47 280 : m_aeSupportedTypes[i] = GDT_Unknown;
48 :
49 14 : m_aeSupportedTypes[0] = GDT_Float32;
50 14 : }
51 :
52 : /************************************************************************/
53 : /* ~VRTFilteredSource() */
54 : /************************************************************************/
55 :
56 14 : VRTFilteredSource::~VRTFilteredSource()
57 : {
58 14 : }
59 :
60 : /************************************************************************/
61 : /* SetExtraEdgePixels() */
62 : /************************************************************************/
63 :
64 12 : void VRTFilteredSource::SetExtraEdgePixels(int nEdgePixels)
65 :
66 : {
67 12 : m_nExtraEdgePixels = nEdgePixels;
68 12 : }
69 :
70 : /************************************************************************/
71 : /* SetFilteringDataTypesSupported() */
72 : /************************************************************************/
73 :
74 14 : void VRTFilteredSource::SetFilteringDataTypesSupported(int nTypeCount,
75 : GDALDataType *paeTypes)
76 :
77 : {
78 14 : if (nTypeCount >
79 : static_cast<int>(sizeof(m_aeSupportedTypes) / sizeof(GDALDataType)))
80 : {
81 0 : CPLAssert(false);
82 : nTypeCount =
83 : static_cast<int>(sizeof(m_aeSupportedTypes) / sizeof(GDALDataType));
84 : }
85 :
86 14 : m_nSupportedTypesCount = nTypeCount;
87 14 : memcpy(m_aeSupportedTypes, paeTypes, sizeof(GDALDataType) * nTypeCount);
88 14 : }
89 :
90 : /************************************************************************/
91 : /* IsTypeSupported() */
92 : /************************************************************************/
93 :
94 28 : int VRTFilteredSource::IsTypeSupported(GDALDataType eTestType) const
95 :
96 : {
97 56 : for (int i = 0; i < m_nSupportedTypesCount; i++)
98 : {
99 28 : if (eTestType == m_aeSupportedTypes[i])
100 0 : return TRUE;
101 : }
102 :
103 28 : return FALSE;
104 : }
105 :
106 : /************************************************************************/
107 : /* RasterIO() */
108 : /************************************************************************/
109 :
110 14 : CPLErr VRTFilteredSource::RasterIO(GDALDataType eVRTBandDataType, int nXOff,
111 : int nYOff, int nXSize, int nYSize,
112 : void *pData, int nBufXSize, int nBufYSize,
113 : GDALDataType eBufType, GSpacing nPixelSpace,
114 : GSpacing nLineSpace,
115 : GDALRasterIOExtraArg *psExtraArg,
116 : WorkingState &oWorkingState)
117 :
118 : {
119 : /* -------------------------------------------------------------------- */
120 : /* For now we don't support filtered access to non-full */
121 : /* resolution requests. Just collect the data directly without */
122 : /* any operator. */
123 : /* -------------------------------------------------------------------- */
124 14 : if (nBufXSize != nXSize || nBufYSize != nYSize)
125 : {
126 0 : return VRTComplexSource::RasterIO(
127 : eVRTBandDataType, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
128 : nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg,
129 0 : oWorkingState);
130 : }
131 :
132 14 : double dfXOff = nXOff;
133 14 : double dfYOff = nYOff;
134 14 : double dfXSize = nXSize;
135 14 : double dfYSize = nYSize;
136 14 : if (psExtraArg != nullptr && psExtraArg->bFloatingPointWindowValidity)
137 : {
138 4 : dfXOff = psExtraArg->dfXOff;
139 4 : dfYOff = psExtraArg->dfYOff;
140 4 : dfXSize = psExtraArg->dfXSize;
141 4 : dfYSize = psExtraArg->dfYSize;
142 : }
143 :
144 : // The window we will actually request from the source raster band.
145 14 : double dfReqXOff = 0.0;
146 14 : double dfReqYOff = 0.0;
147 14 : double dfReqXSize = 0.0;
148 14 : double dfReqYSize = 0.0;
149 14 : int nReqXOff = 0;
150 14 : int nReqYOff = 0;
151 14 : int nReqXSize = 0;
152 14 : int nReqYSize = 0;
153 :
154 : // The window we will actual set _within_ the pData buffer.
155 14 : int nOutXOff = 0;
156 14 : int nOutYOff = 0;
157 14 : int nOutXSize = 0;
158 14 : int nOutYSize = 0;
159 :
160 14 : bool bError = false;
161 14 : if (!GetSrcDstWindow(dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
162 : &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
163 : &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
164 : &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
165 : {
166 0 : return bError ? CE_Failure : CE_None;
167 : }
168 :
169 14 : pData = reinterpret_cast<GByte *>(pData) + nPixelSpace * nOutXOff +
170 14 : nLineSpace * nOutYOff;
171 :
172 : /* -------------------------------------------------------------------- */
173 : /* Determine the data type we want to request. We try to match */
174 : /* the source or destination request, and if both those fail we */
175 : /* fallback to the first supported type at least as expressive */
176 : /* as the request. */
177 : /* -------------------------------------------------------------------- */
178 14 : GDALDataType eOperDataType = GDT_Unknown;
179 :
180 14 : if (IsTypeSupported(eBufType))
181 0 : eOperDataType = eBufType;
182 :
183 14 : auto l_band = GetRasterBand();
184 14 : if (!l_band)
185 0 : return CE_Failure;
186 :
187 28 : if (eOperDataType == GDT_Unknown &&
188 14 : IsTypeSupported(l_band->GetRasterDataType()))
189 0 : eOperDataType = l_band->GetRasterDataType();
190 :
191 14 : if (eOperDataType == GDT_Unknown)
192 : {
193 28 : for (int i = 0; i < m_nSupportedTypesCount; i++)
194 : {
195 14 : if (GDALDataTypeUnion(m_aeSupportedTypes[i], eBufType) ==
196 14 : m_aeSupportedTypes[i])
197 : {
198 0 : eOperDataType = m_aeSupportedTypes[i];
199 : }
200 : }
201 : }
202 :
203 14 : if (eOperDataType == GDT_Unknown)
204 : {
205 14 : eOperDataType = m_aeSupportedTypes[0];
206 :
207 14 : for (int i = 1; i < m_nSupportedTypesCount; i++)
208 : {
209 0 : if (GDALGetDataTypeSize(m_aeSupportedTypes[i]) >
210 0 : GDALGetDataTypeSize(eOperDataType))
211 : {
212 0 : eOperDataType = m_aeSupportedTypes[i];
213 : }
214 : }
215 : }
216 :
217 : /* -------------------------------------------------------------------- */
218 : /* Allocate the buffer of data into which our imagery will be */
219 : /* read, with the extra edge pixels as well. This will be the */
220 : /* source data fed into the filter. */
221 : /* -------------------------------------------------------------------- */
222 14 : if (nOutXSize > INT_MAX - 2 * m_nExtraEdgePixels ||
223 14 : nOutYSize > INT_MAX - 2 * m_nExtraEdgePixels)
224 : {
225 0 : return CE_Failure;
226 : }
227 14 : const int nExtraXSize = nOutXSize + 2 * m_nExtraEdgePixels;
228 14 : const int nExtraYSize = nOutYSize + 2 * m_nExtraEdgePixels;
229 :
230 14 : GByte *pabyWorkData = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
231 : nExtraXSize, nExtraYSize, GDALGetDataTypeSizeBytes(eOperDataType)));
232 :
233 14 : if (pabyWorkData == nullptr)
234 : {
235 0 : return CE_Failure;
236 : }
237 :
238 14 : const GPtrDiff_t nPixelOffset = GDALGetDataTypeSizeBytes(eOperDataType);
239 14 : const GPtrDiff_t nLineOffset = nPixelOffset * nExtraXSize;
240 :
241 14 : memset(pabyWorkData, 0, nLineOffset * nExtraYSize);
242 :
243 : /* -------------------------------------------------------------------- */
244 : /* Allocate the output buffer in the same dimensions as the work */
245 : /* buffer. This allows the filter process to write edge pixels */
246 : /* if needed for two-pass (separable) filtering. */
247 : /* -------------------------------------------------------------------- */
248 : GByte *pabyOutData = static_cast<GByte *>(
249 14 : VSI_MALLOC3_VERBOSE(nExtraXSize, nExtraYSize, nPixelOffset));
250 14 : if (pabyOutData == nullptr)
251 : {
252 0 : CPLFree(pabyWorkData);
253 0 : return CE_Failure;
254 : }
255 :
256 : /* -------------------------------------------------------------------- */
257 : /* Figure out the extended window that we want to load. Note */
258 : /* that we keep track of the file window as well as the amount */
259 : /* we will need to edge fill past the edge of the source dataset. */
260 : /* -------------------------------------------------------------------- */
261 14 : int nFileXOff = nReqXOff - m_nExtraEdgePixels;
262 14 : int nFileYOff = nReqYOff - m_nExtraEdgePixels;
263 14 : int nFileXSize = nExtraXSize;
264 14 : int nFileYSize = nExtraYSize;
265 :
266 14 : int nTopFill = 0;
267 14 : int nLeftFill = 0;
268 14 : int nRightFill = 0;
269 14 : int nBottomFill = 0;
270 :
271 14 : if (nFileXOff < 0)
272 : {
273 14 : nLeftFill = -nFileXOff;
274 14 : nFileXOff = 0;
275 14 : nFileXSize -= nLeftFill;
276 : }
277 :
278 14 : if (nFileYOff < 0)
279 : {
280 8 : nTopFill = -nFileYOff;
281 8 : nFileYOff = 0;
282 8 : nFileYSize -= nTopFill;
283 : }
284 :
285 14 : if (nFileXOff + nFileXSize > l_band->GetXSize())
286 : {
287 14 : nRightFill = nFileXOff + nFileXSize - l_band->GetXSize();
288 14 : nFileXSize -= nRightFill;
289 : }
290 :
291 14 : if (nFileYOff + nFileYSize > l_band->GetYSize())
292 : {
293 8 : nBottomFill = nFileYOff + nFileYSize - l_band->GetYSize();
294 8 : nFileYSize -= nBottomFill;
295 : }
296 :
297 : /* -------------------------------------------------------------------- */
298 : /* Load the data. */
299 : /* -------------------------------------------------------------------- */
300 : {
301 : GDALRasterIOExtraArg sExtraArgs;
302 14 : INIT_RASTERIO_EXTRA_ARG(sExtraArgs);
303 : const bool bIsComplex =
304 14 : CPL_TO_BOOL(GDALDataTypeIsComplex(eOperDataType));
305 14 : const CPLErr eErr = VRTComplexSource::RasterIOInternal<float>(
306 : l_band, eVRTBandDataType, nFileXOff, nFileYOff, nFileXSize,
307 : nFileYSize,
308 14 : pabyWorkData + nLineOffset * nTopFill + nPixelOffset * nLeftFill,
309 : nFileXSize, nFileYSize, eOperDataType, nPixelOffset, nLineOffset,
310 : &sExtraArgs, bIsComplex ? GDT_CFloat32 : GDT_Float32,
311 : oWorkingState);
312 :
313 14 : if (eErr != CE_None)
314 : {
315 0 : VSIFree(pabyWorkData);
316 0 : VSIFree(pabyOutData);
317 :
318 0 : return eErr;
319 : }
320 : }
321 :
322 : /* -------------------------------------------------------------------- */
323 : /* Fill in missing areas. Note that we replicate the edge */
324 : /* valid values out. We don't using "mirroring" which might be */
325 : /* more suitable for some times of filters. We also don't mark */
326 : /* these pixels as "nodata" though perhaps we should. */
327 : /* -------------------------------------------------------------------- */
328 14 : if (nLeftFill != 0 || nRightFill != 0)
329 : {
330 1296 : for (int i = nTopFill; i < nExtraYSize - nBottomFill; i++)
331 : {
332 1282 : if (nLeftFill != 0)
333 1282 : GDALCopyWords(
334 1282 : pabyWorkData + nPixelOffset * nLeftFill + i * nLineOffset,
335 1282 : eOperDataType, 0, pabyWorkData + i * nLineOffset,
336 : eOperDataType, static_cast<int>(nPixelOffset), nLeftFill);
337 :
338 1282 : if (nRightFill != 0)
339 1282 : GDALCopyWords(pabyWorkData + i * nLineOffset +
340 1282 : nPixelOffset * (nExtraXSize - nRightFill - 1),
341 : eOperDataType, 0,
342 1282 : pabyWorkData + i * nLineOffset +
343 1282 : nPixelOffset * (nExtraXSize - nRightFill),
344 : eOperDataType, static_cast<int>(nPixelOffset),
345 : nRightFill);
346 : }
347 : }
348 :
349 27 : for (int i = 0; i < nTopFill; i++)
350 : {
351 13 : memcpy(pabyWorkData + i * nLineOffset,
352 13 : pabyWorkData + nTopFill * nLineOffset, nLineOffset);
353 : }
354 :
355 27 : for (int i = nExtraYSize - nBottomFill; i < nExtraYSize; i++)
356 : {
357 13 : memcpy(pabyWorkData + i * nLineOffset,
358 13 : pabyWorkData + (nExtraYSize - nBottomFill - 1) * nLineOffset,
359 : nLineOffset);
360 : }
361 :
362 : /* -------------------------------------------------------------------- */
363 : /* Filter the data. */
364 : /* -------------------------------------------------------------------- */
365 28 : const CPLErr eErr = FilterData(nExtraXSize, nExtraYSize, eOperDataType,
366 14 : pabyWorkData, pabyOutData);
367 :
368 14 : VSIFree(pabyWorkData);
369 14 : if (eErr != CE_None)
370 : {
371 0 : VSIFree(pabyOutData);
372 :
373 0 : return eErr;
374 : }
375 :
376 : /* -------------------------------------------------------------------- */
377 : /* Copy from work buffer to target buffer. */
378 : /* -------------------------------------------------------------------- */
379 14 : GByte *pabySrcRow =
380 14 : pabyOutData + (nLineOffset + nPixelOffset) * m_nExtraEdgePixels;
381 14 : GByte *pabyDstRow = reinterpret_cast<GByte *>(pData);
382 :
383 1284 : for (int i = 0; i < nOutYSize;
384 1270 : i++, pabySrcRow += nLineOffset, pabyDstRow += nLineSpace)
385 : {
386 1270 : GDALCopyWords(pabySrcRow, eOperDataType, static_cast<int>(nPixelOffset),
387 : pabyDstRow, eBufType, static_cast<int>(nPixelSpace),
388 : nOutXSize);
389 : }
390 :
391 14 : VSIFree(pabyOutData);
392 :
393 14 : return CE_None;
394 : }
395 :
396 : /************************************************************************/
397 : /* ==================================================================== */
398 : /* VRTKernelFilteredSource */
399 : /* ==================================================================== */
400 : /************************************************************************/
401 :
402 : /************************************************************************/
403 : /* VRTKernelFilteredSource() */
404 : /************************************************************************/
405 :
406 14 : VRTKernelFilteredSource::VRTKernelFilteredSource()
407 : {
408 14 : GDALDataType aeSupTypes[] = {GDT_Float32};
409 14 : SetFilteringDataTypesSupported(1, aeSupTypes);
410 14 : }
411 :
412 : /************************************************************************/
413 : /* GetType() */
414 : /************************************************************************/
415 :
416 10 : const char *VRTKernelFilteredSource::GetType() const
417 : {
418 : static const char *TYPE = "KernelFilteredSource";
419 10 : return TYPE;
420 : }
421 :
422 : /************************************************************************/
423 : /* SetNormalized() */
424 : /************************************************************************/
425 :
426 12 : void VRTKernelFilteredSource::SetNormalized(bool bNormalizedIn)
427 :
428 : {
429 12 : m_bNormalized = bNormalizedIn;
430 12 : }
431 :
432 : /************************************************************************/
433 : /* SetKernel() */
434 : /************************************************************************/
435 :
436 : CPLErr
437 12 : VRTKernelFilteredSource::SetKernel(int nNewKernelSize, bool bSeparable,
438 : const std::vector<double> &adfNewCoefs)
439 :
440 : {
441 12 : if (nNewKernelSize < 1 || (nNewKernelSize % 2) != 1)
442 : {
443 0 : CPLError(CE_Failure, CPLE_AppDefined,
444 : "Illegal filtering kernel size %d, "
445 : "must be odd positive number.",
446 : nNewKernelSize);
447 0 : return CE_Failure;
448 : }
449 12 : if (adfNewCoefs.size() !=
450 12 : static_cast<size_t>(nNewKernelSize) * (bSeparable ? 1 : nNewKernelSize))
451 : {
452 0 : CPLError(CE_Failure, CPLE_AppDefined,
453 : "adfNewCoefs[] is not of expected size");
454 0 : return CE_Failure;
455 : }
456 :
457 12 : m_nKernelSize = nNewKernelSize;
458 12 : m_bSeparable = bSeparable;
459 12 : m_adfKernelCoefs = adfNewCoefs;
460 :
461 12 : SetExtraEdgePixels((nNewKernelSize - 1) / 2);
462 :
463 12 : return CE_None;
464 : }
465 :
466 : /************************************************************************/
467 : /* FilterData() */
468 : /************************************************************************/
469 :
470 14 : CPLErr VRTKernelFilteredSource::FilterData(int nXSize, int nYSize,
471 : GDALDataType eType,
472 : GByte *pabySrcData,
473 : GByte *pabyDstData)
474 :
475 : {
476 : /* -------------------------------------------------------------------- */
477 : /* Validate data type. */
478 : /* -------------------------------------------------------------------- */
479 14 : if (eType != GDT_Float32)
480 : {
481 0 : CPLError(CE_Failure, CPLE_AppDefined,
482 : "Unsupported data type (%s) in "
483 : "VRTKernelFilteredSource::FilterData()",
484 : GDALGetDataTypeName(eType));
485 0 : return CE_Failure;
486 : }
487 :
488 14 : CPLAssert(m_nExtraEdgePixels * 2 + 1 == m_nKernelSize ||
489 : (m_nKernelSize == 0 && m_nExtraEdgePixels == 0));
490 :
491 : /* -------------------------------------------------------------------- */
492 : /* Float32 case. */
493 : /* -------------------------------------------------------------------- */
494 : // if( eType == GDT_Float32 )
495 : {
496 14 : float *pafSrcData = reinterpret_cast<float *>(pabySrcData);
497 14 : float *pafDstData = reinterpret_cast<float *>(pabyDstData);
498 :
499 14 : int bHasNoData = FALSE;
500 14 : auto l_poBand = GetRasterBand();
501 14 : if (!l_poBand)
502 0 : return CE_Failure;
503 : const float fNoData =
504 14 : static_cast<float>(l_poBand->GetNoDataValue(&bHasNoData));
505 :
506 14 : const int nAxisCount = m_bSeparable ? 2 : 1;
507 :
508 29 : for (int nAxis = 0; nAxis < nAxisCount; ++nAxis)
509 : {
510 15 : const int nISize = nAxis == 0 ? nYSize : nXSize;
511 15 : const int nJSize = nAxis == 0 ? nXSize : nYSize;
512 15 : const int nIStride = nAxis == 0 ? nXSize : 1;
513 15 : const int nJStride = nAxis == 0 ? 1 : nXSize;
514 :
515 15 : const int nIMin = m_nExtraEdgePixels;
516 15 : const int nIMax = nISize - m_nExtraEdgePixels;
517 15 : const int nJMin = (m_bSeparable ? 0 : m_nExtraEdgePixels);
518 15 : const int nJMax = nJSize - (m_bSeparable ? 0 : m_nExtraEdgePixels);
519 :
520 4359 : for (GPtrDiff_t iJ = nJMin; iJ < nJMax; ++iJ)
521 : {
522 4344 : if (nAxis == 1)
523 62 : memcpy(pafSrcData + iJ * nJStride,
524 62 : pafDstData + iJ * nJStride, sizeof(float) * nXSize);
525 :
526 520944 : for (int iI = nIMin; iI < nIMax; ++iI)
527 : {
528 516600 : const GPtrDiff_t iIndex =
529 516600 : static_cast<GPtrDiff_t>(iI) * nIStride + iJ * nJStride;
530 :
531 516600 : if (bHasNoData && pafSrcData[iIndex] == fNoData)
532 : {
533 200 : pafDstData[iIndex] = fNoData;
534 200 : continue;
535 : }
536 :
537 516400 : double dfSum = 0.0, dfKernSum = 0.0;
538 :
539 2127600 : for (GPtrDiff_t iII = -m_nExtraEdgePixels, iK = 0;
540 2127600 : iII <= m_nExtraEdgePixels; ++iII)
541 : {
542 6283600 : for (GPtrDiff_t iJJ =
543 1611200 : (m_bSeparable ? 0 : -m_nExtraEdgePixels);
544 6283600 : iJJ <= (m_bSeparable ? 0 : m_nExtraEdgePixels);
545 : ++iJJ, ++iK)
546 : {
547 4672400 : const float *pfData = pafSrcData + iIndex +
548 4672400 : iII * nIStride +
549 4672400 : iJJ * nJStride;
550 4672400 : if (bHasNoData && *pfData == fNoData)
551 836 : continue;
552 4671560 : dfSum += *pfData * m_adfKernelCoefs[iK];
553 4671560 : dfKernSum += m_adfKernelCoefs[iK];
554 : }
555 : }
556 :
557 : double fResult;
558 :
559 516400 : if (!m_bNormalized)
560 510000 : fResult = dfSum;
561 6400 : else if (dfKernSum == 0.0)
562 0 : fResult = 0.0;
563 : else
564 6400 : fResult = dfSum / dfKernSum;
565 516400 : pafDstData[iIndex] = static_cast<float>(fResult);
566 : }
567 : }
568 : }
569 : }
570 :
571 14 : return CE_None;
572 : }
573 :
574 : /************************************************************************/
575 : /* XMLInit() */
576 : /************************************************************************/
577 :
578 : CPLErr
579 14 : VRTKernelFilteredSource::XMLInit(const CPLXMLNode *psTree,
580 : const char *pszVRTPath,
581 : VRTMapSharedResources &oMapSharedSources)
582 :
583 : {
584 : {
585 : const CPLErr eErr =
586 14 : VRTFilteredSource::XMLInit(psTree, pszVRTPath, oMapSharedSources);
587 14 : if (eErr != CE_None)
588 0 : return eErr;
589 : }
590 :
591 14 : const int nNewKernelSize = atoi(CPLGetXMLValue(psTree, "Kernel.Size", "0"));
592 :
593 14 : if (nNewKernelSize == 0)
594 0 : return CE_None;
595 : // To prevent a integer overflow when computing nNewKernelSize *
596 : // nNewKernelSize
597 27 : if (nNewKernelSize < 0 ||
598 13 : nNewKernelSize > static_cast<int>(std::sqrt(static_cast<double>(
599 13 : std::numeric_limits<int>::max()))))
600 : {
601 2 : CPLError(CE_Failure, CPLE_IllegalArg,
602 : "Invalid value for kernel size: %d", nNewKernelSize);
603 2 : return CE_Failure;
604 : }
605 :
606 : const CPLStringList aosCoefItems(
607 24 : CSLTokenizeString(CPLGetXMLValue(psTree, "Kernel.Coefs", "")));
608 :
609 12 : const int nCoefs = aosCoefItems.size();
610 :
611 12 : const bool bSquare = nCoefs == nNewKernelSize * nNewKernelSize;
612 12 : const bool bSeparable = nCoefs == nNewKernelSize && nCoefs != 1;
613 :
614 12 : if (!bSquare && !bSeparable)
615 : {
616 0 : CPLError(CE_Failure, CPLE_AppDefined,
617 : "Got wrong number of filter kernel coefficients (%s). "
618 : "Expected %d or %d, got %d.",
619 : CPLGetXMLValue(psTree, "Kernel.Coefs", ""),
620 : nNewKernelSize * nNewKernelSize, nNewKernelSize, nCoefs);
621 0 : return CE_Failure;
622 : }
623 :
624 12 : std::vector<double> adfNewCoefs;
625 12 : adfNewCoefs.reserve(nCoefs);
626 122 : for (int i = 0; i < nCoefs; i++)
627 110 : adfNewCoefs.push_back(CPLAtof(aosCoefItems[i]));
628 :
629 12 : const CPLErr eErr = SetKernel(nNewKernelSize, bSeparable, adfNewCoefs);
630 12 : if (eErr == CE_None)
631 : {
632 12 : SetNormalized(atoi(CPLGetXMLValue(psTree, "Kernel.normalized", "0")) !=
633 : 0);
634 : }
635 12 : return eErr;
636 : }
637 :
638 : /************************************************************************/
639 : /* SerializeToXML() */
640 : /************************************************************************/
641 :
642 2 : CPLXMLNode *VRTKernelFilteredSource::SerializeToXML(const char *pszVRTPath)
643 :
644 : {
645 2 : CPLXMLNode *psSrc = VRTFilteredSource::SerializeToXML(pszVRTPath);
646 :
647 2 : if (psSrc == nullptr)
648 0 : return nullptr;
649 :
650 2 : CPLFree(psSrc->pszValue);
651 2 : psSrc->pszValue = CPLStrdup("KernelFilteredSource");
652 :
653 2 : if (m_nKernelSize == 0)
654 0 : return psSrc;
655 :
656 2 : CPLXMLNode *psKernel = CPLCreateXMLNode(psSrc, CXT_Element, "Kernel");
657 :
658 2 : CPLCreateXMLNode(CPLCreateXMLNode(psKernel, CXT_Attribute, "normalized"),
659 2 : CXT_Text, m_bNormalized ? "1" : "0");
660 :
661 2 : std::string osCoefs;
662 14 : for (auto dfVal : m_adfKernelCoefs)
663 : {
664 12 : if (!osCoefs.empty())
665 10 : osCoefs += ' ';
666 12 : osCoefs += CPLSPrintf("%.8g", dfVal);
667 : }
668 :
669 2 : CPLSetXMLValue(psKernel, "Size", CPLSPrintf("%d", m_nKernelSize));
670 2 : CPLSetXMLValue(psKernel, "Coefs", osCoefs.c_str());
671 :
672 2 : return psSrc;
673 : }
674 :
675 : /************************************************************************/
676 : /* VRTParseFilterSources() */
677 : /************************************************************************/
678 :
679 14 : VRTSource *VRTParseFilterSources(const CPLXMLNode *psChild,
680 : const char *pszVRTPath,
681 : VRTMapSharedResources &oMapSharedSources)
682 :
683 : {
684 14 : if (EQUAL(psChild->pszValue, "KernelFilteredSource"))
685 : {
686 14 : VRTSource *poSrc = new VRTKernelFilteredSource();
687 14 : if (poSrc->XMLInit(psChild, pszVRTPath, oMapSharedSources) == CE_None)
688 12 : return poSrc;
689 :
690 2 : delete poSrc;
691 : }
692 :
693 2 : return nullptr;
694 : }
695 :
696 : /*! @endcond */
|