LCOV - code coverage report
Current view: top level - frmts/pcidsk/sdk/channel - cpcidskchannel.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 119 195 61.0 %
Date: 2026-02-12 23:49:34 Functions: 14 26 53.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Purpose:  Implementation of the CPCIDSKChannel Abstract class.
       4             :  *
       5             :  ******************************************************************************
       6             :  * Copyright (c) 2009
       7             :  * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #include "pcidsk_config.h"
      13             : #include "pcidsk_types.h"
      14             : #include "core/pcidsk_utils.h"
      15             : #include "pcidsk_exception.h"
      16             : #include "pcidsk_channel.h"
      17             : #include "core/cpcidskfile.h"
      18             : #include "channel/cpcidskchannel.h"
      19             : #include "channel/ctiledchannel.h"
      20             : #include <cstring>
      21             : #include <cassert>
      22             : #include <cstdlib>
      23             : #include <cstring>
      24             : #include <cstdio>
      25             : 
      26             : using namespace PCIDSK;
      27             : 
      28             : PCIDSKChannel::~PCIDSKChannel() = default;
      29             : 
      30             : /************************************************************************/
      31             : /*                           CPCIDSKChannel()                           */
      32             : /************************************************************************/
      33             : 
      34         253 : CPCIDSKChannel::CPCIDSKChannel( PCIDSKBuffer &image_header,
      35             :                                 uint64 ih_offsetIn,
      36             :                                 CPCIDSKFile *fileIn,
      37             :                                 eChanType pixel_typeIn,
      38         253 :                                 int channel_numberIn )
      39             : 
      40             : {
      41         253 :     this->pixel_type = pixel_typeIn;
      42         253 :     this->file = fileIn;
      43         253 :     this->channel_number = channel_numberIn;
      44         253 :     this->ih_offset = ih_offsetIn;
      45         253 :     is_locked = false;
      46         253 :     byte_order = 'N';
      47         253 :     needs_swap = !BigEndianSystem();
      48             : 
      49         253 :     width = file->GetWidth();
      50         253 :     height = file->GetHeight();
      51             : 
      52         253 :     block_width = width;
      53         253 :     block_height = 1;
      54             : 
      55             : /* -------------------------------------------------------------------- */
      56             : /*      Establish if we need to byte swap the data on load/store.       */
      57             : /* -------------------------------------------------------------------- */
      58         253 :     if( channel_number != -1 )
      59             :     {
      60         242 :         unsigned short test_value = 1;
      61             : 
      62         242 :         is_locked = image_header.buffer[200] == 'W';
      63         242 :         byte_order = image_header.buffer[201];
      64         242 :         if( (reinterpret_cast<uint8 *>(&test_value))[0] == 1 )
      65         242 :             needs_swap = (byte_order != 'S');
      66             :         else
      67           0 :             needs_swap = (byte_order == 'S');
      68             : 
      69         242 :         if( pixel_type == CHN_8U )
      70         173 :             needs_swap = 0;
      71             : 
      72         242 :         LoadHistory( image_header );
      73             : 
      74             : /* -------------------------------------------------------------------- */
      75             : /*      Initialize the metadata object, but do not try to load till     */
      76             : /*      needed.  We avoid doing this for unassociated channels such     */
      77             : /*      as overviews.                                                   */
      78             : /* -------------------------------------------------------------------- */
      79         242 :         metadata.Initialize( file, "IMG", channel_number );
      80             :     }
      81             : 
      82             : /* -------------------------------------------------------------------- */
      83             : /*      No overviews for unassociated files, so just mark them as       */
      84             : /*      initialized.                                                    */
      85             : /* -------------------------------------------------------------------- */
      86         253 :     overviews_initialized = (channel_number == -1);
      87         253 : }
      88             : 
      89             : /************************************************************************/
      90             : /*                          ~CPCIDSKChannel()                           */
      91             : /************************************************************************/
      92             : 
      93         253 : CPCIDSKChannel::~CPCIDSKChannel()
      94             : 
      95             : {
      96         253 :     InvalidateOverviewInfo();
      97         253 : }
      98             : 
      99             : /************************************************************************/
     100             : /*                       InvalidateOverviewInfo()                       */
     101             : /*                                                                      */
     102             : /*      This is called when CreateOverviews() creates overviews - we    */
     103             : /*      invalidate our loaded info and re-establish on a next request.  */
     104             : /************************************************************************/
     105             : 
     106         253 : void CPCIDSKChannel::InvalidateOverviewInfo()
     107             : 
     108             : {
     109         264 :     for( size_t io=0; io < overview_bands.size(); io++ )
     110             :     {
     111          11 :         if( overview_bands[io] != nullptr )
     112             :         {
     113          11 :             delete overview_bands[io];
     114          11 :             overview_bands[io] = nullptr;
     115             :         }
     116             :     }
     117             : 
     118         253 :     overview_infos.clear();
     119         253 :     overview_bands.clear();
     120         253 :     overview_decimations.clear();
     121             : 
     122         253 :     overviews_initialized = false;
     123         253 : }
     124             : 
     125             : /************************************************************************/
     126             : /*                       EstablishOverviewInfo()                        */
     127             : /************************************************************************/
     128        1588 : void CPCIDSKChannel::EstablishOverviewInfo() const
     129             : 
     130             : {
     131        1588 :     if( overviews_initialized )
     132        1355 :         return;
     133             : 
     134         233 :     overviews_initialized = true;
     135             : 
     136         466 :     std::vector<std::string> keys = GetMetadataKeys();
     137         233 :     if( !keys.empty() )
     138             :     {
     139          12 :         std::sort(keys.begin(), keys.end(),
     140          10 :               [] (const std::string &first,
     141             :                              const std::string &second)
     142             :                 {
     143          10 :                     if( !STARTS_WITH(first.c_str(), "_Overview_") ||
     144           0 :                         !STARTS_WITH(second.c_str(), "_Overview_") )
     145             :                     {
     146          10 :                         return false;
     147             :                     }
     148           0 :                     int nFirst = atoi(first.c_str() + 10);
     149           0 :                     int nSecond = atoi(second.c_str() + 10);
     150           0 :                     return nFirst < nSecond;
     151             :                 });
     152             :     }
     153             : 
     154         250 :     for( const std::string& key: keys)
     155             :     {
     156          17 :         if( !STARTS_WITH(key.c_str(), "_Overview_") )
     157           7 :             continue;
     158             : 
     159          10 :         overview_infos.push_back( GetMetadataValue( key ) );
     160          10 :         overview_bands.push_back( nullptr );
     161          10 :         overview_decimations.push_back( atoi(key.c_str()+10) );
     162             :     }
     163             : }
     164             : 
     165             : /************************************************************************/
     166             : /*                         UpdateOverviewInfo()                         */
     167             : /************************************************************************/
     168             : /** Update the in-memory information for an overview.
     169             :   * This method will add overview information to the in-memory arrays
     170             :   *
     171             :   * @param  pszOverviewMDValue  Overview value
     172             :   *
     173             :   * @param  nFactor             Overview factor i.e. 2, 4, etc
     174             :   */
     175           1 : void CPCIDSKChannel::UpdateOverviewInfo(const char *pszOverviewMDValue,
     176             :                                         int nFactor)
     177             : {
     178           1 :     overview_infos.push_back( pszOverviewMDValue );
     179           1 :     overview_bands.push_back( nullptr );
     180           1 :     overview_decimations.push_back( nFactor );
     181           1 : }
     182             : 
     183             : /************************************************************************/
     184             : /*                           GetBlockCount()                            */
     185             : /************************************************************************/
     186             : 
     187           0 : int CPCIDSKChannel::GetBlockCount() const
     188             : 
     189             : {
     190             :     // We deliberately call GetBlockWidth() and GetWidth() to trigger
     191             :     // computation of the values for tiled layers.  At some point it would
     192             :     // be good to cache the block count as this computation is a bit expensive
     193             : 
     194           0 :     int x_block_count = DIV_ROUND_UP(GetWidth(), GetBlockWidth());
     195           0 :     int y_block_count = DIV_ROUND_UP(GetHeight(), GetBlockHeight());
     196             : 
     197           0 :     return x_block_count * y_block_count;
     198             : }
     199             : 
     200             : /************************************************************************/
     201             : /*                          GetOverviewCount()                          */
     202             : /************************************************************************/
     203             : 
     204         911 : int CPCIDSKChannel::GetOverviewCount()
     205             : 
     206             : {
     207         911 :     EstablishOverviewInfo();
     208             : 
     209         911 :     return static_cast<int>(overview_infos.size());
     210             : }
     211             : 
     212             : /************************************************************************/
     213             : /*                            GetOverview()                             */
     214             : /************************************************************************/
     215             : 
     216          11 : PCIDSKChannel *CPCIDSKChannel::GetOverview( int overview_index )
     217             : 
     218             : {
     219          11 :     EstablishOverviewInfo();
     220             : 
     221          11 :     if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
     222           0 :         return (PCIDSKChannel*)ThrowPCIDSKExceptionPtr( "Non existent overview (%d) requested.",
     223           0 :                               overview_index );
     224             : 
     225          11 :     if( overview_bands[overview_index] == nullptr )
     226             :     {
     227          22 :         PCIDSKBuffer image_header(1024), file_header(1024);
     228             :         char  pseudo_filename[65];
     229             : 
     230          11 :         snprintf( pseudo_filename, sizeof(pseudo_filename), "/SIS=%d",
     231          11 :                  atoi(overview_infos[overview_index].c_str()) );
     232             : 
     233          11 :         image_header.Put( pseudo_filename, 64, 64 );
     234             : 
     235          11 :         overview_bands[overview_index] =
     236             :             new CTiledChannel( image_header, 0, file_header, -1, file,
     237          11 :                                CHN_UNKNOWN );
     238             :     }
     239             : 
     240          11 :     return overview_bands[overview_index];
     241             : }
     242             : 
     243             : /************************************************************************/
     244             : /*                          IsOverviewValid()                           */
     245             : /************************************************************************/
     246             : 
     247           0 : bool CPCIDSKChannel::IsOverviewValid( int overview_index )
     248             : 
     249             : {
     250           0 :     EstablishOverviewInfo();
     251             : 
     252           0 :     if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
     253           0 :         return ThrowPCIDSKException(0, "Non existent overview (%d) requested.",
     254           0 :                               overview_index ) != 0;
     255             : 
     256           0 :     int sis_id, validity=0;
     257             : 
     258           0 :     sscanf( overview_infos[overview_index].c_str(), "%d %d",
     259             :             &sis_id, &validity );
     260             : 
     261           0 :     return validity != 0;
     262             : }
     263             : 
     264             : /************************************************************************/
     265             : /*                       GetOverviewResampling()                        */
     266             : /************************************************************************/
     267             : 
     268           0 : std::string CPCIDSKChannel::GetOverviewResampling( int overview_index )
     269             : 
     270             : {
     271           0 :     EstablishOverviewInfo();
     272             : 
     273           0 :     if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
     274             :     {
     275           0 :         ThrowPCIDSKException( "Non existent overview (%d) requested.",
     276             :                               overview_index );
     277           0 :         return "";
     278             :     }
     279             : 
     280           0 :     int sis_id, validity=0;
     281             :     char resampling[17];
     282             : 
     283           0 :     sscanf( overview_infos[overview_index].c_str(), "%d %d %16s",
     284             :             &sis_id, &validity, &(resampling[0]) );
     285             : 
     286           0 :     return resampling;
     287             : }
     288             : 
     289             : /************************************************************************/
     290             : /*                        SetOverviewValidity()                         */
     291             : /************************************************************************/
     292             : 
     293          21 : void CPCIDSKChannel::SetOverviewValidity( int overview_index,
     294             :                                           bool new_validity )
     295             : 
     296             : {
     297          21 :     EstablishOverviewInfo();
     298             : 
     299          21 :     if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
     300           0 :         return ThrowPCIDSKException( "Non existent overview (%d) requested.",
     301          19 :                               overview_index );
     302             : 
     303          21 :     int sis_id, validity=0;
     304             :     char resampling[17];
     305             : 
     306          21 :     sscanf( overview_infos[overview_index].c_str(), "%d %d %16s",
     307             :             &sis_id, &validity, &(resampling[0]) );
     308             : 
     309             :     // are we already set to this value?
     310          21 :     if( new_validity == (validity != 0) )
     311          19 :         return;
     312             : 
     313             :     char new_info[48];
     314             : 
     315           2 :     snprintf( new_info, sizeof(new_info), "%d %d %s",
     316             :              sis_id, (new_validity ? 1 : 0 ), resampling );
     317             : 
     318           2 :     overview_infos[overview_index] = new_info;
     319             : 
     320             :     // write back to metadata.
     321             :     char key[20];
     322           2 :     snprintf( key, sizeof(key), "_Overview_%d", overview_decimations[overview_index] );
     323             : 
     324           2 :     SetMetadataValue( key, new_info );
     325             : }
     326             : 
     327             : /************************************************************************/
     328             : /*                        InvalidateOverviews()                         */
     329             : /*                                                                      */
     330             : /*      Whenever a write is done on this band, we will invalidate       */
     331             : /*      any previously valid overviews.                                 */
     332             : /************************************************************************/
     333             : 
     334         645 : void CPCIDSKChannel::InvalidateOverviews()
     335             : 
     336             : {
     337         645 :     EstablishOverviewInfo();
     338             : 
     339         665 :     for( int i = 0; i < GetOverviewCount(); i++ )
     340          20 :         SetOverviewValidity( i, false );
     341         645 : }
     342             : 
     343             : /************************************************************************/
     344             : /*                      GetOverviewLevelMapping()                       */
     345             : /************************************************************************/
     346             : 
     347           0 : std::vector<int> CPCIDSKChannel::GetOverviewLevelMapping() const
     348             : {
     349           0 :     EstablishOverviewInfo();
     350             : 
     351           0 :     return overview_decimations;
     352             : }
     353             : 
     354             : /************************************************************************/
     355             : /*                            GetFilename()                             */
     356             : /************************************************************************/
     357           0 : std::string CPCIDSKChannel::GetFilename() const
     358             : {
     359           0 :     return file->GetFilename();
     360             : }
     361             : 
     362             : /************************************************************************/
     363             : /*                           GetDescription()                           */
     364             : /************************************************************************/
     365             : 
     366         249 : std::string CPCIDSKChannel::GetDescription()
     367             : 
     368             : {
     369         249 :     if( ih_offset == 0 )
     370           0 :         return "";
     371             : 
     372         498 :     PCIDSKBuffer ih_1(64);
     373         498 :     std::string ret;
     374             : 
     375         249 :     file->ReadFromFile( ih_1.buffer, ih_offset, 64 );
     376         249 :     ih_1.Get(0,64,ret);
     377             : 
     378         249 :     return ret;
     379             : }
     380             : 
     381             : /************************************************************************/
     382             : /*                           SetDescription()                           */
     383             : /************************************************************************/
     384             : 
     385           3 : void CPCIDSKChannel::SetDescription( const std::string &description )
     386             : 
     387             : {
     388           3 :     if( ih_offset == 0 )
     389           0 :         return ThrowPCIDSKException( "Description cannot be set on overviews." );
     390             : 
     391           6 :     PCIDSKBuffer ih_1(64);
     392           3 :     ih_1.Put( description.c_str(), 0, 64 );
     393           3 :     file->WriteToFile( ih_1.buffer, ih_offset, 64 );
     394             : }
     395             : 
     396             : /************************************************************************/
     397             : /*                            LoadHistory()                             */
     398             : /************************************************************************/
     399             : 
     400         242 : void CPCIDSKChannel::LoadHistory( const PCIDSKBuffer &image_header )
     401             : 
     402             : {
     403             :     // Read the history from the image header. PCIDSK supports
     404             :     // 8 history entries per channel.
     405             : 
     406         484 :     std::string hist_msg;
     407         242 :     history_.clear();
     408        2178 :     for (unsigned int i = 0; i < 8; i++)
     409             :     {
     410        1936 :         image_header.Get(384 + i * 80, 80, hist_msg);
     411             : 
     412             :         // Some programs seem to push history records with a trailing '\0'
     413             :         // so do some extra processing to cleanup.  FUN records on segment
     414             :         // 3 of eltoro.pix are an example of this.
     415        1936 :         size_t size = hist_msg.size();
     416           0 :         while( size > 0
     417        1936 :                && (hist_msg[size-1] == ' ' || hist_msg[size-1] == '\0') )
     418           0 :             size--;
     419             : 
     420        1936 :         hist_msg.resize(size);
     421             : 
     422        1936 :         history_.push_back(hist_msg);
     423             :     }
     424         242 : }
     425             : 
     426             : /************************************************************************/
     427             : /*                         GetHistoryEntries()                          */
     428             : /************************************************************************/
     429             : 
     430           0 : std::vector<std::string> CPCIDSKChannel::GetHistoryEntries() const
     431             : {
     432           0 :     return history_;
     433             : }
     434             : 
     435             : /************************************************************************/
     436             : /*                         SetHistoryEntries()                          */
     437             : /************************************************************************/
     438             : 
     439           0 : void CPCIDSKChannel::SetHistoryEntries(const std::vector<std::string> &entries)
     440             : 
     441             : {
     442           0 :     if( ih_offset == 0 )
     443           0 :         return ThrowPCIDSKException( "Attempt to update history on a raster that is not\na conventional band with an image header." );
     444             : 
     445           0 :     PCIDSKBuffer image_header(1024);
     446             : 
     447           0 :     file->ReadFromFile( image_header.buffer, ih_offset, 1024 );
     448             : 
     449           0 :     for( unsigned int i = 0; i < 8; i++ )
     450             :     {
     451           0 :         const char *msg = "";
     452           0 :         if( entries.size() > i )
     453           0 :             msg = entries[i].c_str();
     454             : 
     455           0 :         image_header.Put( msg, 384 + i * 80, 80 );
     456             :     }
     457             : 
     458           0 :     file->WriteToFile( image_header.buffer, ih_offset, 1024 );
     459             : 
     460             :     // Force reloading of history_
     461           0 :     LoadHistory( image_header );
     462             : }
     463             : 
     464             : /************************************************************************/
     465             : /*                            PushHistory()                             */
     466             : /************************************************************************/
     467             : 
     468           0 : void CPCIDSKChannel::PushHistory( const std::string &app,
     469             :                                   const std::string &message )
     470             : 
     471             : {
     472             : #define MY_MIN(a,b)      ((a<b) ? a : b)
     473             : 
     474             :     char current_time[17];
     475             :     char history[81];
     476             : 
     477           0 :     GetCurrentDateTime( current_time );
     478             : 
     479           0 :     memset( history, ' ', 80 );
     480           0 :     history[80] = '\0';
     481             : 
     482           0 :     memcpy( history + 0, app.c_str(), MY_MIN(app.size(),7) );
     483           0 :     history[7] = ':';
     484             : 
     485           0 :     memcpy( history + 8, message.c_str(), MY_MIN(message.size(),56) );
     486           0 :     memcpy( history + 64, current_time, 16 );
     487             : 
     488           0 :     std::vector<std::string> history_entries = GetHistoryEntries();
     489             : 
     490             :     // coverity[string_null]
     491           0 :     history_entries.insert( history_entries.begin(), history );
     492           0 :     history_entries.resize(8);
     493             : 
     494           0 :     SetHistoryEntries( history_entries );
     495           0 : }
     496             : 
     497             : /************************************************************************/
     498             : /*                            GetChanInfo()                             */
     499             : /************************************************************************/
     500           7 : void CPCIDSKChannel::GetChanInfo( std::string &filename, uint64 &image_offset,
     501             :                                   uint64 &pixel_offset, uint64 &line_offset,
     502             :                                   bool &little_endian ) const
     503             : 
     504             : {
     505           7 :     image_offset = 0;
     506           7 :     pixel_offset = 0;
     507           7 :     line_offset = 0;
     508           7 :     little_endian = true;
     509           7 :     filename = "";
     510           7 : }
     511             : 
     512             : /************************************************************************/
     513             : /*                            SetChanInfo()                             */
     514             : /************************************************************************/
     515             : 
     516           0 : void CPCIDSKChannel::SetChanInfo( CPL_UNUSED std::string filename,
     517             :                                   CPL_UNUSED uint64 image_offset,
     518             :                                   CPL_UNUSED uint64 pixel_offset,
     519             :                                   CPL_UNUSED uint64 line_offset,
     520             :                                   CPL_UNUSED bool little_endian )
     521             : {
     522           0 :     return ThrowPCIDSKException( "Attempt to SetChanInfo() on a channel that is not FILE interleaved." );
     523             : }
     524             : 
     525             : /************************************************************************/
     526             : /*                            GetEChanInfo()                            */
     527             : /************************************************************************/
     528           0 : void CPCIDSKChannel::GetEChanInfo( std::string &filename, int &echannel,
     529             :                                    int &exoff, int &eyoff,
     530             :                                    int &exsize, int &eysize ) const
     531             : 
     532             : {
     533           0 :     echannel = 0;
     534           0 :     exoff = 0;
     535           0 :     eyoff = 0;
     536           0 :     exsize = 0;
     537           0 :     eysize = 0;
     538           0 :     filename = "";
     539           0 : }
     540             : 
     541             : /************************************************************************/
     542             : /*                            SetEChanInfo()                            */
     543             : /************************************************************************/
     544             : 
     545           0 : void CPCIDSKChannel::SetEChanInfo( CPL_UNUSED std::string filename,
     546             :                                    CPL_UNUSED int echannel,
     547             :                                    CPL_UNUSED int exoff,
     548             :                                    CPL_UNUSED int eyoff,
     549             :                                    CPL_UNUSED int exsize,
     550             :                                    CPL_UNUSED int eysize )
     551             : {
     552           0 :     return ThrowPCIDSKException( "Attempt to SetEChanInfo() on a channel that is not FILE interleaved." );
     553             : }

Generated by: LCOV version 1.14