LCOV - code coverage report
Current view: top level - frmts/mrf - LERC_band.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 315 425 74.1 %
Date: 2026-05-29 23:25:07 Functions: 30 43 69.8 %

          Line data    Source code
       1             : /*
       2             : Copyright 2013-2021 Esri
       3             : Licensed under the Apache License, Version 2.0 (the "License");
       4             : you may not use this file except in compliance with the License.
       5             : You may obtain a copy of the License at
       6             : http://www.apache.org/licenses/LICENSE-2.0
       7             : Unless required by applicable law or agreed to in writing, software
       8             : distributed under the License is distributed on an "AS IS" BASIS,
       9             : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      10             : See the License for the specific language governing permissions and
      11             : limitations under the License.
      12             : A local copy of the license and additional notices are located with the
      13             : source distribution at:
      14             : http://github.com/Esri/lerc/
      15             : 
      16             : LERC band implementation
      17             : LERC page compression and decompression functions
      18             : 
      19             : Authors:  Lucian Plesea
      20             : */
      21             : 
      22             : #include "marfa.h"
      23             : #include <algorithm>
      24             : #include <vector>
      25             : #include "LERCV1/Lerc1Image.h"
      26             : 
      27             : #include "gdal_priv_templates.hpp"
      28             : #include "gdal_openinfo.h"
      29             : 
      30             : // Requires lerc at least 2v4, where the c_api changed, but there is no good way
      31             : // to check
      32             : #include <Lerc_c_api.h>
      33             : #include <Lerc_types.h>
      34             : 
      35             : #ifndef LERC_AT_LEAST_VERSION
      36             : #define LERC_AT_LEAST_VERSION(maj, min, patch) 0
      37             : #endif
      38             : 
      39             : // name of internal or external libLerc namespace
      40             : #if defined(USING_NAMESPACE_LERC)
      41             : #define L2NS GDAL_LercNS
      42             : #else
      43             : // External lerc
      44             : #define L2NS LercNS
      45             : #endif
      46             : 
      47             : #define INFOIDX(T) static_cast<size_t>(L2NS::InfoArrOrder::T)
      48             : 
      49             : USING_NAMESPACE_LERC1
      50             : NAMESPACE_MRF_START
      51             : 
      52             : // Read an unaligned 4 byte little endian int from location p, advances pointer
      53         160 : static void READ_GINT32(int &X, const char *&p)
      54             : {
      55         160 :     memcpy(&X, p, sizeof(GInt32));
      56         160 :     p += sizeof(GInt32);
      57         160 : }
      58             : 
      59          16 : static void READ_FLOAT(float &X, const char *&p)
      60             : {
      61          16 :     memcpy(&X, p, sizeof(float));
      62          16 :     p += sizeof(float);
      63          16 : }
      64             : 
      65             : //
      66             : // Check that a buffer contains a supported Lerc1 blob, the type supported by
      67             : // MRF Can't really check everything without decoding, this just checks the main
      68             : // structure returns actual size if it is Lerc1 with size < sz returns 0 if
      69             : // format doesn't match returns -1 if Lerc1 but size can't be determined
      70             : //
      71             : // returns -<actual size> if actual size > sz
      72             : 
      73          16 : static int checkV1(const char *s, size_t sz)
      74             : {
      75             :     GInt32 nBytesMask, nBytesData;
      76             : 
      77             :     // Header is 34 bytes
      78             :     // band header is 16, first mask band then data band
      79          16 :     if (sz < static_cast<size_t>(
      80          16 :                  Lerc1Image::computeNumBytesNeededToWriteVoidImage()))
      81           0 :         return 0;
      82             :     // First ten bytes are ASCII signature
      83          16 :     if (!STARTS_WITH(s, "CntZImage "))
      84           0 :         return 0;
      85          16 :     s += 10;
      86             : 
      87             :     // Version 11
      88             :     int i;
      89          16 :     READ_GINT32(i, s);
      90          16 :     if (i != 11)
      91           0 :         return 0;
      92             : 
      93             :     // Type 8 is CntZ
      94          16 :     READ_GINT32(i, s);
      95          16 :     if (i != 8)
      96           0 :         return 0;
      97             : 
      98             :     // Height
      99          16 :     READ_GINT32(i, s);  // Arbitrary number in Lerc1Image::read()
     100          16 :     if (i > 20000 || i <= 0)
     101           0 :         return 0;
     102             : 
     103             :     // Width
     104          16 :     READ_GINT32(i, s);
     105          16 :     if (i > 20000 || i <= 0)
     106           0 :         return 0;
     107             : 
     108             :     // Skip the max val stored as double
     109          16 :     s += sizeof(double);
     110             : 
     111             :     // First header should be the mask, which mean 0 blocks
     112             :     // Height
     113          16 :     READ_GINT32(i, s);
     114          16 :     if (i != 0)
     115           0 :         return 0;
     116             : 
     117             :     // WIDTH
     118          16 :     READ_GINT32(i, s);
     119          16 :     if (i != 0)
     120           0 :         return 0;
     121             : 
     122          16 :     READ_GINT32(nBytesMask, s);
     123          16 :     if (nBytesMask < 0)
     124           0 :         return 0;
     125             : 
     126             :     // mask max value, 0 or 1 as float
     127             :     float val;
     128          16 :     READ_FLOAT(val, s);
     129          16 :     if (val != 0.0f && val != 1.0f)
     130           0 :         return 0;
     131             : 
     132             :     // If data header can't be read the actual size is unknown
     133          16 :     if (nBytesMask > INT_MAX - 66 || static_cast<size_t>(66 + nBytesMask) >= sz)
     134             :     {
     135           0 :         return -1;
     136             :     }
     137             : 
     138          16 :     s += nBytesMask;
     139             : 
     140             :     // Data Band header
     141          16 :     READ_GINT32(i,
     142             :                 s);  // number of full height blocks, never single pixel blocks
     143          16 :     if (i <= 0 || i > 10000)
     144           0 :         return 0;
     145             : 
     146          16 :     READ_GINT32(i,
     147             :                 s);  // number of full width blocks, never single pixel blocks
     148          16 :     if (i <= 0 || i > 10000)
     149           0 :         return 0;
     150             : 
     151          16 :     READ_GINT32(nBytesData, s);
     152          16 :     if (nBytesData < 0)
     153           0 :         return 0;
     154             : 
     155             :     // Actual LERC blob size
     156          16 :     if (66 + nBytesMask > INT_MAX - nBytesData)
     157           0 :         return -1;
     158          16 :     int size = static_cast<int>(66 + nBytesMask + nBytesData);
     159          16 :     return (static_cast<size_t>(size) > sz) ? -size : size;
     160             : }
     161             : 
     162             : // Load a buffer of type T into a LERC1 zImg, with a given stride
     163             : template <typename T>
     164          12 : static void Lerc1ImgFill(Lerc1Image &zImg, T *src, const ILImage &img,
     165             :                          GInt32 stride)
     166             : {
     167          12 :     int w = img.pagesize.x;
     168          12 :     int h = img.pagesize.y;
     169          12 :     zImg.resize(w, h);
     170          12 :     const float ndv = static_cast<float>(img.hasNoData ? img.NoDataValue : 0);
     171          12 :     if (stride == 1)
     172             :     {
     173        4617 :         for (int row = 0; row < h; row++)
     174     2363904 :             for (int col = 0; col < w; col++)
     175             :             {
     176     2359296 :                 float val = static_cast<float>(*src++);
     177     2359296 :                 zImg(row, col) = val;
     178     2359296 :                 zImg.SetMask(row, col, !CPLIsEqual(ndv, val));
     179             :             }
     180           9 :         return;
     181             :     }
     182        1539 :     for (int row = 0; row < h; row++)
     183      787968 :         for (int col = 0; col < w; col++)
     184             :         {
     185      786432 :             float val = static_cast<float>(*src);
     186      786432 :             src += stride;
     187      786432 :             zImg(row, col) = val;
     188      786432 :             zImg.SetMask(row, col, !CPLIsEqual(ndv, val));
     189             :         }
     190             : }
     191             : 
     192             : // Unload LERC1 zImg into a type T buffer
     193             : template <typename T>
     194          16 : static bool Lerc1ImgUFill(Lerc1Image &zImg, T *dst, const ILImage &img,
     195             :                           GInt32 stride)
     196             : {
     197          14 :     const T ndv =
     198          14 :         static_cast<T>(img.hasNoData && GDALIsValueInRange<T>(img.NoDataValue)
     199          10 :                            ? img.NoDataValue
     200             :                            : 0);
     201          16 :     if (img.pagesize.y != zImg.getHeight() || img.pagesize.x != zImg.getWidth())
     202           0 :         return false;
     203          16 :     int w = img.pagesize.x;
     204          16 :     int h = img.pagesize.y;
     205          16 :     if (1 == stride)
     206             :     {
     207        6672 :         for (int row = 0; row < h; row++)
     208             :         {
     209     3417610 :             for (int col = 0; col < w; col++)
     210             :             {
     211     3410950 :                 if (zImg.IsValid(row, col))
     212             :                 {
     213       27592 :                     GDALCopyWord(zImg(row, col), *dst);
     214             :                 }
     215             :                 else
     216             :                 {
     217     3383350 :                     *dst = ndv;
     218             :                 }
     219     3410950 :                 ++dst;
     220             :             }
     221             :         }
     222          13 :         return true;
     223             :     }
     224        1539 :     for (int row = 0; row < h; row++)
     225             :     {
     226      787968 :         for (int col = 0; col < w; col++)
     227             :         {
     228      786432 :             if (zImg.IsValid(row, col))
     229             :             {
     230      239713 :                 GDALCopyWord(zImg(row, col), *dst);
     231             :             }
     232             :             else
     233             :             {
     234      546719 :                 *dst = ndv;
     235             :             }
     236      786432 :             dst += stride;
     237             :         }
     238             :     }
     239           3 :     return true;
     240             : }
     241             : 
     242          10 : static CPLErr CompressLERC1(buf_mgr &dst, buf_mgr &src, const ILImage &img,
     243             :                             double precision)
     244             : {
     245          20 :     Lerc1Image zImg;
     246          10 :     GInt32 stride = img.pagesize.c;
     247          10 :     Lerc1NS::Byte *ptr = reinterpret_cast<Lerc1NS::Byte *>(dst.buffer);
     248             : 
     249          22 :     for (int c = 0; c < stride; c++)
     250             :     {
     251             : #define FILL(T)                                                                \
     252             :     Lerc1ImgFill(zImg, reinterpret_cast<T *>(src.buffer) + c, img, stride)
     253          12 :         switch (img.dt)
     254             :         {
     255           5 :             case GDT_UInt8:
     256           5 :                 FILL(GByte);
     257           5 :                 break;
     258           1 :             case GDT_UInt16:
     259           1 :                 FILL(GUInt16);
     260           1 :                 break;
     261           1 :             case GDT_Int16:
     262           1 :                 FILL(GInt16);
     263           1 :                 break;
     264           1 :             case GDT_Int32:
     265           1 :                 FILL(GInt32);
     266           1 :                 break;
     267           1 :             case GDT_UInt32:
     268           1 :                 FILL(GUInt32);
     269           1 :                 break;
     270           2 :             case GDT_Float32:
     271           2 :                 FILL(float);
     272           2 :                 break;
     273           1 :             case GDT_Float64:
     274           1 :                 FILL(double);
     275           1 :                 break;
     276           0 :             default:
     277           0 :                 break;
     278             :         }
     279             : #undef FILL
     280          12 :         if (!zImg.write(&ptr, precision))
     281             :         {
     282           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     283             :                      "MRF: Error during LERC compression");
     284           0 :             return CE_Failure;
     285             :         }
     286             :     }
     287             : 
     288             :     // write changes the value of the pointer, we can find the size by testing
     289             :     // how far it moved Add a couple of bytes, to avoid buffer overflow on
     290             :     // reading
     291          10 :     dst.size = reinterpret_cast<char *>(ptr) - dst.buffer + PADDING_BYTES;
     292          10 :     CPLDebug("MRF_LERC", "LERC Compressed to %d\n", (int)dst.size);
     293          10 :     return CE_None;
     294             : }
     295             : 
     296             : // LERC 1 Decompression
     297          14 : static CPLErr DecompressLERC1(buf_mgr &dst, const buf_mgr &src,
     298             :                               const ILImage &img)
     299             : {
     300          28 :     Lerc1Image zImg;
     301             : 
     302             :     // need to add the padding bytes so that out-of-buffer-access
     303          14 :     size_t nRemainingBytes = src.size + PADDING_BYTES;
     304          14 :     Lerc1NS::Byte *ptr = reinterpret_cast<Lerc1NS::Byte *>(src.buffer);
     305          14 :     GInt32 stride = img.pagesize.c;
     306          30 :     for (int c = 0; c < stride; c++)
     307             :     {
     308             :         // Check that input passes snicker test
     309          16 :         if (checkV1(reinterpret_cast<char *>(ptr), nRemainingBytes) <= 0)
     310             :         {
     311           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     312             :                      "MRF: LERC1 tile format error");
     313           0 :             return CE_Failure;
     314             :         }
     315             : 
     316          16 :         if (!zImg.read(&ptr, nRemainingBytes, 1e12))
     317             :         {
     318           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     319             :                      "MRF: Error during LERC decompression");
     320           0 :             return CE_Failure;
     321             :         }
     322             : 
     323             :         // Unpack from zImg to dst buffer, calling the right type
     324          16 :         bool success = false;
     325             : #define UFILL(T)                                                               \
     326             :     success = Lerc1ImgUFill(zImg, reinterpret_cast<T *>(dst.buffer) + c, img,  \
     327             :                             stride)
     328          16 :         switch (img.dt)
     329             :         {
     330           7 :             case GDT_UInt8:
     331           7 :                 UFILL(GByte);
     332           7 :                 break;
     333           0 :             case GDT_Int8:
     334           0 :                 UFILL(GInt8);
     335           0 :                 break;
     336           1 :             case GDT_UInt16:
     337           1 :                 UFILL(GUInt16);
     338           1 :                 break;
     339           1 :             case GDT_Int16:
     340           1 :                 UFILL(GInt16);
     341           1 :                 break;
     342           1 :             case GDT_Int32:
     343           1 :                 UFILL(GInt32);
     344           1 :                 break;
     345           1 :             case GDT_UInt32:
     346           1 :                 UFILL(GUInt32);
     347           1 :                 break;
     348           4 :             case GDT_Float32:
     349           4 :                 UFILL(float);
     350           4 :                 break;
     351           1 :             case GDT_Float64:
     352           1 :                 UFILL(double);
     353           1 :                 break;
     354           0 :             default:
     355           0 :                 break;
     356             :         }
     357             : #undef UFILL
     358          16 :         if (!success)
     359             :         {
     360           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     361             :                      "MRF: Error during LERC decompression");
     362           0 :             return CE_Failure;
     363             :         }
     364             :     }
     365             : 
     366          14 :     return CE_None;
     367             : }
     368             : 
     369             : // Lerc2
     370             : 
     371           2 : static GDALDataType L2toGDT(L2NS::DataType L2type)
     372             : {
     373             :     GDALDataType dt;
     374           2 :     switch (L2type)
     375             :     {
     376           0 :         case L2NS::DataType::dt_short:
     377           0 :             dt = GDT_Int16;
     378           0 :             break;
     379           0 :         case L2NS::DataType::dt_ushort:
     380           0 :             dt = GDT_UInt16;
     381           0 :             break;
     382           0 :         case L2NS::DataType::dt_int:
     383           0 :             dt = GDT_Int32;
     384           0 :             break;
     385           0 :         case L2NS::DataType::dt_uint:
     386           0 :             dt = GDT_UInt32;
     387           0 :             break;
     388           1 :         case L2NS::DataType::dt_float:
     389           1 :             dt = GDT_Float32;
     390           1 :             break;
     391           0 :         case L2NS::DataType::dt_double:
     392           0 :             dt = GDT_Float64;
     393           0 :             break;
     394           0 :         case L2NS::DataType::dt_char:
     395           0 :             dt = GDT_Int8;
     396           0 :             break;
     397           1 :         default:  // Unsigned byte
     398           1 :             dt = GDT_UInt8;
     399             :     }
     400           2 :     return dt;
     401             : }
     402             : 
     403        6425 : static L2NS::DataType GDTtoL2(GDALDataType dt)
     404             : {
     405             :     L2NS::DataType L2dt;
     406        6425 :     switch (dt)
     407             :     {
     408           2 :         case GDT_Int16:
     409           2 :             L2dt = L2NS::DataType::dt_short;
     410           2 :             break;
     411           2 :         case GDT_UInt16:
     412           2 :             L2dt = L2NS::DataType::dt_ushort;
     413           2 :             break;
     414           2 :         case GDT_Int32:
     415           2 :             L2dt = L2NS::DataType::dt_int;
     416           2 :             break;
     417           2 :         case GDT_UInt32:
     418           2 :             L2dt = L2NS::DataType::dt_uint;
     419           2 :             break;
     420           3 :         case GDT_Float32:
     421           3 :             L2dt = L2NS::DataType::dt_float;
     422           3 :             break;
     423           4 :         case GDT_Float64:
     424           4 :             L2dt = L2NS::DataType::dt_double;
     425           4 :             break;
     426        6410 :         default:
     427        6410 :             L2dt = L2NS::DataType::dt_uchar;
     428             :     }
     429        6425 :     return L2dt;
     430             : }
     431             : 
     432             : // Populate a LERC2 bitmask based on comparison with the image no data value
     433             : // Returns the number of NoData values found
     434             : template <typename T>
     435           1 : static size_t MaskFill(std::vector<Lerc1NS::Byte> &bm, const T *src,
     436             :                        const ILImage &img)
     437             : {
     438           1 :     size_t w = static_cast<size_t>(img.pagesize.x);
     439           1 :     size_t h = static_cast<size_t>(img.pagesize.y);
     440           1 :     size_t stride = static_cast<size_t>(img.pagesize.c);
     441           1 :     size_t nndv = 0;
     442             : 
     443           1 :     bm.resize(w * h);
     444             : 
     445           1 :     T ndv = static_cast<T>(img.NoDataValue);
     446           1 :     if (!img.hasNoData)
     447           0 :         ndv = 0;  // It really doesn't get called when img doesn't have
     448             :                   // NoDataValue
     449      262145 :     for (size_t i = 0; i < bm.size(); i++)
     450             :     {
     451      262144 :         if (ndv == src[i * stride])
     452             :         {
     453          57 :             bm[i] = 0;
     454          57 :             nndv++;
     455             :         }
     456             :         else
     457             :         {
     458      262087 :             bm[i] = 1;
     459             :         }
     460             :     }
     461             : 
     462           1 :     return nndv;
     463             : }
     464             : 
     465             : // Fill in no data values based on a LERC2 bitmask
     466             : template <typename T>
     467           1 : static void UnMask(std::vector<Lerc1NS::Byte> &bm, T *data, const ILImage &img)
     468             : {
     469           1 :     size_t w = static_cast<size_t>(img.pagesize.x);
     470           1 :     size_t h = static_cast<size_t>(img.pagesize.y);
     471           1 :     size_t stride = static_cast<size_t>(img.pagesize.c);
     472             : 
     473           1 :     if (bm.size() != w * h)
     474           0 :         return;
     475             : 
     476           1 :     T ndv = T(img.NoDataValue);
     477           1 :     if (stride == 1)
     478             :     {
     479      262145 :         for (size_t i = 0; i < w * h; i++)
     480      262144 :             if (!bm[i])
     481           0 :                 data[i] = ndv;
     482             :     }
     483             :     else
     484             :     {
     485           0 :         for (size_t i = 0; i < w * h; i++)
     486           0 :             if (!bm[i])
     487           0 :                 for (size_t c = 0; c < stride; c++)
     488           0 :                     data[i * stride + c] = ndv;
     489             :     }
     490             : }
     491             : 
     492        4812 : static CPLErr CompressLERC2(buf_mgr &dst, buf_mgr &src, const ILImage &img,
     493             :                             double precision, int l2ver)
     494             : {
     495        4812 :     auto w = static_cast<int>(img.pagesize.x);
     496        4812 :     auto h = static_cast<int>(img.pagesize.y);
     497        4812 :     auto stride = static_cast<int>(img.pagesize.c);
     498             : 
     499             :     // build a mask
     500        9624 :     std::vector<Lerc1NS::Byte> bm;
     501        4812 :     size_t nndv = 0;
     502        4812 :     if (img.hasNoData)
     503             :     {  // Only build a bitmask if no data value is defined
     504           1 :         switch (img.dt)
     505             :         {
     506             : 
     507             : #define MASK(T) nndv = MaskFill(bm, reinterpret_cast<T *>(src.buffer), img)
     508             : 
     509           1 :             case GDT_UInt8:
     510           1 :                 MASK(GByte);
     511           1 :                 break;
     512           0 :             case GDT_UInt16:
     513           0 :                 MASK(GUInt16);
     514           0 :                 break;
     515           0 :             case GDT_Int16:
     516           0 :                 MASK(GInt16);
     517           0 :                 break;
     518           0 :             case GDT_Int32:
     519           0 :                 MASK(GInt32);
     520           0 :                 break;
     521           0 :             case GDT_UInt32:
     522           0 :                 MASK(GUInt32);
     523           0 :                 break;
     524           0 :             case GDT_Float32:
     525           0 :                 MASK(float);
     526           0 :                 break;
     527           0 :             case GDT_Float64:
     528           0 :                 MASK(double);
     529           0 :                 break;
     530           0 :             default:
     531           0 :                 break;
     532             : 
     533             : #undef MASK
     534             :         }
     535             :     }
     536             : 
     537        4812 :     unsigned int sz = 0;
     538        4812 :     auto pbm = bm.data();
     539        4812 :     if (!bm.empty() && nndv != bm.size())
     540           1 :         pbm = nullptr;
     541       14436 :     auto status = lerc_encodeForVersion(
     542        4812 :         reinterpret_cast<void *>(src.buffer), l2ver,
     543        4812 :         static_cast<unsigned int>(GDTtoL2(img.dt)), stride, w, h, 1,
     544             : #if LERC_AT_LEAST_VERSION(3, 0, 0)
     545             :         pbm ? 1 : 0,
     546             : #endif
     547        4812 :         pbm, precision, reinterpret_cast<Lerc1NS::Byte *>(dst.buffer),
     548        4812 :         static_cast<unsigned int>(dst.size), &sz);
     549             : 
     550        4812 :     if (L2NS::ErrCode::Ok != static_cast<L2NS::ErrCode>(status) ||
     551        4812 :         sz > (dst.size - PADDING_BYTES))
     552             :     {
     553           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     554             :                  "MRF: Error during LERC2 compression");
     555           0 :         return CE_Failure;
     556             :     }
     557             : 
     558        4812 :     dst.size = static_cast<size_t>(sz) + PADDING_BYTES;
     559        4812 :     return CE_None;
     560             : }
     561             : 
     562             : // LERC1 splits of early, so this is mostly LERC2
     563        1627 : CPLErr LERC_Band::Decompress(buf_mgr &dst, buf_mgr &src)
     564             : {
     565        2695 :     if (src.size >= Lerc1Image::computeNumBytesNeededToWriteVoidImage() &&
     566        1068 :         IsLerc1(src.buffer))
     567          14 :         return DecompressLERC1(dst, src, img);
     568             : 
     569             :     // Can only be LERC2 here, verify
     570        1613 :     if (src.size < 50 || !IsLerc2(src.buffer))
     571             :     {
     572           0 :         CPLError(CE_Failure, CPLE_AppDefined, "MRF: Not a lerc tile");
     573           0 :         return CE_Failure;
     574             :     }
     575             : 
     576        1613 :     auto w = static_cast<int>(img.pagesize.x);
     577        1613 :     auto h = static_cast<int>(img.pagesize.y);
     578        1613 :     auto stride = static_cast<int>(img.pagesize.c);
     579             : 
     580        3226 :     std::vector<Lerc1NS::Byte> bm;
     581        1613 :     int nMasks = 0;
     582             : #if LERC_AT_LEAST_VERSION(3, 0, 0)
     583             :     {
     584             :         // Determine the number of masks
     585             :         std::vector<unsigned int> info(INFOIDX(nMasks) + 1);
     586             :         auto status =
     587             :             lerc_getBlobInfo(reinterpret_cast<Lerc1NS::Byte *>(src.buffer),
     588             :                              static_cast<unsigned int>(src.size), info.data(),
     589             :                              nullptr, static_cast<int>(info.size()), 0);
     590             :         if (L2NS::ErrCode::Ok == static_cast<L2NS::ErrCode>(status) &&
     591             :             1 == info[INFOIDX(nBands)])
     592             :         {
     593             :             nMasks = info[INFOIDX(nMasks)];
     594             :         }
     595             :     }
     596             : #else
     597        1613 :     if (img.hasNoData)
     598           1 :         nMasks = 1;
     599             : #endif
     600        1613 :     if (nMasks > 0)
     601           1 :         bm.resize(static_cast<size_t>(w) * static_cast<size_t>(h) * nMasks);
     602        1613 :     auto pbm = bm.data();
     603        1613 :     if (bm.empty())
     604        1612 :         pbm = nullptr;
     605             : 
     606             :     // Decoding may fail for many different reasons, including input not
     607             :     // matching tile expectations
     608             :     auto status =
     609        3226 :         lerc_decode(reinterpret_cast<Lerc1NS::Byte *>(src.buffer),
     610        1613 :                     static_cast<unsigned int>(src.size),
     611             : #if LERC_AT_LEAST_VERSION(3, 0, 0)
     612             :                     nMasks,
     613             : #endif
     614             :                     pbm, stride, w, h, 1,
     615        1613 :                     static_cast<unsigned int>(GDTtoL2(img.dt)), dst.buffer);
     616        1613 :     if (L2NS::ErrCode::Ok != static_cast<L2NS::ErrCode>(status))
     617             :     {
     618           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     619             :                  "MRF: Error decoding Lerc: status=%d", status);
     620           0 :         return CE_Failure;
     621             :     }
     622             : 
     623             :     // No mask means we're done
     624        1613 :     if (bm.empty())
     625        1612 :         return CE_None;
     626             : 
     627             :     // Fill in no data values
     628           1 :     switch (img.dt)
     629             :     {
     630             : #define UNMASK(T) UnMask(bm, reinterpret_cast<T *>(dst.buffer), img)
     631           1 :         case GDT_UInt8:
     632           1 :             UNMASK(GByte);
     633           1 :             break;
     634           0 :         case GDT_UInt16:
     635           0 :             UNMASK(GUInt16);
     636           0 :             break;
     637           0 :         case GDT_Int16:
     638           0 :             UNMASK(GInt16);
     639           0 :             break;
     640           0 :         case GDT_Int32:
     641           0 :             UNMASK(GInt32);
     642           0 :             break;
     643           0 :         case GDT_UInt32:
     644           0 :             UNMASK(GUInt32);
     645           0 :             break;
     646           0 :         case GDT_Float32:
     647           0 :             UNMASK(float);
     648           0 :             break;
     649           0 :         case GDT_Float64:
     650           0 :             UNMASK(double);
     651           0 :             break;
     652           0 :         default:
     653           0 :             break;
     654             : #undef DECODE
     655             :     }
     656           1 :     return CE_None;
     657             : }
     658             : 
     659        4822 : CPLErr LERC_Band::Compress(buf_mgr &dst, buf_mgr &src)
     660             : {
     661        4822 :     if (version == 2)
     662        4812 :         return CompressLERC2(dst, src, img, precision, l2ver);
     663             :     else
     664          10 :         return CompressLERC1(dst, src, img, precision);
     665             : }
     666             : 
     667           7 : CPLXMLNode *LERC_Band::GetMRFConfig(GDALOpenInfo *poOpenInfo)
     668             : {
     669             :     // Header of Lerc2 takes 58 bytes, an empty area 62 or more, depending on
     670             :     // the subversion. Size of Lerc1 empty file is 67 Anything under 50 bytes
     671             :     // can't be lerc
     672           7 :     if (poOpenInfo->eAccess != GA_ReadOnly ||
     673           7 :         poOpenInfo->pszFilename == nullptr ||
     674           7 :         poOpenInfo->pabyHeader == nullptr ||
     675           7 :         strlen(poOpenInfo->pszFilename) < 1 || poOpenInfo->nHeaderBytes < 50)
     676           0 :         return nullptr;
     677             : 
     678             :     // Check the header too
     679           7 :     char *psz = reinterpret_cast<char *>(poOpenInfo->pabyHeader);
     680          14 :     CPLString sHeader;
     681           7 :     sHeader.assign(psz, psz + poOpenInfo->nHeaderBytes);
     682           7 :     if (!(IsLerc1(sHeader) || IsLerc2(sHeader)))
     683           0 :         return nullptr;
     684             : 
     685           7 :     GDALDataType dt = GDT_Unknown;  // Use this as a validity flag
     686             : 
     687             :     // Use this structure to fetch width and height
     688           7 :     ILSize size(-1, -1, 1, 1, 1);
     689             : 
     690          12 :     if (IsLerc1(sHeader) &&
     691           5 :         sHeader.size() >= Lerc1Image::computeNumBytesNeededToWriteVoidImage())
     692             :     {
     693           5 :         if (Lerc1Image::getwh(reinterpret_cast<Lerc1NS::Byte *>(psz),
     694           5 :                               poOpenInfo->nHeaderBytes, size.x, size.y))
     695           5 :             dt = GDALGetDataTypeByName(CSLFetchNameValueDef(
     696           5 :                 poOpenInfo->papszOpenOptions, "DATATYPE", "Byte"));
     697             :     }
     698           2 :     else if (IsLerc2(sHeader))
     699             :     {
     700             :         // getBlobInfo will fail without the whole LERC blob
     701             :         // Wasteful, but that's the only choice given by the LERC C API
     702             :         // This will only work if the Lerc2 file is under the constant defined
     703             :         // here
     704             :         static const GIntBig MAX_L2SIZE(10 * 1024 * 1024);  // 10MB
     705           2 :         GByte *buffer = nullptr;
     706             :         vsi_l_offset l2size;
     707             : 
     708           2 :         if (VSIIngestFile(nullptr, poOpenInfo->pszFilename, &buffer, &l2size,
     709           2 :                           MAX_L2SIZE))
     710             :         {
     711             :             //! Info returned in infoArray is { version, dataType, nDim, nCols,
     712             :             //! nRows, nBands, nValidPixels... }, see Lerc_types.h .
     713           4 :             std::vector<unsigned int> info(INFOIDX(nValidPixels) + 1);
     714             :             auto status =
     715           2 :                 lerc_getBlobInfo(reinterpret_cast<Lerc1NS::Byte *>(buffer),
     716             :                                  static_cast<unsigned int>(l2size), info.data(),
     717           2 :                                  nullptr, static_cast<int>(info.size()), 0);
     718           2 :             VSIFree(buffer);
     719           4 :             if (L2NS::ErrCode::Ok == static_cast<L2NS::ErrCode>(status) &&
     720           2 :                 1 == info[INFOIDX(nBands)])
     721             :             {
     722           2 :                 size.x = info[INFOIDX(nCols)];
     723           2 :                 size.y = info[INFOIDX(nRows)];
     724           2 :                 if (info[INFOIDX(version)] > 3)  // Single band before version 4
     725           0 :                     size.c = info[INFOIDX(nDim)];
     726           2 :                 dt = L2toGDT(
     727           2 :                     static_cast<L2NS::DataType>(info[INFOIDX(dataType)]));
     728             :             }
     729             :         }
     730             :     }
     731             : 
     732           7 :     if (size.x <= 0 || size.y <= 0 || dt == GDT_Unknown)
     733           0 :         return nullptr;
     734             : 
     735             :     // Build and return the MRF configuration for a single tile reader
     736           7 :     CPLXMLNode *config = CPLCreateXMLNode(nullptr, CXT_Element, "MRF_META");
     737           7 :     CPLXMLNode *raster = CPLCreateXMLNode(config, CXT_Element, "Raster");
     738           7 :     XMLSetAttributeVal(raster, "Size", size, "%.0f");
     739           7 :     XMLSetAttributeVal(raster, "PageSize", size, "%.0f");
     740           7 :     CPLCreateXMLElementAndValue(raster, "Compression", CompName(IL_LERC));
     741           7 :     CPLCreateXMLElementAndValue(raster, "DataType", GDALGetDataTypeName(dt));
     742           7 :     CPLCreateXMLElementAndValue(raster, "DataFile", poOpenInfo->pszFilename);
     743             :     // Set a magic index file name to prevent the driver from attempting to open
     744             :     // it
     745           7 :     CPLCreateXMLElementAndValue(raster, "IndexFile", "(null)");
     746             :     // The NDV could be passed as an open option
     747             :     const char *pszNDV =
     748           7 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "NDV", "");
     749           7 :     if (strlen(pszNDV) > 0)
     750             :     {
     751             :         CPLXMLNode *values =
     752           4 :             CPLCreateXMLNode(raster, CXT_Element, "DataValues");
     753           4 :         XMLSetAttributeVal(values, "NoData", pszNDV);
     754             :     }
     755           7 :     return config;
     756             : }
     757             : 
     758          76 : LERC_Band::LERC_Band(MRFDataset *pDS, const ILImage &image, int b, int level)
     759          76 :     : MRFRasterBand(pDS, image, b, level)
     760             : {
     761             :     // Lerc doesn't handle 64bit int types
     762          76 :     if (image.dt == GDT_UInt64 || image.dt == GDT_Int64)
     763             :     {
     764           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     765             :                  "Lerc compression of 64 bit integers is not supported");
     766           0 :         return;
     767             :     }
     768             : 
     769             :     // Pick 1/1000 for floats and 0.5 losless for integers.
     770          76 :     if (eDataType == GDT_Float32 || eDataType == GDT_Float64)
     771          18 :         precision = strtod(GetOptionValue("LERC_PREC", ".001"), nullptr);
     772             :     else
     773          58 :         precision =
     774          58 :             std::max(0.5, strtod(GetOptionValue("LERC_PREC", ".5"), nullptr));
     775             : 
     776             :     // Encode in V2 by default.
     777          76 :     version = GetOptlist().FetchBoolean("V1", FALSE) ? 1 : 2;
     778             :     // For LERC 2 there are multiple versions too, -1 means use the library
     779             :     // default Use v2.2 for single band encoding
     780         152 :     l2ver = atoi(GetOptlist().FetchNameValueDef(
     781          76 :         "L2_VER", (img.pagesize.c == 1) ? "2" : "-1"));
     782             : 
     783          76 :     if (image.pageSizeBytes > INT_MAX / 4)
     784             :     {
     785           0 :         CPLError(CE_Failure, CPLE_AppDefined, "LERC page too large");
     786           0 :         return;
     787             :     }
     788             :     // Enlarge the page buffer, LERC may expand data.
     789          76 :     pDS->SetPBufferSize(2 * image.pageSizeBytes);
     790             : }
     791             : 
     792         152 : LERC_Band::~LERC_Band()
     793             : {
     794         152 : }
     795             : 
     796             : NAMESPACE_MRF_END

Generated by: LCOV version 1.14