LCOV - code coverage report
Current view: top level - apps - nearblack_lib_floodfill.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 211 243 86.8 %
Date: 2024-05-03 15:49:35 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Utilities
       4             :  * Purpose:  Convert nearly black or nearly white border to exact black/white
       5             :  *           using the flood fill algorithm.
       6             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       7             :  *
       8             :  * ****************************************************************************
       9             :  * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "gdal_priv.h"
      31             : #include "nearblack_lib.h"
      32             : 
      33             : #include <algorithm>
      34             : #include <memory>
      35             : #include <queue>
      36             : 
      37             : /************************************************************************/
      38             : /*                    GDALNearblackFloodFillAlg                         */
      39             : /************************************************************************/
      40             : 
      41             : // Implements the "final, combined-scan-and-fill span filler was then published
      42             : // in 1990" algorithm of https://en.wikipedia.org/wiki/Flood_fill#Span_filling
      43             : 
      44             : struct GDALNearblackFloodFillAlg
      45             : {
      46             :     // Input arguments of the algorithm
      47             :     const GDALNearblackOptions *m_psOptions = nullptr;
      48             :     GDALDataset *m_poSrcDataset = nullptr;
      49             :     GDALDataset *m_poDstDS = nullptr;
      50             :     GDALRasterBand *m_poMaskBand = nullptr;
      51             :     int m_nSrcBands = 0;
      52             :     int m_nDstBands = 0;
      53             :     bool m_bSetMask = false;
      54             :     Colors m_oColors{};
      55             :     GByte m_nReplacevalue = 0;
      56             : 
      57             :     // As we (generally) do not modify the value of pixels that are "black"
      58             :     // we need to keep track of the pixels we visited
      59             :     // Cf https://en.wikipedia.org/wiki/Flood_fill#Disadvantages_2
      60             :     // and https://en.wikipedia.org/wiki/Flood_fill#Adding_pattern_filling_support
      61             :     // for the requirement to add that extra sentinel
      62             :     std::unique_ptr<GDALDataset> m_poVisitedDS = nullptr;
      63             : 
      64             :     // Active line for the m_abyLine, m_abyLineMustSet, m_abyMask buffers
      65             :     int m_nLoadedLine = -1;
      66             : 
      67             :     // Whether Set(..., m_nLoadedLine) has been called
      68             :     bool m_bLineModified = true;
      69             : 
      70             :     // Content of m_poSrcDataset/m_poDstDS for m_nLoadedLine
      71             :     // Contains m_nDstBands * nXSize values in the order (R,G,B),(R,G,B),...
      72             :     std::vector<GByte> m_abyLine{};
      73             : 
      74             :     static constexpr GByte MUST_FILL_UNINIT = 0;  // must be 0
      75             :     static constexpr GByte MUST_FILL_FALSE = 1;
      76             :     static constexpr GByte MUST_FILL_TRUE = 2;
      77             :     // Content of m_poVisitedDS for m_nLoadedLine
      78             :     std::vector<GByte> m_abyLineMustSet{};
      79             : 
      80             :     // Only use if m_bSetMask
      81             :     std::vector<GByte> m_abyMask{};
      82             : 
      83             :     // Used for progress bar. Incremented the first time a line ifs loaded
      84             :     int m_nCountLoadedOnce = 0;
      85             : 
      86             :     // m_abLineLoadedOnce[line] is set to true after the first time the line
      87             :     // of m_poSrcDataset is loaded by LoadLine(line)
      88             :     std::vector<bool> m_abLineLoadedOnce{};
      89             : 
      90             :     // m_abLineSavedOnce[line] is set to true after the first time the line
      91             :     // of m_poDstDS is written by LoadLine()
      92             :     std::vector<bool> m_abLineSavedOnce{};
      93             : 
      94             : #ifdef DEBUG
      95             :     size_t m_nMaxQueueSize = 0;
      96             : #endif
      97             : 
      98             :     // Entry point
      99             :     bool Process();
     100             : 
     101             :   private:
     102             :     bool Fill(int iX, int iY);
     103             :     bool LoadLine(int iY);
     104             :     bool MustSet(int iX, int iY);
     105             :     void Set(int iX, int iY);
     106             : };
     107             : 
     108             : /************************************************************************/
     109             : /*              GDALNearblackFloodFillAlg::MustSet()                    */
     110             : /*                                                                      */
     111             : /* Called Inside() in https://en.wikipedia.org/wiki/Flood_fill          */
     112             : /************************************************************************/
     113             : 
     114             : // Returns true if the pixel (iX, iY) is "black" (or more generally transparent
     115             : // according to m_oColors)
     116        9281 : bool GDALNearblackFloodFillAlg::MustSet(int iX, int iY)
     117             : {
     118        9281 :     CPLAssert(iX >= 0);
     119        9281 :     CPLAssert(iX < m_poSrcDataset->GetRasterXSize());
     120             : 
     121        9281 :     CPLAssert(iY >= 0);
     122        9281 :     CPLAssert(iY < m_poSrcDataset->GetRasterYSize());
     123        9281 :     CPLAssert(iY == m_nLoadedLine);
     124        9281 :     CPL_IGNORE_RET_VAL(iY);
     125             : 
     126        9281 :     if (m_abyLineMustSet[iX] != MUST_FILL_UNINIT)
     127             :     {
     128        1300 :         return m_abyLineMustSet[iX] == MUST_FILL_TRUE;
     129             :     }
     130             : 
     131             :     /***** loop over the colors *****/
     132             : 
     133       10965 :     for (int iColor = 0; iColor < static_cast<int>(m_oColors.size()); iColor++)
     134             :     {
     135        9617 :         const Color &oColor = m_oColors[iColor];
     136             : 
     137             :         /***** loop over the bands *****/
     138        9617 :         bool bIsNonBlack = false;
     139             : 
     140       29267 :         for (int iBand = 0; iBand < m_nSrcBands; iBand++)
     141             :         {
     142       22634 :             const int nPix = m_abyLine[iX * m_nDstBands + iBand];
     143             : 
     144       45004 :             if (oColor[iBand] - nPix > m_psOptions->nNearDist ||
     145       22370 :                 nPix > m_psOptions->nNearDist + oColor[iBand])
     146             :             {
     147        2984 :                 bIsNonBlack = true;
     148        2984 :                 break;
     149             :             }
     150             :         }
     151             : 
     152        9617 :         if (!bIsNonBlack)
     153             :         {
     154        6633 :             m_abyLineMustSet[iX] = MUST_FILL_TRUE;
     155        6633 :             return true;
     156             :         }
     157             :     }
     158             : 
     159        1348 :     m_abyLineMustSet[iX] = MUST_FILL_FALSE;
     160        1348 :     return false;
     161             : }
     162             : 
     163             : /************************************************************************/
     164             : /*             GDALNearblackFloodFillAlg::LoadLine()                    */
     165             : /************************************************************************/
     166             : 
     167             : // Load the new line iY, and saves if needed buffer of the previous loaded
     168             : // line (m_nLoadedLine).
     169             : // Returns true if no error
     170        2222 : bool GDALNearblackFloodFillAlg::LoadLine(int iY)
     171             : {
     172        2222 :     if (iY != m_nLoadedLine)
     173             :     {
     174             : #ifdef DEBUG
     175             :         // CPLDebug("GDAL", "GDALNearblackFloodFillAlg::LoadLine(%d)", iY);
     176             : #endif
     177         953 :         const int nXSize = m_poSrcDataset->GetRasterXSize();
     178             : 
     179         953 :         if (m_nLoadedLine >= 0)
     180             :         {
     181        1396 :             if (m_bLineModified || (m_poDstDS != m_poSrcDataset &&
     182        1396 :                                     !m_abLineSavedOnce[m_nLoadedLine]))
     183             :             {
     184         423 :                 if (m_poDstDS->RasterIO(
     185         423 :                         GF_Write, 0, m_nLoadedLine, nXSize, 1, m_abyLine.data(),
     186         423 :                         nXSize, 1, GDT_Byte, m_nDstBands, nullptr, m_nDstBands,
     187         423 :                         static_cast<GSpacing>(nXSize) * m_nDstBands, 1,
     188         423 :                         nullptr) != CE_None)
     189             :                 {
     190           0 :                     return false;
     191             :                 }
     192             :             }
     193             : 
     194        1198 :             if (m_bSetMask &&
     195        1198 :                 (m_bLineModified || !m_abLineSavedOnce[m_nLoadedLine]))
     196             :             {
     197         118 :                 if (m_poMaskBand->RasterIO(GF_Write, 0, m_nLoadedLine, nXSize,
     198         118 :                                            1, m_abyMask.data(), nXSize, 1,
     199         118 :                                            GDT_Byte, 0, 0, nullptr) != CE_None)
     200             :                 {
     201           0 :                     return false;
     202             :                 }
     203             :             }
     204             : 
     205         929 :             m_abLineSavedOnce[m_nLoadedLine] = true;
     206             :         }
     207             : 
     208         953 :         if (iY >= 0)
     209             :         {
     210         929 :             if (m_poDstDS != m_poSrcDataset && m_abLineSavedOnce[iY])
     211             :             {
     212             :                 // If the output dataset is different from the source one,
     213             :                 // load from the output dataset if we have already written the
     214             :                 // line of interest
     215         497 :                 if (m_poDstDS->RasterIO(
     216         497 :                         GF_Read, 0, iY, nXSize, 1, m_abyLine.data(), nXSize, 1,
     217         497 :                         GDT_Byte, m_nDstBands, nullptr, m_nDstBands,
     218         497 :                         static_cast<GSpacing>(nXSize) * m_nDstBands, 1,
     219         497 :                         nullptr) != CE_None)
     220             :                 {
     221           0 :                     return false;
     222             :                 }
     223             :             }
     224             :             else
     225             :             {
     226             :                 // Otherwise load from the source data
     227         432 :                 if (m_poSrcDataset->RasterIO(
     228         432 :                         GF_Read, 0, iY, nXSize, 1, m_abyLine.data(), nXSize, 1,
     229             :                         GDT_Byte,
     230             :                         // m_nSrcBands intended
     231             :                         m_nSrcBands,
     232             :                         // m_nDstBands intended
     233         432 :                         nullptr, m_nDstBands,
     234         432 :                         static_cast<GSpacing>(nXSize) * m_nDstBands, 1,
     235         432 :                         nullptr) != CE_None)
     236             :                 {
     237           0 :                     return false;
     238             :                 }
     239             : 
     240             :                 // Initialize the alpha component to 255 if it is the first time
     241             :                 // we load that line.
     242         432 :                 if (m_psOptions->bSetAlpha && !m_abLineLoadedOnce[iY])
     243             :                 {
     244        7650 :                     for (int iCol = 0; iCol < nXSize; iCol++)
     245             :                     {
     246        7500 :                         m_abyLine[iCol * m_nDstBands + m_nDstBands - 1] = 255;
     247             :                     }
     248             :                 }
     249             :             }
     250             : 
     251         929 :             if (m_bSetMask)
     252             :             {
     253         269 :                 if (!m_abLineLoadedOnce[iY])
     254             :                 {
     255        2700 :                     for (int iCol = 0; iCol < nXSize; iCol++)
     256             :                     {
     257        2625 :                         m_abyMask[iCol] = 255;
     258             :                     }
     259             :                 }
     260             :                 else
     261             :                 {
     262         194 :                     if (m_poMaskBand->RasterIO(
     263         194 :                             GF_Read, 0, iY, nXSize, 1, m_abyMask.data(), nXSize,
     264         194 :                             1, GDT_Byte, 0, 0, nullptr) != CE_None)
     265             :                     {
     266           0 :                         return false;
     267             :                     }
     268             :                 }
     269             :             }
     270             : 
     271         929 :             if (!m_abLineLoadedOnce[iY])
     272             :             {
     273         375 :                 m_nCountLoadedOnce++;
     274             :                 // Very rough progression report based on the first time
     275             :                 // we load a line...
     276             :                 // We arbitrarily consider that it's 90% of the processing time
     277         375 :                 const int nYSize = m_poSrcDataset->GetRasterYSize();
     278         375 :                 if (!(m_psOptions->pfnProgress(
     279             :                         0.9 *
     280         375 :                             (m_nCountLoadedOnce / static_cast<double>(nYSize)),
     281         375 :                         nullptr, m_psOptions->pProgressData)))
     282             :                 {
     283           0 :                     return false;
     284             :                 }
     285         375 :                 m_abLineLoadedOnce[iY] = true;
     286             :             }
     287             :         }
     288             : 
     289         953 :         if (m_nLoadedLine >= 0)
     290             :         {
     291        1858 :             if (m_poVisitedDS->GetRasterBand(1)->RasterIO(
     292             :                     GF_Write, 0, m_nLoadedLine, nXSize, 1,
     293         929 :                     m_abyLineMustSet.data(), nXSize, 1, GDT_Byte, 0, 0,
     294         929 :                     nullptr) != CE_None)
     295             :             {
     296           0 :                 return false;
     297             :             }
     298             :         }
     299             : 
     300         953 :         if (iY >= 0)
     301             :         {
     302        1858 :             if (m_poVisitedDS->GetRasterBand(1)->RasterIO(
     303         929 :                     GF_Read, 0, iY, nXSize, 1, m_abyLineMustSet.data(), nXSize,
     304         929 :                     1, GDT_Byte, 0, 0, nullptr) != CE_None)
     305             :             {
     306           0 :                 return false;
     307             :             }
     308             :         }
     309             : 
     310         953 :         m_bLineModified = false;
     311         953 :         m_nLoadedLine = iY;
     312             :     }
     313        2222 :     return true;
     314             : }
     315             : 
     316             : /************************************************************************/
     317             : /*              GDALNearblackFloodFillAlg::Set()                        */
     318             : /************************************************************************/
     319             : 
     320             : // Mark the pixel as transparent
     321        6633 : void GDALNearblackFloodFillAlg::Set(int iX, int iY)
     322             : {
     323        6633 :     CPLAssert(iY == m_nLoadedLine);
     324        6633 :     CPL_IGNORE_RET_VAL(iY);
     325             : 
     326        6633 :     m_bLineModified = true;
     327        6633 :     m_abyLineMustSet[iX] = MUST_FILL_FALSE;
     328             : 
     329       26182 :     for (int iBand = 0; iBand < m_nSrcBands; iBand++)
     330       19549 :         m_abyLine[iX * m_nDstBands + iBand] = m_nReplacevalue;
     331             : 
     332             :     /***** alpha *****/
     333        6633 :     if (m_nDstBands > m_nSrcBands)
     334        1946 :         m_abyLine[iX * m_nDstBands + m_nDstBands - 1] = 0;
     335             : 
     336        6633 :     if (m_bSetMask)
     337         879 :         m_abyMask[iX] = 0;
     338        6633 : }
     339             : 
     340             : /************************************************************************/
     341             : /*              GDALNearblackFloodFillAlg::Fill()                       */
     342             : /************************************************************************/
     343             : 
     344             : /* Implements the "final, combined-scan-and-fill span filler was then published
     345             :  * in 1990" algorithm of https://en.wikipedia.org/wiki/Flood_fill#Span_filling
     346             :  * with the following enhancements:
     347             :  * - extra bound checking to avoid calling MustSet() outside the raster
     348             :  * - extra bound checking to avoid pushing spans outside the raster
     349             :  *
     350             :  * Returns true if no error.
     351             :  */
     352             : 
     353        1652 : bool GDALNearblackFloodFillAlg::Fill(int iXInit, int iYInit)
     354             : {
     355        1652 :     const int nXSize = m_poSrcDataset->GetRasterXSize();
     356        1652 :     const int nYSize = m_poSrcDataset->GetRasterYSize();
     357             : 
     358             :     struct Span
     359             :     {
     360             :         int x1;
     361             :         int x2;
     362             :         int y;
     363             :         int dy;
     364             : 
     365         546 :         Span(int x1In, int x2In, int yIn, int dyIn)
     366         546 :             : x1(x1In), x2(x2In), y(yIn), dy(dyIn)
     367             :         {
     368         546 :         }
     369             :     };
     370             : 
     371        1652 :     if (!LoadLine(iYInit))
     372           0 :         return false;
     373             : 
     374        1652 :     if (!MustSet(iXInit, iYInit))
     375             :     {
     376             :         // nothing to do
     377        1632 :         return true;
     378             :     }
     379             : 
     380          40 :     std::queue<Span> queue;
     381          20 :     queue.emplace(Span(iXInit, iXInit, iYInit, 1));
     382          20 :     if (iYInit > 0)
     383             :     {
     384           7 :         queue.emplace(Span(iXInit, iXInit, iYInit - 1, -1));
     385             :     }
     386             : 
     387         566 :     while (!queue.empty())
     388             :     {
     389             : #ifdef DEBUG
     390         546 :         m_nMaxQueueSize = std::max(m_nMaxQueueSize, queue.size());
     391             : #endif
     392             : 
     393         546 :         const Span s = queue.front();
     394         546 :         queue.pop();
     395             : 
     396         546 :         CPLAssert(s.x1 >= 0);
     397         546 :         CPLAssert(s.x1 < nXSize);
     398         546 :         CPLAssert(s.x2 >= 0);
     399         546 :         CPLAssert(s.x2 < nXSize);
     400         546 :         CPLAssert(s.x2 >= s.x1);
     401         546 :         CPLAssert(s.y >= 0);
     402         546 :         CPLAssert(s.y < nYSize);
     403             : 
     404         546 :         int iX = s.x1;
     405         546 :         const int iY = s.y;
     406             : 
     407         546 :         if (!LoadLine(iY))
     408           0 :             return false;
     409             : 
     410         546 :         if (iX > 0 && MustSet(iX, iY))
     411             :         {
     412          56 :             while (MustSet(iX - 1, iY))
     413             :             {
     414          11 :                 Set(iX - 1, iY);
     415          11 :                 iX--;
     416          11 :                 if (iX == 0)
     417           0 :                     break;
     418             :             }
     419             :         }
     420         546 :         if (iX >= 0 && iX <= s.x1 - 1 && iY - s.dy >= 0 && iY - s.dy < nYSize)
     421             :         {
     422           8 :             queue.emplace(Span(iX, s.x1 - 1, iY - s.dy, -s.dy));
     423             :         }
     424         546 :         int iX1 = s.x1;
     425         546 :         const int iX2 = s.x2;
     426        1209 :         while (iX1 <= iX2)
     427             :         {
     428        7212 :             while (MustSet(iX1, iY))
     429             :             {
     430        6622 :                 Set(iX1, iY);
     431        6622 :                 iX1++;
     432        6622 :                 if (iX1 == nXSize)
     433          73 :                     break;
     434             :             }
     435         663 :             if (iX <= iX1 - 1 && iY + s.dy >= 0 && iY + s.dy < nYSize)
     436             :             {
     437         441 :                 queue.emplace(Span(iX, iX1 - 1, iY + s.dy, s.dy));
     438             :             }
     439         663 :             if (iX1 - 1 > iX2 && iY - s.dy >= 0 && iY - s.dy < nYSize)
     440             :             {
     441          70 :                 queue.emplace(Span(iX2 + 1, iX1 - 1, iY - s.dy, -s.dy));
     442             :             }
     443         663 :             iX1++;
     444         828 :             while (iX1 < iX2 && !MustSet(iX1, iY))
     445         165 :                 iX1++;
     446         663 :             iX = iX1;
     447             :         }
     448             :     }
     449             : 
     450          20 :     return true;
     451             : }
     452             : 
     453             : /************************************************************************/
     454             : /*              GDALNearblackFloodFillAlg::Process()                    */
     455             : /************************************************************************/
     456             : 
     457             : // Entry point.
     458             : // Returns true if no error.
     459             : 
     460          24 : bool GDALNearblackFloodFillAlg::Process()
     461             : {
     462          24 :     const int nXSize = m_poSrcDataset->GetRasterXSize();
     463          24 :     const int nYSize = m_poSrcDataset->GetRasterYSize();
     464             : 
     465             :     /* -------------------------------------------------------------------- */
     466             :     /*      Allocate working buffers.                                       */
     467             :     /* -------------------------------------------------------------------- */
     468             :     try
     469             :     {
     470          24 :         m_abyLine.resize(static_cast<size_t>(nXSize) * m_nDstBands);
     471          24 :         m_abyLineMustSet.resize(nXSize);
     472          24 :         if (m_bSetMask)
     473          18 :             m_abyMask.resize(nXSize);
     474             : 
     475          24 :         if (m_psOptions->nMaxNonBlack > 0)
     476             :         {
     477          12 :             m_abLineLoadedOnce.resize(nYSize, true);
     478          12 :             m_abLineSavedOnce.resize(nYSize, true);
     479             :         }
     480             :         else
     481             :         {
     482          12 :             m_abLineLoadedOnce.resize(nYSize);
     483          12 :             m_abLineSavedOnce.resize(nYSize);
     484             :         }
     485             :     }
     486           0 :     catch (const std::exception &e)
     487             :     {
     488           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     489           0 :                  "Cannot allocate working buffers: %s", e.what());
     490           0 :         return false;
     491             :     }
     492             : 
     493             :     /* -------------------------------------------------------------------- */
     494             :     /*      Create a temporary dataset to save visited state                */
     495             :     /* -------------------------------------------------------------------- */
     496             : 
     497             :     // For debugging / testing purposes only
     498             :     const char *pszTmpDriver =
     499          24 :         CPLGetConfigOption("GDAL_TEMP_DRIVER_NAME", nullptr);
     500          24 :     if (!pszTmpDriver)
     501             :     {
     502          24 :         pszTmpDriver =
     503          24 :             (nXSize < 100 * 1024 * 1024 / nYSize ||
     504           0 :              (m_poDstDS->GetDriver() &&
     505           0 :               strcmp(m_poDstDS->GetDriver()->GetDescription(), "MEM") == 0))
     506          24 :                 ? "MEM"
     507             :                 : "GTiff";
     508             :     }
     509          24 :     GDALDriverH hDriver = GDALGetDriverByName(pszTmpDriver);
     510          24 :     if (!hDriver)
     511             :     {
     512           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     513             :                  "Cannot find driver %s for temporary file", pszTmpDriver);
     514           0 :         return false;
     515             :     }
     516          48 :     std::string osVisitedDataset = m_poDstDS->GetDescription();
     517             :     VSIStatBuf sStat;
     518          48 :     if (strcmp(pszTmpDriver, "MEM") == 0 ||
     519          24 :         STARTS_WITH(osVisitedDataset.c_str(), "/vsimem/") ||
     520             :         // Regular VSIStat() (not VSIStatL()) intended to check this is
     521             :         // a real file
     522           0 :         VSIStat(osVisitedDataset.c_str(), &sStat) == 0)
     523             :     {
     524          24 :         osVisitedDataset += ".visited";
     525             :     }
     526             :     else
     527             :     {
     528           0 :         osVisitedDataset = CPLGenerateTempFilename(osVisitedDataset.c_str());
     529             :     }
     530          48 :     CPLStringList aosOptions;
     531          24 :     if (strcmp(pszTmpDriver, "GTiff") == 0)
     532             :     {
     533           0 :         aosOptions.SetNameValue("SPARSE_OK", "YES");
     534           0 :         aosOptions.SetNameValue("COMPRESS", "LZW");
     535           0 :         osVisitedDataset += ".tif";
     536             :     }
     537          24 :     m_poVisitedDS.reset(GDALDataset::FromHandle(
     538             :         GDALCreate(hDriver, osVisitedDataset.c_str(), nXSize, nYSize, 1,
     539          24 :                    GDT_Byte, aosOptions.List())));
     540          24 :     if (!m_poVisitedDS)
     541           0 :         return false;
     542          24 :     if (strcmp(pszTmpDriver, "MEM") != 0)
     543             :     {
     544           0 :         VSIUnlink(osVisitedDataset.c_str());
     545             :     }
     546          24 :     m_poVisitedDS->MarkSuppressOnClose();
     547             : 
     548             :     /* -------------------------------------------------------------------- */
     549             :     /*      Iterate over the border of the raster                           */
     550             :     /* -------------------------------------------------------------------- */
     551             :     // Fill from top line
     552         461 :     for (int iX = 0; iX < nXSize; iX++)
     553             :     {
     554         437 :         if (!Fill(iX, 0))
     555           0 :             return false;
     556             :     }
     557             : 
     558             :     // Fill from left and right side
     559         413 :     for (int iY = 1; iY < nYSize - 1; iY++)
     560             :     {
     561         389 :         if (!Fill(0, iY))
     562           0 :             return false;
     563         389 :         if (!Fill(nXSize - 1, iY))
     564           0 :             return false;
     565             :     }
     566             : 
     567             :     // Fill from bottom line
     568         461 :     for (int iX = 0; iX < nXSize; iX++)
     569             :     {
     570         437 :         if (!Fill(iX, nYSize - 1))
     571           0 :             return false;
     572             :     }
     573             : 
     574          24 :     if (!(m_psOptions->pfnProgress(1.0, nullptr, m_psOptions->pProgressData)))
     575             :     {
     576           0 :         return false;
     577             :     }
     578             : 
     579             : #ifdef DEBUG
     580          24 :     CPLDebug("GDAL", "flood fill max queue size = %u",
     581          24 :              unsigned(m_nMaxQueueSize));
     582             : #endif
     583             : 
     584             :     // Force update of last visited line
     585          24 :     return LoadLine(-1);
     586             : }
     587             : 
     588             : /************************************************************************/
     589             : /*                    GDALNearblackFloodFill()                          */
     590             : /************************************************************************/
     591             : 
     592             : // Entry point.
     593             : // Returns true if no error.
     594             : 
     595          24 : bool GDALNearblackFloodFill(const GDALNearblackOptions *psOptions,
     596             :                             GDALDatasetH hSrcDataset, GDALDatasetH hDstDS,
     597             :                             GDALRasterBandH hMaskBand, int nSrcBands,
     598             :                             int nDstBands, bool bSetMask, const Colors &oColors)
     599             : {
     600          48 :     GDALNearblackFloodFillAlg alg;
     601          24 :     alg.m_psOptions = psOptions;
     602          24 :     alg.m_poSrcDataset = GDALDataset::FromHandle(hSrcDataset);
     603          24 :     alg.m_poDstDS = GDALDataset::FromHandle(hDstDS);
     604          24 :     alg.m_poMaskBand = GDALRasterBand::FromHandle(hMaskBand);
     605          24 :     alg.m_nSrcBands = nSrcBands;
     606          24 :     alg.m_nDstBands = nDstBands;
     607          24 :     alg.m_bSetMask = bSetMask;
     608          24 :     alg.m_oColors = oColors;
     609          24 :     alg.m_nReplacevalue = psOptions->bNearWhite ? 255 : 0;
     610             : 
     611          24 :     if (psOptions->nMaxNonBlack > 0)
     612             :     {
     613             :         // First pass: use the TwoPasses algorithm to deal with nMaxNonBlack
     614          24 :         GDALNearblackOptions sOptionsTmp(*psOptions);
     615          24 :         sOptionsTmp.pProgressData = GDALCreateScaledProgress(
     616          12 :             0, 0.5, psOptions->pfnProgress, psOptions->pProgressData);
     617          12 :         sOptionsTmp.pfnProgress = GDALScaledProgress;
     618          12 :         bool bRet = GDALNearblackTwoPassesAlgorithm(
     619             :             &sOptionsTmp, hSrcDataset, hDstDS, hMaskBand, nSrcBands, nDstBands,
     620             :             bSetMask, oColors);
     621          12 :         GDALDestroyScaledProgress(sOptionsTmp.pProgressData);
     622          12 :         if (!bRet)
     623           0 :             return false;
     624             : 
     625             :         // Second pass: use flood fill
     626          24 :         sOptionsTmp.pProgressData = GDALCreateScaledProgress(
     627          12 :             0.5, 1, psOptions->pfnProgress, psOptions->pProgressData);
     628          12 :         sOptionsTmp.pfnProgress = GDALScaledProgress;
     629          12 :         alg.m_psOptions = &sOptionsTmp;
     630          12 :         bRet = alg.Process();
     631          12 :         GDALDestroyScaledProgress(sOptionsTmp.pProgressData);
     632          12 :         return bRet;
     633             :     }
     634             :     else
     635             :     {
     636          12 :         return alg.Process();
     637             :     }
     638             : }

Generated by: LCOV version 1.14