@@ -190,6 +190,7 @@ static CPLErr GWKBilinearNoMasksOrDstDensityOnlyDouble(GDALWarpKernel *poWK);
190
190
static CPLErr GWKCubicNoMasksOrDstDensityOnlyShort (GDALWarpKernel *poWK);
191
191
static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyShort (GDALWarpKernel *poWK);
192
192
static CPLErr GWKNearestShort (GDALWarpKernel *poWK);
193
+ static CPLErr GWKNearestUnsignedShort (GDALWarpKernel *poWK);
193
194
static CPLErr GWKNearestNoMasksOrDstDensityOnlyFloat (GDALWarpKernel *poWK);
194
195
static CPLErr GWKNearestFloat (GDALWarpKernel *poWK);
195
196
static CPLErr GWKAverageOrMode (GDALWarpKernel *);
@@ -1307,10 +1308,12 @@ CPLErr GDALWarpKernel::PerformWarp()
1307
1308
bNoMasksOrDstDensityOnly)
1308
1309
return GWKBilinearNoMasksOrDstDensityOnlyUShort (this );
1309
1310
1310
- if ((eWorkingDataType == GDT_Int16 || eWorkingDataType == GDT_UInt16) &&
1311
- eResample == GRA_NearestNeighbour)
1311
+ if (eWorkingDataType == GDT_Int16 && eResample == GRA_NearestNeighbour)
1312
1312
return GWKNearestShort (this );
1313
1313
1314
+ if (eWorkingDataType == GDT_Int16 && eResample == GRA_NearestNeighbour)
1315
+ return GWKNearestUnsignedShort (this );
1316
+
1314
1317
if (eWorkingDataType == GDT_Float32 && eResample == GRA_NearestNeighbour &&
1315
1318
bNoMasksOrDstDensityOnly)
1316
1319
return GWKNearestNoMasksOrDstDensityOnlyFloat (this );
@@ -1520,6 +1523,60 @@ template <> double GWKClampValueT<double>(double dfValue)
1520
1523
}
1521
1524
#endif
1522
1525
1526
+ /* ***********************************************************************/
1527
+ /* AvoidNoData() */
1528
+ /* ***********************************************************************/
1529
+
1530
+ template <class T >
1531
+ inline void AvoidNoData (const GDALWarpKernel *poWK, int iBand,
1532
+ GPtrDiff_t iDstOffset)
1533
+ {
1534
+ GByte *pabyDst = poWK->papabyDstImage [iBand];
1535
+ T *pDst = reinterpret_cast <T *>(pabyDst);
1536
+
1537
+ if (poWK->padfDstNoDataReal != nullptr &&
1538
+ poWK->padfDstNoDataReal [iBand] == static_cast <double >(pDst[iDstOffset]))
1539
+ {
1540
+ if constexpr (std::numeric_limits<T>::is_integer)
1541
+ {
1542
+ if (pDst[iDstOffset] ==
1543
+ static_cast <T>(std::numeric_limits<T>::lowest ()))
1544
+ {
1545
+ pDst[iDstOffset] =
1546
+ static_cast <T>(std::numeric_limits<T>::lowest () + 1 );
1547
+ }
1548
+ else
1549
+ pDst[iDstOffset]--;
1550
+ }
1551
+ else
1552
+ {
1553
+ if (pDst[iDstOffset] == std::numeric_limits<T>::max ())
1554
+ {
1555
+ pDst[iDstOffset] =
1556
+ std::nextafter (pDst[iDstOffset], static_cast <T>(0 ));
1557
+ }
1558
+ else
1559
+ {
1560
+ pDst[iDstOffset] = std::nextafter (
1561
+ pDst[iDstOffset], std::numeric_limits<T>::max ());
1562
+ }
1563
+ }
1564
+
1565
+ if (!poWK->bWarnedAboutDstNoDataReplacement )
1566
+ {
1567
+ const_cast <GDALWarpKernel *>(poWK)
1568
+ ->bWarnedAboutDstNoDataReplacement = true ;
1569
+ CPLError (CE_Warning, CPLE_AppDefined,
1570
+ " Value %g in the source dataset has been changed to %g "
1571
+ " in the destination dataset to avoid being treated as "
1572
+ " NoData. To avoid this, select a different NoData value "
1573
+ " for the destination dataset." ,
1574
+ poWK->padfDstNoDataReal [iBand],
1575
+ static_cast <double >(pDst[iDstOffset]));
1576
+ }
1577
+ }
1578
+ }
1579
+
1523
1580
/* ***********************************************************************/
1524
1581
/* GWKSetPixelValueRealT() */
1525
1582
/* ***********************************************************************/
@@ -1580,16 +1637,39 @@ static bool GWKSetPixelValueRealT(const GDALWarpKernel *poWK, int iBand,
1580
1637
pDst[iDstOffset] = value;
1581
1638
}
1582
1639
1583
- if (poWK->padfDstNoDataReal != nullptr &&
1584
- poWK->padfDstNoDataReal [iBand] == static_cast <double >(pDst[iDstOffset]))
1640
+ AvoidNoData<T>(poWK, iBand, iDstOffset);
1641
+
1642
+ return true ;
1643
+ }
1644
+
1645
+ /* ***********************************************************************/
1646
+ /* ClampRoundAndAvoidNoData() */
1647
+ /* ***********************************************************************/
1648
+
1649
+ template <class T >
1650
+ inline void ClampRoundAndAvoidNoData (const GDALWarpKernel *poWK, int iBand,
1651
+ GPtrDiff_t iDstOffset, double dfReal)
1652
+ {
1653
+ GByte *pabyDst = poWK->papabyDstImage [iBand];
1654
+ T *pDst = reinterpret_cast <T *>(pabyDst);
1655
+
1656
+ if constexpr (std::numeric_limits<T>::is_integer)
1585
1657
{
1586
- if (pDst[iDstOffset] == std::numeric_limits<T>::min ())
1587
- pDst[iDstOffset] = std::numeric_limits<T>::min () + 1 ;
1658
+ if (dfReal < static_cast <double >(std::numeric_limits<T>::lowest ()))
1659
+ pDst[iDstOffset] = static_cast <T>(std::numeric_limits<T>::lowest ());
1660
+ else if (dfReal > static_cast <double >(std::numeric_limits<T>::max ()))
1661
+ pDst[iDstOffset] = static_cast <T>(std::numeric_limits<T>::max ());
1588
1662
else
1589
- pDst[iDstOffset]--;
1663
+ pDst[iDstOffset] = (std::numeric_limits<T>::is_signed)
1664
+ ? static_cast <T>(floor (dfReal + 0.5 ))
1665
+ : static_cast <T>(dfReal + 0.5 );
1666
+ }
1667
+ else
1668
+ {
1669
+ pDst[iDstOffset] = static_cast <T>(dfReal);
1590
1670
}
1591
1671
1592
- return true ;
1672
+ AvoidNoData<T>(poWK, iBand, iDstOffset) ;
1593
1673
}
1594
1674
1595
1675
/* ***********************************************************************/
@@ -1724,83 +1804,55 @@ static bool GWKSetPixelValue(const GDALWarpKernel *poWK, int iBand,
1724
1804
(dfDensity + dfDstInfluence);
1725
1805
}
1726
1806
1727
- /* -------------------------------------------------------------------- */
1728
- /* Actually apply the destination value. */
1729
- /* */
1730
- /* Avoid using the destination nodata value for integer datatypes */
1731
- /* if by chance it is equal to the computed pixel value. */
1732
- /* -------------------------------------------------------------------- */
1733
-
1734
- // TODO(schwehr): Can we make this a template?
1735
- #define CLAMP (type ) \
1736
- do \
1737
- { \
1738
- type *_pDst = reinterpret_cast <type *>(pabyDst); \
1739
- if (dfReal < static_cast <double >(std::numeric_limits<type>::min ())) \
1740
- _pDst[iDstOffset] = \
1741
- static_cast <type>(std::numeric_limits<type>::min ()); \
1742
- else if (dfReal > \
1743
- static_cast <double >(std::numeric_limits<type>::max ())) \
1744
- _pDst[iDstOffset] = \
1745
- static_cast <type>(std::numeric_limits<type>::max ()); \
1746
- else \
1747
- _pDst[iDstOffset] = (std::numeric_limits<type>::is_signed) \
1748
- ? static_cast <type>(floor (dfReal + 0.5 )) \
1749
- : static_cast <type>(dfReal + 0.5 ); \
1750
- if (poWK->padfDstNoDataReal != nullptr && \
1751
- poWK->padfDstNoDataReal [iBand] == \
1752
- static_cast <double >(_pDst[iDstOffset])) \
1753
- { \
1754
- if (_pDst[iDstOffset] == \
1755
- static_cast <type>(std::numeric_limits<type>::min ())) \
1756
- _pDst[iDstOffset] = \
1757
- static_cast <type>(std::numeric_limits<type>::min () + 1 ); \
1758
- else \
1759
- _pDst[iDstOffset]--; \
1760
- } \
1761
- } while (false )
1807
+ /* -------------------------------------------------------------------- */
1808
+ /* Actually apply the destination value. */
1809
+ /* */
1810
+ /* Avoid using the destination nodata value for integer datatypes */
1811
+ /* if by chance it is equal to the computed pixel value. */
1812
+ /* -------------------------------------------------------------------- */
1762
1813
1763
1814
switch (poWK->eWorkingDataType )
1764
1815
{
1765
1816
case GDT_Byte:
1766
- CLAMP ( GByte);
1817
+ ClampRoundAndAvoidNoData< GByte>(poWK, iBand, iDstOffset, dfReal );
1767
1818
break ;
1768
1819
1769
1820
case GDT_Int8:
1770
- CLAMP ( GInt8);
1821
+ ClampRoundAndAvoidNoData< GInt8>(poWK, iBand, iDstOffset, dfReal );
1771
1822
break ;
1772
1823
1773
1824
case GDT_Int16:
1774
- CLAMP ( GInt16);
1825
+ ClampRoundAndAvoidNoData< GInt16>(poWK, iBand, iDstOffset, dfReal );
1775
1826
break ;
1776
1827
1777
1828
case GDT_UInt16:
1778
- CLAMP ( GUInt16);
1829
+ ClampRoundAndAvoidNoData< GUInt16>(poWK, iBand, iDstOffset, dfReal );
1779
1830
break ;
1780
1831
1781
1832
case GDT_UInt32:
1782
- CLAMP ( GUInt32);
1833
+ ClampRoundAndAvoidNoData< GUInt32>(poWK, iBand, iDstOffset, dfReal );
1783
1834
break ;
1784
1835
1785
1836
case GDT_Int32:
1786
- CLAMP ( GInt32);
1837
+ ClampRoundAndAvoidNoData< GInt32>(poWK, iBand, iDstOffset, dfReal );
1787
1838
break ;
1788
1839
1789
1840
case GDT_UInt64:
1790
- CLAMP (std::uint64_t );
1841
+ ClampRoundAndAvoidNoData<std::uint64_t >(poWK, iBand, iDstOffset,
1842
+ dfReal);
1791
1843
break ;
1792
1844
1793
1845
case GDT_Int64:
1794
- CLAMP (std::int64_t );
1846
+ ClampRoundAndAvoidNoData<std::int64_t >(poWK, iBand, iDstOffset,
1847
+ dfReal);
1795
1848
break ;
1796
1849
1797
1850
case GDT_Float32:
1798
- reinterpret_cast <float *>(pabyDst)[iDstOffset] =
1799
- static_cast <float >(dfReal);
1851
+ ClampRoundAndAvoidNoData<float >(poWK, iBand, iDstOffset, dfReal);
1800
1852
break ;
1801
1853
1802
1854
case GDT_Float64:
1803
- reinterpret_cast <double *>(pabyDst)[iDstOffset] = dfReal;
1855
+ ClampRoundAndAvoidNoData <double >(poWK, iBand, iDstOffset, dfReal) ;
1804
1856
break ;
1805
1857
1806
1858
case GDT_CInt16:
@@ -1983,44 +2035,45 @@ static bool GWKSetPixelValueReal(const GDALWarpKernel *poWK, int iBand,
1983
2035
switch (poWK->eWorkingDataType )
1984
2036
{
1985
2037
case GDT_Byte:
1986
- CLAMP ( GByte);
2038
+ ClampRoundAndAvoidNoData< GByte>(poWK, iBand, iDstOffset, dfReal );
1987
2039
break ;
1988
2040
1989
2041
case GDT_Int8:
1990
- CLAMP ( GInt8);
2042
+ ClampRoundAndAvoidNoData< GInt8>(poWK, iBand, iDstOffset, dfReal );
1991
2043
break ;
1992
2044
1993
2045
case GDT_Int16:
1994
- CLAMP ( GInt16);
2046
+ ClampRoundAndAvoidNoData< GInt16>(poWK, iBand, iDstOffset, dfReal );
1995
2047
break ;
1996
2048
1997
2049
case GDT_UInt16:
1998
- CLAMP ( GUInt16);
2050
+ ClampRoundAndAvoidNoData< GUInt16>(poWK, iBand, iDstOffset, dfReal );
1999
2051
break ;
2000
2052
2001
2053
case GDT_UInt32:
2002
- CLAMP ( GUInt32);
2054
+ ClampRoundAndAvoidNoData< GUInt32>(poWK, iBand, iDstOffset, dfReal );
2003
2055
break ;
2004
2056
2005
2057
case GDT_Int32:
2006
- CLAMP ( GInt32);
2058
+ ClampRoundAndAvoidNoData< GInt32>(poWK, iBand, iDstOffset, dfReal );
2007
2059
break ;
2008
2060
2009
2061
case GDT_UInt64:
2010
- CLAMP (std::uint64_t );
2062
+ ClampRoundAndAvoidNoData<std::uint64_t >(poWK, iBand, iDstOffset,
2063
+ dfReal);
2011
2064
break ;
2012
2065
2013
2066
case GDT_Int64:
2014
- CLAMP (std::int64_t );
2067
+ ClampRoundAndAvoidNoData<std::int64_t >(poWK, iBand, iDstOffset,
2068
+ dfReal);
2015
2069
break ;
2016
2070
2017
2071
case GDT_Float32:
2018
- reinterpret_cast <float *>(pabyDst)[iDstOffset] =
2019
- static_cast <float >(dfReal);
2072
+ ClampRoundAndAvoidNoData<float >(poWK, iBand, iDstOffset, dfReal);
2020
2073
break ;
2021
2074
2022
2075
case GDT_Float64:
2023
- reinterpret_cast <double *>(pabyDst)[iDstOffset] = dfReal;
2076
+ ClampRoundAndAvoidNoData <double >(poWK, iBand, iDstOffset, dfReal) ;
2024
2077
break ;
2025
2078
2026
2079
case GDT_CInt16:
@@ -6678,24 +6731,8 @@ template <class T> static void GWKNearestThread(void *pData)
6678
6731
padfZ[iDstX] * dfMultFactorVerticalShiftPipeline);
6679
6732
}
6680
6733
6681
- if (dfBandDensity < 1.0 )
6682
- {
6683
- if (dfBandDensity == 0.0 )
6684
- {
6685
- // Do nothing.
6686
- }
6687
- else
6688
- {
6689
- // Let the general code take care of mixing.
6690
- GWKSetPixelValueRealT (poWK, iBand, iDstOffset,
6691
- dfBandDensity, value);
6692
- }
6693
- }
6694
- else
6695
- {
6696
- reinterpret_cast <T *>(
6697
- poWK->papabyDstImage [iBand])[iDstOffset] = value;
6698
- }
6734
+ GWKSetPixelValueRealT (poWK, iBand, iDstOffset,
6735
+ dfBandDensity, value);
6699
6736
}
6700
6737
}
6701
6738
@@ -6810,6 +6847,11 @@ static CPLErr GWKNearestShort(GDALWarpKernel *poWK)
6810
6847
return GWKRun (poWK, " GWKNearestShort" , GWKNearestThread<GInt16>);
6811
6848
}
6812
6849
6850
+ static CPLErr GWKNearestUnsignedShort (GDALWarpKernel *poWK)
6851
+ {
6852
+ return GWKRun (poWK, " GWKNearestUnsignedShort" , GWKNearestThread<GUInt16>);
6853
+ }
6854
+
6813
6855
static CPLErr GWKNearestNoMasksOrDstDensityOnlyFloat (GDALWarpKernel *poWK)
6814
6856
{
6815
6857
return GWKRun (
0 commit comments