LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_color_merge.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 367 377 97.3 %
Date: 2025-09-10 17:48:50 Functions: 19 19 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "color-merge" step of "raster pipeline"
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
       9             :  * Copyright (c) 2009, Frank Warmerdam
      10             : 
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gdalalg_raster_color_merge.h"
      15             : 
      16             : #include "cpl_conv.h"
      17             : #include "gdal_priv.h"
      18             : 
      19             : #include <algorithm>
      20             : #include <limits>
      21             : 
      22             : #if defined(__x86_64) || defined(_M_X64)
      23             : #define HAVE_SSE2
      24             : #endif
      25             : #ifdef HAVE_SSE2
      26             : #include "gdalsse_priv.h"
      27             : #endif
      28             : 
      29             : //! @cond Doxygen_Suppress
      30             : 
      31             : #ifndef _
      32             : #define _(x) (x)
      33             : #endif
      34             : 
      35             : /************************************************************************/
      36             : /*     GDALRasterColorMergeAlgorithm::GDALRasterColorMergeAlgorithm()   */
      37             : /************************************************************************/
      38             : 
      39          71 : GDALRasterColorMergeAlgorithm::GDALRasterColorMergeAlgorithm(
      40          71 :     bool standaloneStep)
      41             :     : GDALRasterPipelineStepAlgorithm(
      42             :           NAME, DESCRIPTION, HELP_URL,
      43           0 :           ConstructorOptions()
      44          71 :               .SetStandaloneStep(standaloneStep)
      45          71 :               .SetAddDefaultArguments(false)
      46         142 :               .SetInputDatasetHelpMsg(_("Input RGB/RGBA raster dataset"))
      47         142 :               .SetInputDatasetAlias("color-input")
      48         142 :               .SetInputDatasetMetaVar("COLOR-INPUT")
      49         213 :               .SetOutputDatasetHelpMsg(_("Output RGB/RGBA raster dataset")))
      50             : {
      51          71 :     const auto AddGrayscaleDataset = [this]()
      52             :     {
      53             :         auto &arg = AddArg("grayscale", 0, _("Grayscale dataset"),
      54         142 :                            &m_grayScaleDataset, GDAL_OF_RASTER)
      55          71 :                         .SetPositional()
      56          71 :                         .SetRequired();
      57             : 
      58          71 :         SetAutoCompleteFunctionForFilename(arg, GDAL_OF_RASTER);
      59          71 :     };
      60             : 
      61          71 :     if (standaloneStep)
      62             :     {
      63          20 :         AddRasterInputArgs(false, false);
      64          20 :         AddGrayscaleDataset();
      65          20 :         AddProgressArg();
      66          20 :         AddRasterOutputArgs(false);
      67             :     }
      68             :     else
      69             :     {
      70          51 :         AddRasterHiddenInputDatasetArg();
      71          51 :         AddGrayscaleDataset();
      72             :     }
      73          71 : }
      74             : 
      75             : namespace
      76             : {
      77             : 
      78             : /************************************************************************/
      79             : /*                        HSVMergeDataset                               */
      80             : /************************************************************************/
      81             : 
      82             : class HSVMergeDataset final : public GDALDataset
      83             : {
      84             :   public:
      85             :     HSVMergeDataset(GDALDataset &oColorDS, GDALDataset &oGrayScaleDS);
      86             : 
      87           2 :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
      88             :     {
      89           2 :         return m_oColorDS.GetGeoTransform(gt);
      90             :     }
      91             : 
      92           2 :     const OGRSpatialReference *GetSpatialRef() const override
      93             :     {
      94           2 :         return m_oColorDS.GetSpatialRef();
      95             :     }
      96             : 
      97             :     bool AcquireSourcePixels(int nXOff, int nYOff, int nXSize, int nYSize,
      98             :                              int nBufXSize, int nBufYSize,
      99             :                              GDALRasterIOExtraArg *psExtraArg);
     100             : 
     101             :   protected:
     102             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
     103             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
     104             :                      GDALDataType eBufType, int nBandCount,
     105             :                      BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
     106             :                      GSpacing nLineSpace, GSpacing nBandSpace,
     107             :                      GDALRasterIOExtraArg *psExtraArg) override;
     108             : 
     109             :   private:
     110             :     friend class HSVMergeBand;
     111             :     GDALDataset &m_oColorDS;
     112             :     GDALDataset &m_oGrayScaleDS;
     113             :     std::vector<std::unique_ptr<HSVMergeDataset>> m_apoOverviews{};
     114             :     int m_nCachedXOff = 0;
     115             :     int m_nCachedYOff = 0;
     116             :     int m_nCachedXSize = 0;
     117             :     int m_nCachedYSize = 0;
     118             :     int m_nCachedBufXSize = 0;
     119             :     int m_nCachedBufYSize = 0;
     120             :     GDALRasterIOExtraArg m_sCachedExtraArg{};
     121             :     std::vector<GByte> m_abyBuffer{};
     122             :     bool m_ioError = false;
     123             : };
     124             : 
     125             : /************************************************************************/
     126             : /*                           rgb_to_hs()                                */
     127             : /************************************************************************/
     128             : 
     129             : // rgb comes in as [r,g,b] with values in the range [0,255]. The returned
     130             : // values will be with hue and saturation in the range [0,1].
     131             : 
     132             : // Derived from hsv_merge.py
     133             : 
     134     1050400 : static void rgb_to_hs(int r, int g, int b, float *h, float *s)
     135             : {
     136             :     int minc, maxc;
     137     1050400 :     if (r <= g)
     138             :     {
     139      534309 :         if (r <= b)
     140             :         {
     141      359156 :             minc = r;
     142      359156 :             maxc = std::max(g, b);
     143             :         }
     144             :         else /* b < r */
     145             :         {
     146      175153 :             minc = b;
     147      175153 :             maxc = g;
     148             :         }
     149             :     }
     150             :     else /* g < r */
     151             :     {
     152      516096 :         if (g <= b)
     153             :         {
     154      349440 :             minc = g;
     155      349440 :             maxc = std::max(r, b);
     156             :         }
     157             :         else /* b < g */
     158             :         {
     159      166656 :             minc = b;
     160      166656 :             maxc = r;
     161             :         }
     162             :     }
     163     1050400 :     const int maxc_minus_minc = maxc - minc;
     164     1050400 :     if (s)
     165     1050400 :         *s = maxc_minus_minc / static_cast<float>(std::max(1, maxc));
     166     1050400 :     if (h)
     167             :     {
     168     1050400 :         const float maxc_minus_minc_times_6 =
     169     1050400 :             maxc_minus_minc == 0 ? 1.0f : 6.0f * maxc_minus_minc;
     170     1050400 :         if (maxc == b)
     171      358501 :             *h = 4.0f / 6.0f + (r - g) / maxc_minus_minc_times_6;
     172      691904 :         else if (maxc == g)
     173      350528 :             *h = 2.0f / 6.0f + (b - r) / maxc_minus_minc_times_6;
     174             :         else
     175             :         {
     176      341376 :             const float tmp = (g - b) / maxc_minus_minc_times_6;
     177      341376 :             *h = tmp < 0.0f ? tmp + 1.0f : tmp;
     178             :         }
     179             :     }
     180     1050400 : }
     181             : 
     182             : /************************************************************************/
     183             : /*                           choose_among()                             */
     184             : /************************************************************************/
     185             : 
     186             : template <typename T>
     187     1577620 : static inline T choose_among(int idx, T a0, T a1, T a2, T a3, T a4, T a5)
     188             : {
     189     1577620 :     switch (idx)
     190             :     {
     191      262080 :         case 0:
     192      262080 :             return a0;
     193      263055 :         case 1:
     194      263055 :             return a1;
     195      264039 :         case 2:
     196      264039 :             return a2;
     197      263778 :         case 3:
     198      263778 :             return a3;
     199      262591 :         case 4:
     200      262591 :             return a4;
     201      262080 :         default:
     202      262080 :             break;
     203             :     }
     204      262080 :     return a5;
     205             : }
     206             : 
     207             : /************************************************************************/
     208             : /*                           hsv_to_rgb()                               */
     209             : /************************************************************************/
     210             : 
     211             : // hsv comes in as [h,s,v] with hue and saturation in the range [0,1],
     212             : // but value in the range [0,255].
     213             : 
     214             : // Derived from hsv_merge.py
     215             : 
     216     1050400 : static void hsv_to_rgb(float h, float s, GByte v, GByte *r, GByte *g, GByte *b)
     217             : {
     218     1050400 :     const int i = static_cast<int>(6.0f * h);
     219     1050400 :     const float f = 6.0f * h - i;
     220     1050400 :     const GByte p = static_cast<GByte>(v * (1.0f - s) + 0.5f);
     221     1050400 :     const GByte q = static_cast<GByte>(v * (1.0f - s * f) + 0.5f);
     222     1050400 :     const GByte t = static_cast<GByte>(v * (1.0f - s * (1.0f - f)) + 0.5f);
     223             : 
     224     1050400 :     if (r)
     225      525875 :         *r = choose_among(i, v, q, p, p, t, v);
     226     1050400 :     if (g)
     227      525874 :         *g = choose_among(i, t, v, v, q, p, p);
     228     1050400 :     if (b)
     229      525874 :         *b = choose_among(i, p, p, t, v, v, q);
     230     1050400 : }
     231             : 
     232             : /************************************************************************/
     233             : /*                           XMM_RGB_to_HS()                            */
     234             : /************************************************************************/
     235             : 
     236             : #ifdef HAVE_SSE2
     237             : static inline void
     238      538808 : XMM_RGB_to_HS(const GByte *CPL_RESTRICT pInR, const GByte *CPL_RESTRICT pInG,
     239             :               const GByte *CPL_RESTRICT pInB, const XMMReg4Float &zero,
     240             :               const XMMReg4Float &one, const XMMReg4Float &six,
     241             :               const XMMReg4Float &two_over_six,
     242             :               const XMMReg4Float &four_over_six, XMMReg4Float &h,
     243             :               XMMReg4Float &s)
     244             : {
     245      538808 :     const auto r = XMMReg4Float::Load4Val(pInR);
     246      538808 :     const auto g = XMMReg4Float::Load4Val(pInG);
     247      538808 :     const auto b = XMMReg4Float::Load4Val(pInB);
     248      538808 :     const auto minc = XMMReg4Float::Min(XMMReg4Float::Min(r, g), b);
     249      538808 :     const auto maxc = XMMReg4Float::Max(XMMReg4Float::Max(r, g), b);
     250      538808 :     const auto max_minus_min = maxc - minc;
     251      538808 :     s = max_minus_min / XMMReg4Float::Max(one, maxc);
     252             :     const auto inv_max_minus_min_times_6_0 =
     253      538808 :         XMMReg4Float::Ternary(XMMReg4Float::Equals(max_minus_min, zero), one,
     254      538808 :                               six * max_minus_min)
     255      538808 :             .inverse();
     256      538808 :     const auto tmp = (g - b) * inv_max_minus_min_times_6_0;
     257      538808 :     h = XMMReg4Float::Ternary(
     258      538808 :         XMMReg4Float::Equals(maxc, b),
     259      538808 :         four_over_six + (r - g) * inv_max_minus_min_times_6_0,
     260      538808 :         XMMReg4Float::Ternary(
     261      538808 :             XMMReg4Float::Equals(maxc, g),
     262      538808 :             two_over_six + (b - r) * inv_max_minus_min_times_6_0,
     263      538808 :             XMMReg4Float::Ternary(XMMReg4Float::Lesser(tmp, zero), tmp + one,
     264      538808 :                                   tmp)));
     265      538808 : }
     266             : #endif
     267             : 
     268             : /************************************************************************/
     269             : /*                         patch_value_line()                           */
     270             : /************************************************************************/
     271             : 
     272             : static
     273             : #ifdef __GNUC__
     274             :     __attribute__((__noinline__))
     275             : #endif
     276             :     void
     277         695 :     patch_value_line(int nCount, const GByte *CPL_RESTRICT pInR,
     278             :                      const GByte *CPL_RESTRICT pInG,
     279             :                      const GByte *CPL_RESTRICT pInB,
     280             :                      const GByte *CPL_RESTRICT pInGray,
     281             :                      GByte *CPL_RESTRICT pOutR, GByte *CPL_RESTRICT pOutG,
     282             :                      GByte *CPL_RESTRICT pOutB)
     283             : {
     284         695 :     int i = 0;
     285             : #ifdef HAVE_SSE2
     286         695 :     const auto zero = XMMReg4Float::Zero();
     287         695 :     const auto one = XMMReg4Float::Set1(1.0f);
     288         695 :     const auto six = XMMReg4Float::Set1(6.0f);
     289         695 :     const auto two_over_six = XMMReg4Float::Set1(2.0f / 6.0f);
     290         695 :     const auto four_over_six = two_over_six + two_over_six;
     291             : 
     292         695 :     constexpr int ELTS = 8;
     293      270099 :     for (; i + (ELTS - 1) < nCount; i += ELTS)
     294             :     {
     295      269404 :         XMMReg4Float h0, s0;
     296      269404 :         XMM_RGB_to_HS(pInR + i, pInG + i, pInB + i, zero, one, six,
     297             :                       two_over_six, four_over_six, h0, s0);
     298      269404 :         XMMReg4Float h1, s1;
     299      269404 :         XMM_RGB_to_HS(pInR + i + ELTS / 2, pInG + i + ELTS / 2,
     300      269404 :                       pInB + i + ELTS / 2, zero, one, six, two_over_six,
     301             :                       four_over_six, h1, s1);
     302             : 
     303      269404 :         XMMReg4Float v0, v1;
     304      269404 :         XMMReg4Float::Load8Val(pInGray + i, v0, v1);
     305             : 
     306      269404 :         const auto half = XMMReg4Float::Set1(0.5f);
     307      269404 :         const auto six_h0 = six * h0;
     308      269404 :         const auto idx0 = six_h0.truncate_to_int();
     309      269404 :         const auto f0 = six_h0 - idx0.to_float();
     310      269404 :         const auto p0 = (v0 * (one - s0) + half).truncate_to_int();
     311      269404 :         const auto q0 = (v0 * (one - s0 * f0) + half).truncate_to_int();
     312      269404 :         const auto t0 = (v0 * (one - s0 * (one - f0)) + half).truncate_to_int();
     313             : 
     314      269404 :         const auto six_h1 = six * h1;
     315      269404 :         const auto idx1 = six_h1.truncate_to_int();
     316      269404 :         const auto f1 = six_h1 - idx1.to_float();
     317      269404 :         const auto p1 = (v1 * (one - s1) + half).truncate_to_int();
     318      269404 :         const auto q1 = (v1 * (one - s1 * f1) + half).truncate_to_int();
     319      269404 :         const auto t1 = (v1 * (one - s1 * (one - f1)) + half).truncate_to_int();
     320             : 
     321      269404 :         const auto idx = XMMReg8Byte::Pack(idx0, idx1);
     322             :         const auto v =
     323      269404 :             XMMReg8Byte::Pack(v0.truncate_to_int(), v1.truncate_to_int());
     324      269404 :         const auto p = XMMReg8Byte::Pack(p0, p1);
     325      269404 :         const auto q = XMMReg8Byte::Pack(q0, q1);
     326      269404 :         const auto t = XMMReg8Byte::Pack(t0, t1);
     327             : 
     328      269404 :         const auto equalsTo0 = XMMReg8Byte::Equals(idx, XMMReg8Byte::Zero());
     329      269404 :         const auto one8Byte = XMMReg8Byte::Set1(1);
     330      269404 :         const auto equalsTo1 = XMMReg8Byte::Equals(idx, one8Byte);
     331      269404 :         const auto two8Byte = one8Byte + one8Byte;
     332      269404 :         const auto equalsTo2 = XMMReg8Byte::Equals(idx, two8Byte);
     333      269404 :         const auto four8Byte = two8Byte + two8Byte;
     334      269404 :         const auto equalsTo4 = XMMReg8Byte::Equals(idx, four8Byte);
     335      269404 :         const auto equalsTo3 = XMMReg8Byte::Equals(idx, four8Byte - one8Byte);
     336             :         // clang-format off
     337      269404 :         if (pOutR)
     338             :         {
     339             :             const auto out_r =
     340             :                 XMMReg8Byte::Ternary(equalsTo0, v,
     341      134702 :                 XMMReg8Byte::Ternary(equalsTo1, q,
     342      134702 :                 XMMReg8Byte::Ternary(XMMReg8Byte::Or(equalsTo2, equalsTo3), p,
     343      269404 :                 XMMReg8Byte::Ternary(equalsTo4, t, v))));
     344      134702 :             out_r.Store8Val(pOutR + i);
     345             :         }
     346      269404 :         if (pOutG)
     347             :         {
     348             :             const auto out_g =
     349             :                 XMMReg8Byte::Ternary(equalsTo0, t,
     350      118318 :                 XMMReg8Byte::Ternary(XMMReg8Byte::Or(equalsTo1, equalsTo2), v,
     351      236636 :                 XMMReg8Byte::Ternary(equalsTo3, q, p)));
     352      118318 :             out_g.Store8Val(pOutG + i);
     353             :         }
     354      269404 :         if (pOutB)
     355             :         {
     356             :             const auto out_b =
     357      118318 :                 XMMReg8Byte::Ternary(XMMReg8Byte::Or(equalsTo0, equalsTo1), p,
     358      118318 :                 XMMReg8Byte::Ternary(equalsTo2, t,
     359      118318 :                 XMMReg8Byte::Ternary(XMMReg8Byte::Or(equalsTo3, equalsTo4),
     360      118318 :                                      v, q)));
     361      118318 :             out_b.Store8Val(pOutB + i);
     362             :         }
     363             :         // clang-format on
     364             :     }
     365             : #endif
     366             : 
     367        2524 :     for (; i < nCount; ++i)
     368             :     {
     369             :         float h, s;
     370        1829 :         rgb_to_hs(pInR[i], pInG[i], pInB[i], &h, &s);
     371        5001 :         hsv_to_rgb(h, s, pInGray[i], pOutR ? pOutR + i : nullptr,
     372        3172 :                    pOutG ? pOutG + i : nullptr, pOutB ? pOutB + i : nullptr);
     373             :     }
     374         695 : }
     375             : 
     376             : /************************************************************************/
     377             : /*                           HSVMergeBand                               */
     378             : /************************************************************************/
     379             : 
     380             : class HSVMergeBand final : public GDALRasterBand
     381             : {
     382             :   public:
     383          46 :     HSVMergeBand(HSVMergeDataset &oHSVMergeDataset, int nBandIn)
     384          46 :         : m_oHSVMergeDataset(oHSVMergeDataset)
     385             :     {
     386          46 :         nBand = nBandIn;
     387          46 :         nRasterXSize = oHSVMergeDataset.GetRasterXSize();
     388          46 :         nRasterYSize = oHSVMergeDataset.GetRasterYSize();
     389          46 :         oHSVMergeDataset.m_oColorDS.GetRasterBand(1)->GetBlockSize(
     390             :             &nBlockXSize, &nBlockYSize);
     391          46 :         eDataType = GDT_Byte;
     392          46 :     }
     393             : 
     394          10 :     GDALColorInterp GetColorInterpretation() override
     395             :     {
     396          10 :         return m_oHSVMergeDataset.m_oColorDS.GetRasterBand(nBand)
     397          10 :             ->GetColorInterpretation();
     398             :     }
     399             : 
     400          18 :     int GetOverviewCount() override
     401             :     {
     402          18 :         return static_cast<int>(m_oHSVMergeDataset.m_apoOverviews.size());
     403             :     }
     404             : 
     405          14 :     GDALRasterBand *GetOverview(int idx) override
     406             :     {
     407          13 :         return idx >= 0 && idx < GetOverviewCount()
     408          27 :                    ? m_oHSVMergeDataset.m_apoOverviews[idx]->GetRasterBand(
     409             :                          nBand)
     410          14 :                    : nullptr;
     411             :     }
     412             : 
     413             :   protected:
     414          10 :     CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override
     415             :     {
     416          10 :         int nReqXSize = 0;
     417          10 :         int nReqYSize = 0;
     418          10 :         GetActualBlockSize(nBlockXOff, nBlockYOff, &nReqXSize, &nReqYSize);
     419          20 :         return RasterIO(GF_Read, nBlockXOff * nBlockXSize,
     420          10 :                         nBlockYOff * nBlockYSize, nReqXSize, nReqYSize, pData,
     421          10 :                         nReqXSize, nReqYSize, GDT_Byte, 1, nBlockXSize,
     422          20 :                         nullptr);
     423             :     }
     424             : 
     425             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
     426             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
     427             :                      GDALDataType eBufType, GSpacing nPixelSpace,
     428             :                      GSpacing nLineSpace,
     429             :                      GDALRasterIOExtraArg *psExtraArg) override;
     430             : 
     431             :   private:
     432             :     HSVMergeDataset &m_oHSVMergeDataset;
     433             : };
     434             : 
     435             : /************************************************************************/
     436             : /*                 HSVMergeDataset::HSVMergeDataset()                   */
     437             : /************************************************************************/
     438             : 
     439          14 : HSVMergeDataset::HSVMergeDataset(GDALDataset &oColorDS,
     440          14 :                                  GDALDataset &oGrayScaleDS)
     441          14 :     : m_oColorDS(oColorDS), m_oGrayScaleDS(oGrayScaleDS)
     442             : {
     443          14 :     CPLAssert(oColorDS.GetRasterCount() == 3 || oColorDS.GetRasterCount() == 4);
     444          14 :     CPLAssert(oColorDS.GetRasterXSize() == oGrayScaleDS.GetRasterXSize());
     445          14 :     CPLAssert(oColorDS.GetRasterYSize() == oGrayScaleDS.GetRasterYSize());
     446          14 :     nRasterXSize = oColorDS.GetRasterXSize();
     447          14 :     nRasterYSize = oColorDS.GetRasterYSize();
     448          14 :     const int nOvrCount = oGrayScaleDS.GetRasterBand(1)->GetOverviewCount();
     449          14 :     bool bCanCreateOvr = true;
     450          60 :     for (int iBand = 1; iBand <= oColorDS.GetRasterCount(); ++iBand)
     451             :     {
     452          46 :         SetBand(iBand, std::make_unique<HSVMergeBand>(*this, iBand));
     453          46 :         bCanCreateOvr =
     454          92 :             bCanCreateOvr &&
     455          46 :             oColorDS.GetRasterBand(iBand)->GetOverviewCount() == nOvrCount;
     456          54 :         for (int iOvr = 0; iOvr < nOvrCount && bCanCreateOvr; ++iOvr)
     457             :         {
     458             :             const auto poColorOvrBand =
     459           8 :                 oColorDS.GetRasterBand(iBand)->GetOverview(iOvr);
     460             :             const auto poGSOvrBand =
     461           8 :                 oGrayScaleDS.GetRasterBand(1)->GetOverview(iOvr);
     462           8 :             bCanCreateOvr =
     463           8 :                 poColorOvrBand->GetDataset() != &oColorDS &&
     464           8 :                 poColorOvrBand->GetDataset() == oColorDS.GetRasterBand(1)
     465           8 :                                                     ->GetOverview(iOvr)
     466           8 :                                                     ->GetDataset() &&
     467           8 :                 poGSOvrBand->GetDataset() != &oGrayScaleDS &&
     468          24 :                 poColorOvrBand->GetXSize() == poGSOvrBand->GetXSize() &&
     469           8 :                 poColorOvrBand->GetYSize() == poGSOvrBand->GetYSize();
     470             :         }
     471             :     }
     472             : 
     473          14 :     SetDescription(CPLSPrintf("Merge %s with %s", m_oColorDS.GetDescription(),
     474          14 :                               m_oGrayScaleDS.GetDescription()));
     475          14 :     SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
     476             : 
     477          14 :     if (bCanCreateOvr)
     478             :     {
     479          16 :         for (int iOvr = 0; iOvr < nOvrCount; ++iOvr)
     480             :         {
     481           2 :             m_apoOverviews.push_back(std::make_unique<HSVMergeDataset>(
     482           2 :                 *(oColorDS.GetRasterBand(1)->GetOverview(iOvr)->GetDataset()),
     483           2 :                 *(oGrayScaleDS.GetRasterBand(1)
     484           2 :                       ->GetOverview(iOvr)
     485           2 :                       ->GetDataset())));
     486             :         }
     487             :     }
     488          14 : }
     489             : 
     490             : /************************************************************************/
     491             : /*               HSVMergeDataset::AcquireSourcePixels()                 */
     492             : /************************************************************************/
     493             : 
     494         221 : bool HSVMergeDataset::AcquireSourcePixels(int nXOff, int nYOff, int nXSize,
     495             :                                           int nYSize, int nBufXSize,
     496             :                                           int nBufYSize,
     497             :                                           GDALRasterIOExtraArg *psExtraArg)
     498             : {
     499         221 :     if (nXOff == m_nCachedXOff && nYOff == m_nCachedYOff &&
     500          22 :         nXSize == m_nCachedXSize && nYSize == m_nCachedYSize &&
     501          10 :         nBufXSize == m_nCachedBufXSize && nBufYSize == m_nCachedBufYSize &&
     502          10 :         psExtraArg->eResampleAlg == m_sCachedExtraArg.eResampleAlg &&
     503          10 :         psExtraArg->bFloatingPointWindowValidity ==
     504          10 :             m_sCachedExtraArg.bFloatingPointWindowValidity &&
     505          10 :         (!psExtraArg->bFloatingPointWindowValidity ||
     506           0 :          (psExtraArg->dfXOff == m_sCachedExtraArg.dfXOff &&
     507           0 :           psExtraArg->dfYOff == m_sCachedExtraArg.dfYOff &&
     508           0 :           psExtraArg->dfXSize == m_sCachedExtraArg.dfXSize &&
     509           0 :           psExtraArg->dfYSize == m_sCachedExtraArg.dfYSize)))
     510             :     {
     511          10 :         return !m_abyBuffer.empty();
     512             :     }
     513             : 
     514         211 :     constexpr int N_COMPS_IN_BUFFER = 4;  //  RGB + Grayscale
     515             : 
     516         422 :     if (static_cast<size_t>(nBufXSize) >
     517         211 :         std::numeric_limits<size_t>::max() / nBufYSize / N_COMPS_IN_BUFFER)
     518             :     {
     519           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     520             :                  "Out of memory allocating temporary buffer");
     521           0 :         m_abyBuffer.clear();
     522           0 :         m_ioError = true;
     523           0 :         return false;
     524             :     }
     525             : 
     526         211 :     const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
     527             :     try
     528             :     {
     529         211 :         if (m_abyBuffer.size() < nPixelCount * N_COMPS_IN_BUFFER)
     530          11 :             m_abyBuffer.resize(nPixelCount * N_COMPS_IN_BUFFER);
     531             :     }
     532           4 :     catch (const std::exception &)
     533             :     {
     534           4 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     535             :                  "Out of memory allocating temporary buffer");
     536           4 :         m_abyBuffer.clear();
     537           4 :         m_ioError = true;
     538           4 :         return false;
     539             :     }
     540             : 
     541             :     const bool bOK =
     542         207 :         (m_oColorDS.RasterIO(
     543         207 :              GF_Read, nXOff, nYOff, nXSize, nYSize, m_abyBuffer.data(),
     544             :              nBufXSize, nBufYSize, GDT_Byte, 3, nullptr, 1, nBufXSize,
     545         412 :              static_cast<GSpacing>(nPixelCount), psExtraArg) == CE_None &&
     546         410 :          m_oGrayScaleDS.GetRasterBand(1)->RasterIO(
     547             :              GF_Read, nXOff, nYOff, nXSize, nYSize,
     548         205 :              m_abyBuffer.data() + nPixelCount * 3, nBufXSize, nBufYSize,
     549         207 :              GDT_Byte, 1, nBufXSize, psExtraArg) == CE_None);
     550         207 :     if (bOK)
     551             :     {
     552         205 :         m_nCachedXOff = nXOff;
     553         205 :         m_nCachedYOff = nYOff;
     554         205 :         m_nCachedXSize = nXSize;
     555         205 :         m_nCachedYSize = nYSize;
     556         205 :         m_nCachedBufXSize = nBufXSize;
     557         205 :         m_nCachedBufYSize = nBufYSize;
     558         205 :         m_sCachedExtraArg = *psExtraArg;
     559             :     }
     560             :     else
     561             :     {
     562           2 :         m_abyBuffer.clear();
     563           2 :         m_ioError = true;
     564             :     }
     565         207 :     return bOK;
     566             : }
     567             : 
     568             : /************************************************************************/
     569             : /*                     HSVMergeDataset::IRasterIO()                     */
     570             : /************************************************************************/
     571             : 
     572         206 : CPLErr HSVMergeDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     573             :                                   int nXSize, int nYSize, void *pData,
     574             :                                   int nBufXSize, int nBufYSize,
     575             :                                   GDALDataType eBufType, int nBandCount,
     576             :                                   BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
     577             :                                   GSpacing nLineSpace, GSpacing nBandSpace,
     578             :                                   GDALRasterIOExtraArg *psExtraArg)
     579             : {
     580             :     // Try to pass the request to the most appropriate overview dataset.
     581         206 :     if (nBufXSize < nXSize && nBufYSize < nYSize)
     582             :     {
     583           2 :         int bTried = FALSE;
     584           2 :         const CPLErr eErr = TryOverviewRasterIO(
     585             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
     586             :             eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
     587             :             nBandSpace, psExtraArg, &bTried);
     588           2 :         if (bTried)
     589           2 :             return eErr;
     590             :     }
     591             : 
     592         204 :     GByte *pabyDst = static_cast<GByte *>(pData);
     593         204 :     if (eRWFlag == GF_Read && eBufType == GDT_Byte && nBandCount == nBands &&
     594         203 :         panBandMap[0] == 1 && panBandMap[1] == 2 && panBandMap[2] == 3 &&
     595         196 :         (nBandCount == 3 || panBandMap[3] == 4) &&
     596         203 :         AcquireSourcePixels(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
     597         604 :                             psExtraArg) &&
     598         196 :         (nBandCount == 3 ||
     599         392 :          m_oColorDS.GetRasterBand(4)->RasterIO(
     600         196 :              GF_Read, nXOff, nYOff, nXSize, nYSize, pabyDst + nBandSpace * 3,
     601             :              nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace,
     602             :              psExtraArg) == CE_None))
     603             :     {
     604         197 :         const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
     605         197 :         const GByte *pabyR = m_abyBuffer.data();
     606         197 :         const GByte *pabyG = m_abyBuffer.data() + nPixelCount;
     607         197 :         const GByte *pabyB = m_abyBuffer.data() + nPixelCount * 2;
     608         197 :         const GByte *pabyGrayScale = m_abyBuffer.data() + nPixelCount * 3;
     609         197 :         size_t nSrcIdx = 0;
     610         516 :         for (int j = 0; j < nBufYSize; ++j)
     611             :         {
     612         319 :             auto nDstOffset = j * nLineSpace;
     613         319 :             if (nPixelSpace == 1 && nLineSpace >= nPixelSpace * nBufXSize &&
     614         317 :                 nBandSpace >= nLineSpace * nBufYSize)
     615             :             {
     616         317 :                 patch_value_line(nBufXSize, pabyR + nSrcIdx, pabyG + nSrcIdx,
     617             :                                  pabyB + nSrcIdx, pabyGrayScale + nSrcIdx,
     618         317 :                                  pabyDst + nDstOffset,
     619         317 :                                  pabyDst + nDstOffset + nBandSpace,
     620         317 :                                  pabyDst + nDstOffset + 2 * nBandSpace);
     621         317 :                 nSrcIdx += nBufXSize;
     622             :             }
     623             :             else
     624             :             {
     625      262146 :                 for (int i = 0; i < nBufXSize;
     626      262144 :                      ++i, ++nSrcIdx, nDstOffset += nPixelSpace)
     627             :                 {
     628             :                     float h, s;
     629      262144 :                     rgb_to_hs(pabyR[nSrcIdx], pabyG[nSrcIdx], pabyB[nSrcIdx],
     630             :                               &h, &s);
     631      262144 :                     hsv_to_rgb(h, s, pabyGrayScale[nSrcIdx],
     632      262144 :                                &pabyDst[nDstOffset + 0 * nBandSpace],
     633      262144 :                                &pabyDst[nDstOffset + 1 * nBandSpace],
     634      262144 :                                &pabyDst[nDstOffset + 2 * nBandSpace]);
     635             :                 }
     636             :             }
     637             :         }
     638             : 
     639         197 :         return CE_None;
     640             :     }
     641           7 :     else if (m_ioError)
     642             :     {
     643           6 :         return CE_Failure;
     644             :     }
     645             :     else
     646             :     {
     647           1 :         const CPLErr eErr = GDALDataset::IRasterIO(
     648             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
     649             :             eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
     650             :             nBandSpace, psExtraArg);
     651           1 :         m_ioError = eErr != CE_None;
     652           1 :         return eErr;
     653             :     }
     654             : }
     655             : 
     656             : /************************************************************************/
     657             : /*                   HSVMergeDataset::IRasterIO()                       */
     658             : /************************************************************************/
     659             : 
     660          29 : CPLErr HSVMergeBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     661             :                                int nXSize, int nYSize, void *pData,
     662             :                                int nBufXSize, int nBufYSize,
     663             :                                GDALDataType eBufType, GSpacing nPixelSpace,
     664             :                                GSpacing nLineSpace,
     665             :                                GDALRasterIOExtraArg *psExtraArg)
     666             : {
     667             :     // Try to pass the request to the most appropriate overview dataset.
     668          29 :     if (nBufXSize < nXSize && nBufYSize < nYSize)
     669             :     {
     670           1 :         int bTried = FALSE;
     671           1 :         const CPLErr eErr = TryOverviewRasterIO(
     672             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
     673             :             eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried);
     674           1 :         if (bTried)
     675           1 :             return eErr;
     676             :     }
     677             : 
     678          28 :     if (nBand >= 4)
     679             :     {
     680           3 :         return m_oHSVMergeDataset.m_oColorDS.GetRasterBand(nBand)->RasterIO(
     681             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
     682           3 :             eBufType, nPixelSpace, nLineSpace, psExtraArg);
     683             :     }
     684          43 :     else if (eRWFlag == GF_Read && eBufType == GDT_Byte &&
     685          18 :              m_oHSVMergeDataset.AcquireSourcePixels(nXOff, nYOff, nXSize,
     686             :                                                     nYSize, nBufXSize,
     687             :                                                     nBufYSize, psExtraArg))
     688             :     {
     689          18 :         GByte *pabyDst = static_cast<GByte *>(pData);
     690          18 :         const GByte *pabyR = m_oHSVMergeDataset.m_abyBuffer.data();
     691          18 :         const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
     692             :         const GByte *pabyG =
     693          18 :             m_oHSVMergeDataset.m_abyBuffer.data() + nPixelCount;
     694             :         const GByte *pabyB =
     695          18 :             m_oHSVMergeDataset.m_abyBuffer.data() + nPixelCount * 2;
     696             :         const GByte *pabyGrayScale =
     697          18 :             m_oHSVMergeDataset.m_abyBuffer.data() + nPixelCount * 3;
     698          18 :         size_t nSrcIdx = 0;
     699         402 :         for (int j = 0; j < nBufYSize; ++j)
     700             :         {
     701         384 :             auto nDstOffset = j * nLineSpace;
     702         384 :             if (nPixelSpace == 1 && nLineSpace >= nPixelSpace * nBufXSize)
     703             :             {
     704         884 :                 patch_value_line(nBufXSize, pabyR + nSrcIdx, pabyG + nSrcIdx,
     705             :                                  pabyB + nSrcIdx, pabyGrayScale + nSrcIdx,
     706         378 :                                  nBand == 1 ? pabyDst + nDstOffset : nullptr,
     707         378 :                                  nBand == 2 ? pabyDst + nDstOffset : nullptr,
     708         378 :                                  nBand == 3 ? pabyDst + nDstOffset : nullptr);
     709         378 :                 nSrcIdx += nBufXSize;
     710             :             }
     711             :             else
     712             :             {
     713      786438 :                 for (int i = 0; i < nBufXSize;
     714      786432 :                      ++i, ++nSrcIdx, nDstOffset += nPixelSpace)
     715             :                 {
     716             :                     float h, s;
     717      786432 :                     rgb_to_hs(pabyR[nSrcIdx], pabyG[nSrcIdx], pabyB[nSrcIdx],
     718             :                               &h, &s);
     719      786432 :                     if (nBand == 1)
     720             :                     {
     721      262144 :                         hsv_to_rgb(h, s, pabyGrayScale[nSrcIdx],
     722      262144 :                                    &pabyDst[nDstOffset], nullptr, nullptr);
     723             :                     }
     724      524288 :                     else if (nBand == 2)
     725             :                     {
     726      262144 :                         hsv_to_rgb(h, s, pabyGrayScale[nSrcIdx], nullptr,
     727      262144 :                                    &pabyDst[nDstOffset], nullptr);
     728             :                     }
     729             :                     else
     730             :                     {
     731      262144 :                         CPLAssert(nBand == 3);
     732      262144 :                         hsv_to_rgb(h, s, pabyGrayScale[nSrcIdx], nullptr,
     733      262144 :                                    nullptr, &pabyDst[nDstOffset]);
     734             :                     }
     735             :                 }
     736             :             }
     737             :         }
     738             : 
     739          18 :         return CE_None;
     740             :     }
     741           7 :     else if (m_oHSVMergeDataset.m_ioError)
     742             :     {
     743           0 :         return CE_Failure;
     744             :     }
     745             :     else
     746             :     {
     747           7 :         const CPLErr eErr = GDALRasterBand::IRasterIO(
     748             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
     749             :             eBufType, nPixelSpace, nLineSpace, psExtraArg);
     750           7 :         m_oHSVMergeDataset.m_ioError = eErr != CE_None;
     751           7 :         return eErr;
     752             :     }
     753             : }
     754             : 
     755             : }  // namespace
     756             : 
     757             : /************************************************************************/
     758             : /*                 GDALRasterColorMergeAlgorithm::RunStep()             */
     759             : /************************************************************************/
     760             : 
     761          19 : bool GDALRasterColorMergeAlgorithm::RunStep(GDALPipelineStepRunContext &)
     762             : {
     763          19 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     764          19 :     CPLAssert(poSrcDS);
     765             : 
     766          19 :     auto poGrayScaleDS = m_grayScaleDataset.GetDatasetRef();
     767          19 :     CPLAssert(poGrayScaleDS);
     768             : 
     769          36 :     if ((poSrcDS->GetRasterCount() != 3 && poSrcDS->GetRasterCount() != 4) ||
     770          17 :         poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
     771             :     {
     772           3 :         ReportError(CE_Failure, CPLE_NotSupported,
     773             :                     "Only 3 or 4-band Byte dataset supported as input");
     774           3 :         return false;
     775             :     }
     776             : 
     777          31 :     if (poGrayScaleDS->GetRasterCount() != 1 ||
     778          15 :         poGrayScaleDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
     779             :     {
     780           2 :         ReportError(CE_Failure, CPLE_NotSupported,
     781             :                     "Only 1-band Byte dataset supported as grayscale dataset");
     782           2 :         return false;
     783             :     }
     784             : 
     785          27 :     if (poSrcDS->GetRasterXSize() != poGrayScaleDS->GetRasterXSize() ||
     786          13 :         poSrcDS->GetRasterYSize() != poGrayScaleDS->GetRasterYSize())
     787             :     {
     788           2 :         ReportError(CE_Failure, CPLE_IllegalArg,
     789             :                     "Input RGB/RGBA dataset and grayscale dataset must have "
     790             :                     "the same dimensions");
     791           2 :         return false;
     792             :     }
     793             : 
     794          12 :     m_outputDataset.Set(
     795          24 :         std::make_unique<HSVMergeDataset>(*poSrcDS, *poGrayScaleDS));
     796             : 
     797          12 :     return true;
     798             : }
     799             : 
     800             : GDALRasterColorMergeAlgorithmStandalone::
     801             :     ~GDALRasterColorMergeAlgorithmStandalone() = default;
     802             : 
     803             : //! @endcond

Generated by: LCOV version 1.14