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-06-19 12:30:01 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          39 : GDALRasterColorMergeAlgorithm::GDALRasterColorMergeAlgorithm(
      40          39 :     bool standaloneStep)
      41             :     : GDALRasterPipelineStepAlgorithm(
      42             :           NAME, DESCRIPTION, HELP_URL,
      43           0 :           ConstructorOptions()
      44          39 :               .SetStandaloneStep(standaloneStep)
      45          39 :               .SetAddDefaultArguments(false)
      46          78 :               .SetInputDatasetHelpMsg(_("Input RGB/RGBA raster dataset"))
      47          78 :               .SetInputDatasetAlias("color-input")
      48          78 :               .SetInputDatasetMetaVar("COLOR-INPUT")
      49         117 :               .SetOutputDatasetHelpMsg(_("Output RGB/RGBA raster dataset")))
      50             : {
      51          39 :     const auto AddGrayscaleDataset = [this]()
      52             :     {
      53             :         auto &arg = AddArg("grayscale", 0, _("Grayscale dataset"),
      54          78 :                            &m_grayScaleDataset, GDAL_OF_RASTER)
      55          39 :                         .SetPositional()
      56          39 :                         .SetRequired();
      57             : 
      58          39 :         SetAutoCompleteFunctionForFilename(arg, GDAL_OF_RASTER);
      59          39 :     };
      60             : 
      61          39 :     if (standaloneStep)
      62             :     {
      63          20 :         AddRasterInputArgs(false, false);
      64          20 :         AddGrayscaleDataset();
      65          20 :         AddProgressArg();
      66          20 :         AddRasterOutputArgs(false);
      67             :     }
      68             :     else
      69             :     {
      70          19 :         AddRasterHiddenInputDatasetArg();
      71          19 :         AddGrayscaleDataset();
      72             :     }
      73          39 : }
      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           1 :     CPLErr GetGeoTransform(double *padfGT) override
      88             :     {
      89           1 :         return m_oColorDS.GetGeoTransform(padfGT);
      90             :     }
      91             : 
      92           1 :     const OGRSpatialReference *GetSpatialRef() const override
      93             :     {
      94           1 :         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     1049920 : static void rgb_to_hs(int r, int g, int b, float *h, float *s)
     135             : {
     136             :     int minc, maxc;
     137     1049920 :     if (r <= g)
     138             :     {
     139      533825 :         if (r <= b)
     140             :         {
     141      358888 :             minc = r;
     142      358888 :             maxc = std::max(g, b);
     143             :         }
     144             :         else /* b < r */
     145             :         {
     146      174937 :             minc = b;
     147      174937 :             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     1049920 :     const int maxc_minus_minc = maxc - minc;
     164     1049920 :     if (s)
     165     1049920 :         *s = maxc_minus_minc / static_cast<float>(std::max(1, maxc));
     166     1049920 :     if (h)
     167             :     {
     168     1049920 :         const float maxc_minus_minc_times_6 =
     169     1049920 :             maxc_minus_minc == 0 ? 1.0f : 6.0f * maxc_minus_minc;
     170     1049920 :         if (maxc == b)
     171      358237 :             *h = 4.0f / 6.0f + (r - g) / maxc_minus_minc_times_6;
     172      691684 :         else if (maxc == g)
     173      350308 :             *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     1049920 : }
     181             : 
     182             : /************************************************************************/
     183             : /*                           choose_among()                             */
     184             : /************************************************************************/
     185             : 
     186             : template <typename T>
     187     1576900 : static inline T choose_among(int idx, T a0, T a1, T a2, T a3, T a4, T a5)
     188             : {
     189     1576900 :     switch (idx)
     190             :     {
     191      262080 :         case 0:
     192      262080 :             return a0;
     193      262731 :         case 1:
     194      262731 :             return a1;
     195      264033 :         case 2:
     196      264033 :             return a2;
     197      263382 :         case 3:
     198      263382 :             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     1049920 : static void hsv_to_rgb(float h, float s, GByte v, GByte *r, GByte *g, GByte *b)
     217             : {
     218     1049920 :     const int i = static_cast<int>(6.0f * h);
     219     1049920 :     const float f = 6.0f * h - i;
     220     1049920 :     const GByte p = static_cast<GByte>(v * (1.0f - s) + 0.5f);
     221     1049920 :     const GByte q = static_cast<GByte>(v * (1.0f - s * f) + 0.5f);
     222     1049920 :     const GByte t = static_cast<GByte>(v * (1.0f - s * (1.0f - f)) + 0.5f);
     223             : 
     224     1049920 :     if (r)
     225      525633 :         *r = choose_among(i, v, q, p, p, t, v);
     226     1049920 :     if (g)
     227      525632 :         *g = choose_among(i, t, v, v, q, p, p);
     228     1049920 :     if (b)
     229      525632 :         *b = choose_among(i, p, p, t, v, v, q);
     230     1049920 : }
     231             : 
     232             : /************************************************************************/
     233             : /*                           XMM_RGB_to_HS()                            */
     234             : /************************************************************************/
     235             : 
     236             : #ifdef HAVE_SSE2
     237             : static inline void
     238      524288 : 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      524288 :     const auto r = XMMReg4Float::Load4Val(pInR);
     246      524288 :     const auto g = XMMReg4Float::Load4Val(pInG);
     247      524288 :     const auto b = XMMReg4Float::Load4Val(pInB);
     248      524288 :     const auto minc = XMMReg4Float::Min(XMMReg4Float::Min(r, g), b);
     249      524288 :     const auto maxc = XMMReg4Float::Max(XMMReg4Float::Max(r, g), b);
     250      524288 :     const auto max_minus_min = maxc - minc;
     251      524288 :     s = max_minus_min / XMMReg4Float::Max(one, maxc);
     252             :     const auto inv_max_minus_min_times_6_0 =
     253      524288 :         XMMReg4Float::Ternary(XMMReg4Float::Equals(max_minus_min, zero), one,
     254      524288 :                               six * max_minus_min)
     255      524288 :             .inverse();
     256      524288 :     const auto tmp = (g - b) * inv_max_minus_min_times_6_0;
     257      524288 :     h = XMMReg4Float::Ternary(
     258      524288 :         XMMReg4Float::Equals(maxc, b),
     259      524288 :         four_over_six + (r - g) * inv_max_minus_min_times_6_0,
     260      524288 :         XMMReg4Float::Ternary(
     261      524288 :             XMMReg4Float::Equals(maxc, g),
     262      524288 :             two_over_six + (b - r) * inv_max_minus_min_times_6_0,
     263      524288 :             XMMReg4Float::Ternary(XMMReg4Float::Lesser(tmp, zero), tmp + one,
     264      524288 :                                   tmp)));
     265      524288 : }
     266             : #endif
     267             : 
     268             : /************************************************************************/
     269             : /*                         patch_value_line()                           */
     270             : /************************************************************************/
     271             : 
     272             : static
     273             : #ifdef __GNUC__
     274             :     __attribute__((__noinline__))
     275             : #endif
     276             :     void
     277         211 :     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         211 :     int i = 0;
     285             : #ifdef HAVE_SSE2
     286         211 :     const auto zero = XMMReg4Float::Zero();
     287         211 :     const auto one = XMMReg4Float::Set1(1.0f);
     288         211 :     const auto six = XMMReg4Float::Set1(6.0f);
     289         211 :     const auto two_over_six = XMMReg4Float::Set1(2.0f / 6.0f);
     290         211 :     const auto four_over_six = two_over_six + two_over_six;
     291             : 
     292         211 :     constexpr int ELTS = 8;
     293      262355 :     for (; i + (ELTS - 1) < nCount; i += ELTS)
     294             :     {
     295      262144 :         XMMReg4Float h0, s0;
     296      262144 :         XMM_RGB_to_HS(pInR + i, pInG + i, pInB + i, zero, one, six,
     297             :                       two_over_six, four_over_six, h0, s0);
     298      262144 :         XMMReg4Float h1, s1;
     299      262144 :         XMM_RGB_to_HS(pInR + i + ELTS / 2, pInG + i + ELTS / 2,
     300      262144 :                       pInB + i + ELTS / 2, zero, one, six, two_over_six,
     301             :                       four_over_six, h1, s1);
     302             : 
     303      262144 :         XMMReg4Float v0, v1;
     304      262144 :         XMMReg4Float::Load8Val(pInGray + i, v0, v1);
     305             : 
     306      262144 :         const auto half = XMMReg4Float::Set1(0.5f);
     307      262144 :         const auto six_h0 = six * h0;
     308      262144 :         const auto idx0 = six_h0.truncate_to_int();
     309      262144 :         const auto f0 = six_h0 - idx0.to_float();
     310      262144 :         const auto p0 = (v0 * (one - s0) + half).truncate_to_int();
     311      262144 :         const auto q0 = (v0 * (one - s0 * f0) + half).truncate_to_int();
     312      262144 :         const auto t0 = (v0 * (one - s0 * (one - f0)) + half).truncate_to_int();
     313             : 
     314      262144 :         const auto six_h1 = six * h1;
     315      262144 :         const auto idx1 = six_h1.truncate_to_int();
     316      262144 :         const auto f1 = six_h1 - idx1.to_float();
     317      262144 :         const auto p1 = (v1 * (one - s1) + half).truncate_to_int();
     318      262144 :         const auto q1 = (v1 * (one - s1 * f1) + half).truncate_to_int();
     319      262144 :         const auto t1 = (v1 * (one - s1 * (one - f1)) + half).truncate_to_int();
     320             : 
     321      262144 :         const auto idx = XMMReg8Byte::Pack(idx0, idx1);
     322             :         const auto v =
     323      262144 :             XMMReg8Byte::Pack(v0.truncate_to_int(), v1.truncate_to_int());
     324      262144 :         const auto p = XMMReg8Byte::Pack(p0, p1);
     325      262144 :         const auto q = XMMReg8Byte::Pack(q0, q1);
     326      262144 :         const auto t = XMMReg8Byte::Pack(t0, t1);
     327             : 
     328      262144 :         const auto equalsTo0 = XMMReg8Byte::Equals(idx, XMMReg8Byte::Zero());
     329      262144 :         const auto one8Byte = XMMReg8Byte::Set1(1);
     330      262144 :         const auto equalsTo1 = XMMReg8Byte::Equals(idx, one8Byte);
     331      262144 :         const auto two8Byte = one8Byte + one8Byte;
     332      262144 :         const auto equalsTo2 = XMMReg8Byte::Equals(idx, two8Byte);
     333      262144 :         const auto four8Byte = two8Byte + two8Byte;
     334      262144 :         const auto equalsTo4 = XMMReg8Byte::Equals(idx, four8Byte);
     335      262144 :         const auto equalsTo3 = XMMReg8Byte::Equals(idx, four8Byte - one8Byte);
     336             :         // clang-format off
     337      262144 :         if (pOutR)
     338             :         {
     339             :             const auto out_r =
     340             :                 XMMReg8Byte::Ternary(equalsTo0, v,
     341      131072 :                 XMMReg8Byte::Ternary(equalsTo1, q,
     342      131072 :                 XMMReg8Byte::Ternary(XMMReg8Byte::Or(equalsTo2, equalsTo3), p,
     343      262144 :                 XMMReg8Byte::Ternary(equalsTo4, t, v))));
     344      131072 :             out_r.Store8Val(pOutR + i);
     345             :         }
     346      262144 :         if (pOutG)
     347             :         {
     348             :             const auto out_g =
     349             :                 XMMReg8Byte::Ternary(equalsTo0, t,
     350      114688 :                 XMMReg8Byte::Ternary(XMMReg8Byte::Or(equalsTo1, equalsTo2), v,
     351      229376 :                 XMMReg8Byte::Ternary(equalsTo3, q, p)));
     352      114688 :             out_g.Store8Val(pOutG + i);
     353             :         }
     354      262144 :         if (pOutB)
     355             :         {
     356             :             const auto out_b =
     357      114688 :                 XMMReg8Byte::Ternary(XMMReg8Byte::Or(equalsTo0, equalsTo1), p,
     358      114688 :                 XMMReg8Byte::Ternary(equalsTo2, t,
     359      114688 :                 XMMReg8Byte::Ternary(XMMReg8Byte::Or(equalsTo3, equalsTo4),
     360      114688 :                                      v, q)));
     361      114688 :             out_b.Store8Val(pOutB + i);
     362             :         }
     363             :         // clang-format on
     364             :     }
     365             : #endif
     366             : 
     367        1556 :     for (; i < nCount; ++i)
     368             :     {
     369             :         float h, s;
     370        1345 :         rgb_to_hs(pInR[i], pInG[i], pInB[i], &h, &s);
     371        4033 :         hsv_to_rgb(h, s, pInGray[i], pOutR ? pOutR + i : nullptr,
     372        2688 :                    pOutG ? pOutG + i : nullptr, pOutB ? pOutB + i : nullptr);
     373             :     }
     374         211 : }
     375             : 
     376             : /************************************************************************/
     377             : /*                           HSVMergeBand                               */
     378             : /************************************************************************/
     379             : 
     380             : class HSVMergeBand final : public GDALRasterBand
     381             : {
     382             :   public:
     383          40 :     HSVMergeBand(HSVMergeDataset &oHSVMergeDataset, int nBandIn)
     384          40 :         : m_oHSVMergeDataset(oHSVMergeDataset)
     385             :     {
     386          40 :         nBand = nBandIn;
     387          40 :         nRasterXSize = oHSVMergeDataset.GetRasterXSize();
     388          40 :         nRasterYSize = oHSVMergeDataset.GetRasterYSize();
     389          40 :         oHSVMergeDataset.m_oColorDS.GetRasterBand(1)->GetBlockSize(
     390             :             &nBlockXSize, &nBlockYSize);
     391          40 :         eDataType = GDT_Byte;
     392          40 :     }
     393             : 
     394           1 :     GDALColorInterp GetColorInterpretation() override
     395             :     {
     396           1 :         return m_oHSVMergeDataset.m_oColorDS.GetRasterBand(nBand)
     397           1 :             ->GetColorInterpretation();
     398             :     }
     399             : 
     400          17 :     int GetOverviewCount() override
     401             :     {
     402          17 :         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           7 :     CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override
     415             :     {
     416           7 :         int nReqXSize = 0;
     417           7 :         int nReqYSize = 0;
     418           7 :         GetActualBlockSize(nBlockXOff, nBlockYOff, &nReqXSize, &nReqYSize);
     419          14 :         return RasterIO(GF_Read, nBlockXOff * nBlockXSize,
     420           7 :                         nBlockYOff * nBlockYSize, nReqXSize, nReqYSize, pData,
     421           7 :                         nReqXSize, nReqYSize, GDT_Byte, 1, nBlockXSize,
     422          14 :                         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          12 : HSVMergeDataset::HSVMergeDataset(GDALDataset &oColorDS,
     440          12 :                                  GDALDataset &oGrayScaleDS)
     441          12 :     : m_oColorDS(oColorDS), m_oGrayScaleDS(oGrayScaleDS)
     442             : {
     443          12 :     CPLAssert(oColorDS.GetRasterCount() == 3 || oColorDS.GetRasterCount() == 4);
     444          12 :     CPLAssert(oColorDS.GetRasterXSize() == oGrayScaleDS.GetRasterXSize());
     445          12 :     CPLAssert(oColorDS.GetRasterYSize() == oGrayScaleDS.GetRasterYSize());
     446          12 :     nRasterXSize = oColorDS.GetRasterXSize();
     447          12 :     nRasterYSize = oColorDS.GetRasterYSize();
     448          12 :     const int nOvrCount = oGrayScaleDS.GetRasterBand(1)->GetOverviewCount();
     449          12 :     bool bCanCreateOvr = true;
     450          52 :     for (int iBand = 1; iBand <= oColorDS.GetRasterCount(); ++iBand)
     451             :     {
     452          40 :         SetBand(iBand, std::make_unique<HSVMergeBand>(*this, iBand));
     453          40 :         bCanCreateOvr =
     454          80 :             bCanCreateOvr &&
     455          40 :             oColorDS.GetRasterBand(iBand)->GetOverviewCount() == nOvrCount;
     456          48 :         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          12 :     SetDescription(CPLSPrintf("Merge %s with %s", m_oColorDS.GetDescription(),
     474          12 :                               m_oGrayScaleDS.GetDescription()));
     475          12 :     SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
     476             : 
     477          12 :     if (bCanCreateOvr)
     478             :     {
     479          14 :         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          12 : }
     489             : 
     490             : /************************************************************************/
     491             : /*               HSVMergeDataset::AcquireSourcePixels()                 */
     492             : /************************************************************************/
     493             : 
     494         217 : bool HSVMergeDataset::AcquireSourcePixels(int nXOff, int nYOff, int nXSize,
     495             :                                           int nYSize, int nBufXSize,
     496             :                                           int nBufYSize,
     497             :                                           GDALRasterIOExtraArg *psExtraArg)
     498             : {
     499         217 :     if (nXOff == m_nCachedXOff && nYOff == m_nCachedYOff &&
     500          18 :         nXSize == m_nCachedXSize && nYSize == m_nCachedYSize &&
     501           8 :         nBufXSize == m_nCachedBufXSize && nBufYSize == m_nCachedBufYSize &&
     502           8 :         psExtraArg->eResampleAlg == m_sCachedExtraArg.eResampleAlg &&
     503           8 :         psExtraArg->bFloatingPointWindowValidity ==
     504           8 :             m_sCachedExtraArg.bFloatingPointWindowValidity &&
     505           8 :         (!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           8 :         return !m_abyBuffer.empty();
     512             :     }
     513             : 
     514         209 :     constexpr int N_COMPS_IN_BUFFER = 4;  //  RGB + Grayscale
     515             : 
     516         418 :     if (static_cast<size_t>(nBufXSize) >
     517         209 :         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         209 :     const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
     527             :     try
     528             :     {
     529         209 :         if (m_abyBuffer.size() < nPixelCount * N_COMPS_IN_BUFFER)
     530           9 :             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         205 :         (m_oColorDS.RasterIO(
     543         205 :              GF_Read, nXOff, nYOff, nXSize, nYSize, m_abyBuffer.data(),
     544             :              nBufXSize, nBufYSize, GDT_Byte, 3, nullptr, 1, nBufXSize,
     545         408 :              static_cast<GSpacing>(nPixelCount), psExtraArg) == CE_None &&
     546         406 :          m_oGrayScaleDS.GetRasterBand(1)->RasterIO(
     547             :              GF_Read, nXOff, nYOff, nXSize, nYSize,
     548         203 :              m_abyBuffer.data() + nPixelCount * 3, nBufXSize, nBufYSize,
     549         205 :              GDT_Byte, 1, nBufXSize, psExtraArg) == CE_None);
     550         205 :     if (bOK)
     551             :     {
     552         203 :         m_nCachedXOff = nXOff;
     553         203 :         m_nCachedYOff = nYOff;
     554         203 :         m_nCachedXSize = nXSize;
     555         203 :         m_nCachedYSize = nYSize;
     556         203 :         m_nCachedBufXSize = nBufXSize;
     557         203 :         m_nCachedBufYSize = nBufYSize;
     558         203 :         m_sCachedExtraArg = *psExtraArg;
     559             :     }
     560             :     else
     561             :     {
     562           2 :         m_abyBuffer.clear();
     563           2 :         m_ioError = true;
     564             :     }
     565         205 :     return bOK;
     566             : }
     567             : 
     568             : /************************************************************************/
     569             : /*                     HSVMergeDataset::IRasterIO()                     */
     570             : /************************************************************************/
     571             : 
     572         205 : 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         205 :     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         203 :     GByte *pabyDst = static_cast<GByte *>(pData);
     593         203 :     if (eRWFlag == GF_Read && eBufType == GDT_Byte && nBandCount == nBands &&
     594         202 :         panBandMap[0] == 1 && panBandMap[1] == 2 && panBandMap[2] == 3 &&
     595         196 :         (nBandCount == 3 || panBandMap[3] == 4) &&
     596         202 :         AcquireSourcePixels(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
     597         602 :                             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         196 :         const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
     605         196 :         const GByte *pabyR = m_abyBuffer.data();
     606         196 :         const GByte *pabyG = m_abyBuffer.data() + nPixelCount;
     607         196 :         const GByte *pabyB = m_abyBuffer.data() + nPixelCount * 2;
     608         196 :         const GByte *pabyGrayScale = m_abyBuffer.data() + nPixelCount * 3;
     609         196 :         size_t nSrcIdx = 0;
     610         394 :         for (int j = 0; j < nBufYSize; ++j)
     611             :         {
     612         198 :             auto nDstOffset = j * nLineSpace;
     613         198 :             if (nPixelSpace == 1 && nLineSpace >= nPixelSpace * nBufXSize &&
     614         196 :                 nBandSpace >= nLineSpace * nBufYSize)
     615             :             {
     616         196 :                 patch_value_line(nBufXSize, pabyR + nSrcIdx, pabyG + nSrcIdx,
     617             :                                  pabyB + nSrcIdx, pabyGrayScale + nSrcIdx,
     618         196 :                                  pabyDst + nDstOffset,
     619         196 :                                  pabyDst + nDstOffset + nBandSpace,
     620         196 :                                  pabyDst + nDstOffset + 2 * nBandSpace);
     621         196 :                 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         196 :         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          23 : 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          23 :     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          22 :     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          34 :     else if (eRWFlag == GF_Read && eBufType == GDT_Byte &&
     685          15 :              m_oHSVMergeDataset.AcquireSourcePixels(nXOff, nYOff, nXSize,
     686             :                                                     nYSize, nBufXSize,
     687             :                                                     nBufYSize, psExtraArg))
     688             :     {
     689          15 :         GByte *pabyDst = static_cast<GByte *>(pData);
     690          15 :         const GByte *pabyR = m_oHSVMergeDataset.m_abyBuffer.data();
     691          15 :         const size_t nPixelCount = static_cast<size_t>(nBufXSize) * nBufYSize;
     692             :         const GByte *pabyG =
     693          15 :             m_oHSVMergeDataset.m_abyBuffer.data() + nPixelCount;
     694             :         const GByte *pabyB =
     695          15 :             m_oHSVMergeDataset.m_abyBuffer.data() + nPixelCount * 2;
     696             :         const GByte *pabyGrayScale =
     697          15 :             m_oHSVMergeDataset.m_abyBuffer.data() + nPixelCount * 3;
     698          15 :         size_t nSrcIdx = 0;
     699          36 :         for (int j = 0; j < nBufYSize; ++j)
     700             :         {
     701          21 :             auto nDstOffset = j * nLineSpace;
     702          21 :             if (nPixelSpace == 1 && nLineSpace >= nPixelSpace * nBufXSize)
     703             :             {
     704          37 :                 patch_value_line(nBufXSize, pabyR + nSrcIdx, pabyG + nSrcIdx,
     705             :                                  pabyB + nSrcIdx, pabyGrayScale + nSrcIdx,
     706          15 :                                  nBand == 1 ? pabyDst + nDstOffset : nullptr,
     707          15 :                                  nBand == 2 ? pabyDst + nDstOffset : nullptr,
     708          15 :                                  nBand == 3 ? pabyDst + nDstOffset : nullptr);
     709          15 :                 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          15 :         return CE_None;
     740             :     }
     741           4 :     else if (m_oHSVMergeDataset.m_ioError)
     742             :     {
     743           0 :         return CE_Failure;
     744             :     }
     745             :     else
     746             :     {
     747           4 :         const CPLErr eErr = GDALRasterBand::IRasterIO(
     748             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
     749             :             eBufType, nPixelSpace, nLineSpace, psExtraArg);
     750           4 :         m_oHSVMergeDataset.m_ioError = eErr != CE_None;
     751           4 :         return eErr;
     752             :     }
     753             : }
     754             : 
     755             : }  // namespace
     756             : 
     757             : /************************************************************************/
     758             : /*                 GDALRasterColorMergeAlgorithm::RunStep()             */
     759             : /************************************************************************/
     760             : 
     761          17 : bool GDALRasterColorMergeAlgorithm::RunStep(GDALPipelineStepRunContext &)
     762             : {
     763          17 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     764          17 :     CPLAssert(poSrcDS);
     765             : 
     766          17 :     auto poGrayScaleDS = m_grayScaleDataset.GetDatasetRef();
     767          17 :     CPLAssert(poGrayScaleDS);
     768             : 
     769          32 :     if ((poSrcDS->GetRasterCount() != 3 && poSrcDS->GetRasterCount() != 4) ||
     770          15 :         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          27 :     if (poGrayScaleDS->GetRasterCount() != 1 ||
     778          13 :         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          23 :     if (poSrcDS->GetRasterXSize() != poGrayScaleDS->GetRasterXSize() ||
     786          11 :         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          10 :     m_outputDataset.Set(
     795          20 :         std::make_unique<HSVMergeDataset>(*poSrcDS, *poGrayScaleDS));
     796             : 
     797          10 :     return true;
     798             : }
     799             : 
     800             : GDALRasterColorMergeAlgorithmStandalone::
     801             :     ~GDALRasterColorMergeAlgorithmStandalone() = default;
     802             : 
     803             : //! @endcond

Generated by: LCOV version 1.14