LCOV - code coverage report
Current view: top level - frmts/mrf - QB3_band.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 71 112 63.4 %
Date: 2025-07-09 17:50:03 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /*
       2             : Copyright 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             : 
      13             : QB3 band implementation
      14             : QB3 page compression and decompression functions
      15             : 
      16             : Authors:  Lucian Plesea
      17             : */
      18             : #include "marfa.h"
      19             : #include <QB3.h>
      20             : 
      21             : NAMESPACE_MRF_START
      22          14 : CPLErr QB3_Band::Compress(buf_mgr &dst, buf_mgr &src)
      23             : {
      24          14 :     auto bands = static_cast<size_t>(img.pagesize.c);
      25          14 :     encsp pQB3 = nullptr;
      26             : #define CREATE_QB3(T)                                                          \
      27             :     qb3_create_encoder(img.pagesize.x, img.pagesize.y, bands, qb3_dtype::T)
      28             : 
      29          14 :     switch (img.dt)
      30             :     {
      31           9 :         case (GDT_Byte):
      32           9 :             pQB3 = CREATE_QB3(QB3_U8);
      33           9 :             break;
      34           1 :         case (GDT_Int16):
      35           1 :             pQB3 = CREATE_QB3(QB3_I16);
      36           1 :             break;
      37           1 :         case (GDT_UInt16):
      38           1 :             pQB3 = CREATE_QB3(QB3_U16);
      39           1 :             break;
      40           1 :         case (GDT_Int32):
      41           1 :             pQB3 = CREATE_QB3(QB3_I32);
      42           1 :             break;
      43           1 :         case (GDT_UInt32):
      44           1 :             pQB3 = CREATE_QB3(QB3_U32);
      45           1 :             break;
      46           1 :         case (GDT_Int64):
      47           1 :             pQB3 = CREATE_QB3(QB3_I64);
      48           1 :             break;
      49           0 :         case (GDT_UInt64):
      50           0 :             pQB3 = CREATE_QB3(QB3_U64);
      51           0 :             break;
      52           0 :         default:
      53           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
      54             :                      "MRF:QB3 Data type not supported");
      55           0 :             return CE_Failure;
      56             :     }
      57             : #undef CREATE_QB3
      58             : 
      59          14 :     if (nullptr == pQB3)
      60             :     {
      61           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
      62             :                  "MRF:QB3 Cannot create encoder");
      63           0 :         return CE_Failure;
      64             :     }
      65             : 
      66          14 :     CPLErr status = CE_None;
      67             :     try
      68             :     {
      69          14 :         if (dst.size < qb3_max_encoded_size(pQB3))
      70             :         {
      71           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
      72             :                      "MRF:QB3 encoded buffer size too small");
      73           0 :             throw CE_Failure;
      74             :         }
      75             : 
      76             :         // Use independent band compression when by default band 1 is core band
      77          15 :         if (coreband.empty() && (3 == bands || 4 == bands) &&
      78           1 :             EQUAL(poMRFDS->GetPhotometricInterpretation(), "MULTISPECTRAL"))
      79             :         {
      80           0 :             size_t corebands[] = {0, 1, 2, 3};  // Identity, no core bands
      81           0 :             qb3_set_encoder_coreband(pQB3, bands, corebands);
      82             :         }
      83             : 
      84          14 :         if (!coreband.empty())
      85           1 :             qb3_set_encoder_coreband(pQB3, bands, coreband.data());
      86             : 
      87             :         // Quality of 90 and above trigger the better encoding
      88          14 :         qb3_set_encoder_mode(pQB3, (img.quality > 90) ? QB3M_BEST : QB3M_BASE);
      89             : 
      90             : #if defined(QB3_HAS_FTL)
      91             :         // Quality below 5 triggers the faster encoding, when available
      92          14 :         if (img.quality < 5)
      93           0 :             qb3_set_encoder_mode(pQB3, QB3M_FTL);
      94             : #endif
      95             : 
      96          14 :         dst.size = qb3_encode(pQB3, src.buffer, dst.buffer);
      97          14 :         if (0 == dst.size)
      98             :         {
      99           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
     100             :                      "MRF:QB3 encoding failed");
     101           0 :             throw CE_Failure;
     102             :         }
     103             : 
     104             :         // Never happens if qb3_max_encoded doesn't lie
     105          14 :         if (dst.size > qb3_max_encoded_size(pQB3))
     106             :         {
     107           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
     108             :                      "MRF:QB3 encoded size exceeds limit, check QB3 library");
     109           0 :             throw CE_Failure;
     110             :         }
     111             :     }
     112           0 :     catch (CPLErr error)
     113             :     {
     114           0 :         status = error;
     115             :     }
     116          14 :     qb3_destroy_encoder(pQB3);
     117          14 :     return status;
     118             : }
     119             : 
     120          10 : CPLErr QB3_Band::Decompress(buf_mgr &dst, buf_mgr &src)
     121             : {
     122             :     size_t img_size[3];
     123          10 :     auto pdQB3 = qb3_read_start(src.buffer, src.size, img_size);
     124          10 :     if (nullptr == pdQB3)
     125             :     {
     126           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     127             :                  "MRF: QB3 can't create decoder, is it a valid QB3 stream?");
     128           0 :         return CE_Failure;
     129             :     }
     130             : 
     131          10 :     CPLErr status = CE_None;
     132             :     try
     133             :     {
     134          10 :         if (img_size[0] != static_cast<size_t>(img.pagesize.x) ||
     135          10 :             img_size[1] != static_cast<size_t>(img.pagesize.y) ||
     136          10 :             img_size[2] != static_cast<size_t>(img.pagesize.c))
     137             :         {
     138           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     139             :                      "MRF: QB Page has invalid size");
     140           0 :             throw CE_Failure;
     141             :         }
     142             : 
     143          10 :         if (!qb3_read_info(pdQB3))
     144             :         {
     145           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     146             :                      "MRF: QB3 metadata read failure");
     147           0 :             throw CE_Failure;
     148             :         }
     149             : 
     150          10 :         if (static_cast<size_t>(img.pageSizeBytes) != qb3_decoded_size(pdQB3))
     151             :         {
     152           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     153             :                      "MRF: QB3 incorrect decoded tile size");
     154           0 :             throw CE_Failure;
     155             :         }
     156             : 
     157          10 :         dst.size = qb3_read_data(pdQB3, dst.buffer);
     158          10 :         if (static_cast<size_t>(img.pageSizeBytes) != dst.size)
     159             :         {
     160           0 :             CPLError(CE_Failure, CPLE_AppDefined, "MRF: QB3 decoding error");
     161           0 :             throw CE_Failure;
     162             :         }
     163             :     }
     164           0 :     catch (CPLErr error)
     165             :     {
     166           0 :         status = error;
     167             :     }
     168          10 :     qb3_destroy_decoder(pdQB3);
     169          10 :     return status;
     170             : }
     171             : 
     172          53 : QB3_Band::QB3_Band(MRFDataset *pDS, const ILImage &image, int b, int level)
     173          53 :     : MRFRasterBand(pDS, image, b, level)
     174             : {
     175             :     static_assert(CPL_IS_LSB,
     176             :                   "QB3 is only implemented for little endian architectures");
     177          53 :     if (image.pageSizeBytes > INT_MAX / 4)
     178             :     {
     179           0 :         CPLError(CE_Failure, CPLE_AppDefined, "QB3 page too large");
     180           0 :         return;
     181             :     }
     182             : 
     183          53 :     if (0 != nBlockXSize % 4 || 0 != nBlockYSize % 4)
     184             :     {
     185           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     186             :                  "QB3 page size has to be a multiple of 4");
     187           0 :         return;
     188             :     }
     189             : 
     190          53 :     if (!GDALDataTypeIsInteger(image.dt) || GDALDataTypeIsComplex(image.dt))
     191             :     {
     192           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     193             :                  "Data type not supported by QB3 compression");
     194           0 :         return;
     195             :     }
     196             : 
     197             :     // Pick up the band map. Comma separated list of bands
     198             :     // Either identity (core) or derived from a core band.
     199             :     // Missing entries are assumed to be identity.
     200         106 :     std::string setting(GetOptionValue("QB3_BAND_MAP", ""));
     201          53 :     if (image.pagesize.c != 1 && !setting.empty())
     202             :     {
     203             :         auto tokens =
     204           9 :             CSLTokenizeString2(setting.c_str(), ",", CSLT_ALLOWEMPTYTOKENS);
     205           9 :         coreband.resize(image.pagesize.c);
     206          36 :         for (int i = 0; i < image.pagesize.c; i++)
     207             :         {
     208          27 :             coreband[i] = i;
     209          27 :             if (tokens && tokens[i] && strlen(tokens[i]) > 0 &&
     210          18 :                 std::isdigit(tokens[i][0]))
     211             :             {
     212          18 :                 auto c = atoi(tokens[i]);
     213          18 :                 if (c < 0 || c >= image.pagesize.c)
     214             :                 {
     215           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
     216             :                              "Invalid band %d in QB3_BAND_MAP", c);
     217           0 :                     continue;
     218             :                 }
     219          18 :                 coreband[i] = c;
     220             :             }
     221             :         }
     222           9 :         CSLDestroy(tokens);
     223             :         // Second pass to check that bands are either core or derived
     224          36 :         for (int i = 0; i < image.pagesize.c; i++)
     225             :         {
     226          27 :             const auto c = coreband[i];
     227          27 :             if (c == static_cast<size_t>(i) || c == coreband[c])
     228          27 :                 continue;  // Core band or derived from core band
     229           0 :             CPLError(CE_Warning, CPLE_NotSupported,
     230             :                      "Band %d in QB3_BAND_MAP is not a core band", i);
     231           0 :             coreband[i] = i;  // Reset to identity
     232             :         }
     233             :     }
     234             :     // Should use qb3_max_encoded_size();
     235             : 
     236             :     // Enlarge the page buffer, QB3 may expand data.
     237          53 :     pDS->SetPBufferSize(2 * image.pageSizeBytes);
     238             : }
     239             : 
     240             : NAMESPACE_MRF_END

Generated by: LCOV version 1.14