LCOV - code coverage report
Current view: top level - frmts/pcidsk/sdk/channel - cpcidskchannel.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 121 197 61.4 %
Date: 2025-12-13 23:48:27 Functions: 15 27 55.6 %

          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             : /*                           SortOverviews()                            */
     127             : /************************************************************************/
     128             : 
     129             : // recent libc++ std::sort() involve unsigned integer overflow in some
     130             : // situation
     131             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
     132         233 : static void SortOverviews(std::vector<std::string>& keys)
     133             : {
     134         233 :     std::sort(keys.begin(), keys.end(),
     135          10 :               [] (const std::string &first,
     136             :                              const std::string &second)
     137             :                 {
     138          10 :                     if( !STARTS_WITH(first.c_str(), "_Overview_") ||
     139           0 :                         !STARTS_WITH(second.c_str(), "_Overview_") )
     140             :                     {
     141          10 :                         return false;
     142             :                     }
     143           0 :                     int nFirst = atoi(first.c_str() + 10);
     144           0 :                     int nSecond = atoi(second.c_str() + 10);
     145           0 :                     return nFirst < nSecond;
     146             :                 });
     147         233 : }
     148             : 
     149             : /************************************************************************/
     150             : /*                       EstablishOverviewInfo()                        */
     151             : /************************************************************************/
     152        1588 : void CPCIDSKChannel::EstablishOverviewInfo() const
     153             : 
     154             : {
     155        1588 :     if( overviews_initialized )
     156        1355 :         return;
     157             : 
     158         233 :     overviews_initialized = true;
     159             : 
     160         466 :     std::vector<std::string> keys = GetMetadataKeys();
     161         233 :     SortOverviews(keys);
     162             :     size_t i;
     163             : 
     164         250 :     for( i = 0; i < keys.size(); i++ )
     165             :     {
     166          17 :         if( !STARTS_WITH(keys[i].c_str(), "_Overview_") )
     167           7 :             continue;
     168             : 
     169          10 :         overview_infos.push_back( GetMetadataValue( keys[i] ) );
     170          10 :         overview_bands.push_back( nullptr );
     171          10 :         overview_decimations.push_back( atoi(keys[i].c_str()+10) );
     172             :     }
     173             : }
     174             : 
     175             : /************************************************************************/
     176             : /*                       UpdateOverviewInfo()                           */
     177             : /************************************************************************/
     178             : /** Update the in-memory information for an overview.
     179             :   * This method will add overview information to the in-memory arrays
     180             :   *
     181             :   * @param  pszOverviewMDValue  Overview value
     182             :   *
     183             :   * @param  nFactor             Overview factor i.e. 2, 4, etc
     184             :   */
     185           1 : void CPCIDSKChannel::UpdateOverviewInfo(const char *pszOverviewMDValue,
     186             :                                         int nFactor)
     187             : {
     188           1 :     overview_infos.push_back( pszOverviewMDValue );
     189           1 :     overview_bands.push_back( nullptr );
     190           1 :     overview_decimations.push_back( nFactor );
     191           1 : }
     192             : 
     193             : /************************************************************************/
     194             : /*                           GetBlockCount()                            */
     195             : /************************************************************************/
     196             : 
     197           0 : int CPCIDSKChannel::GetBlockCount() const
     198             : 
     199             : {
     200             :     // We deliberately call GetBlockWidth() and GetWidth() to trigger
     201             :     // computation of the values for tiled layers.  At some point it would
     202             :     // be good to cache the block count as this computation is a bit expensive
     203             : 
     204           0 :     int x_block_count = DIV_ROUND_UP(GetWidth(), GetBlockWidth());
     205           0 :     int y_block_count = DIV_ROUND_UP(GetHeight(), GetBlockHeight());
     206             : 
     207           0 :     return x_block_count * y_block_count;
     208             : }
     209             : 
     210             : /************************************************************************/
     211             : /*                          GetOverviewCount()                          */
     212             : /************************************************************************/
     213             : 
     214         911 : int CPCIDSKChannel::GetOverviewCount()
     215             : 
     216             : {
     217         911 :     EstablishOverviewInfo();
     218             : 
     219         911 :     return static_cast<int>(overview_infos.size());
     220             : }
     221             : 
     222             : /************************************************************************/
     223             : /*                            GetOverview()                             */
     224             : /************************************************************************/
     225             : 
     226          11 : PCIDSKChannel *CPCIDSKChannel::GetOverview( int overview_index )
     227             : 
     228             : {
     229          11 :     EstablishOverviewInfo();
     230             : 
     231          11 :     if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
     232           0 :         return (PCIDSKChannel*)ThrowPCIDSKExceptionPtr( "Non existent overview (%d) requested.",
     233           0 :                               overview_index );
     234             : 
     235          11 :     if( overview_bands[overview_index] == nullptr )
     236             :     {
     237          22 :         PCIDSKBuffer image_header(1024), file_header(1024);
     238             :         char  pseudo_filename[65];
     239             : 
     240          11 :         snprintf( pseudo_filename, sizeof(pseudo_filename), "/SIS=%d",
     241          11 :                  atoi(overview_infos[overview_index].c_str()) );
     242             : 
     243          11 :         image_header.Put( pseudo_filename, 64, 64 );
     244             : 
     245          11 :         overview_bands[overview_index] =
     246             :             new CTiledChannel( image_header, 0, file_header, -1, file,
     247          11 :                                CHN_UNKNOWN );
     248             :     }
     249             : 
     250          11 :     return overview_bands[overview_index];
     251             : }
     252             : 
     253             : /************************************************************************/
     254             : /*                          IsOverviewValid()                           */
     255             : /************************************************************************/
     256             : 
     257           0 : bool CPCIDSKChannel::IsOverviewValid( int overview_index )
     258             : 
     259             : {
     260           0 :     EstablishOverviewInfo();
     261             : 
     262           0 :     if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
     263           0 :         return ThrowPCIDSKException(0, "Non existent overview (%d) requested.",
     264           0 :                               overview_index ) != 0;
     265             : 
     266           0 :     int sis_id, validity=0;
     267             : 
     268           0 :     sscanf( overview_infos[overview_index].c_str(), "%d %d",
     269             :             &sis_id, &validity );
     270             : 
     271           0 :     return validity != 0;
     272             : }
     273             : 
     274             : /************************************************************************/
     275             : /*                       GetOverviewResampling()                        */
     276             : /************************************************************************/
     277             : 
     278           0 : std::string CPCIDSKChannel::GetOverviewResampling( int overview_index )
     279             : 
     280             : {
     281           0 :     EstablishOverviewInfo();
     282             : 
     283           0 :     if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
     284             :     {
     285           0 :         ThrowPCIDSKException( "Non existent overview (%d) requested.",
     286             :                               overview_index );
     287           0 :         return "";
     288             :     }
     289             : 
     290           0 :     int sis_id, validity=0;
     291             :     char resampling[17];
     292             : 
     293           0 :     sscanf( overview_infos[overview_index].c_str(), "%d %d %16s",
     294             :             &sis_id, &validity, &(resampling[0]) );
     295             : 
     296           0 :     return resampling;
     297             : }
     298             : 
     299             : /************************************************************************/
     300             : /*                        SetOverviewValidity()                         */
     301             : /************************************************************************/
     302             : 
     303          21 : void CPCIDSKChannel::SetOverviewValidity( int overview_index,
     304             :                                           bool new_validity )
     305             : 
     306             : {
     307          21 :     EstablishOverviewInfo();
     308             : 
     309          21 :     if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
     310           0 :         return ThrowPCIDSKException( "Non existent overview (%d) requested.",
     311          19 :                               overview_index );
     312             : 
     313          21 :     int sis_id, validity=0;
     314             :     char resampling[17];
     315             : 
     316          21 :     sscanf( overview_infos[overview_index].c_str(), "%d %d %16s",
     317             :             &sis_id, &validity, &(resampling[0]) );
     318             : 
     319             :     // are we already set to this value?
     320          21 :     if( new_validity == (validity != 0) )
     321          19 :         return;
     322             : 
     323             :     char new_info[48];
     324             : 
     325           2 :     snprintf( new_info, sizeof(new_info), "%d %d %s",
     326             :              sis_id, (new_validity ? 1 : 0 ), resampling );
     327             : 
     328           2 :     overview_infos[overview_index] = new_info;
     329             : 
     330             :     // write back to metadata.
     331             :     char key[20];
     332           2 :     snprintf( key, sizeof(key), "_Overview_%d", overview_decimations[overview_index] );
     333             : 
     334           2 :     SetMetadataValue( key, new_info );
     335             : }
     336             : 
     337             : /************************************************************************/
     338             : /*                        InvalidateOverviews()                         */
     339             : /*                                                                      */
     340             : /*      Whenever a write is done on this band, we will invalidate       */
     341             : /*      any previously valid overviews.                                 */
     342             : /************************************************************************/
     343             : 
     344         645 : void CPCIDSKChannel::InvalidateOverviews()
     345             : 
     346             : {
     347         645 :     EstablishOverviewInfo();
     348             : 
     349         665 :     for( int i = 0; i < GetOverviewCount(); i++ )
     350          20 :         SetOverviewValidity( i, false );
     351         645 : }
     352             : 
     353             : /************************************************************************/
     354             : /*                  GetOverviewLevelMapping()                           */
     355             : /************************************************************************/
     356             : 
     357           0 : std::vector<int> CPCIDSKChannel::GetOverviewLevelMapping() const
     358             : {
     359           0 :     EstablishOverviewInfo();
     360             : 
     361           0 :     return overview_decimations;
     362             : }
     363             : 
     364             : /************************************************************************/
     365             : /*                              GetFilename()                           */
     366             : /************************************************************************/
     367           0 : std::string CPCIDSKChannel::GetFilename() const
     368             : {
     369           0 :     return file->GetFilename();
     370             : }
     371             : 
     372             : /************************************************************************/
     373             : /*                           GetDescription()                           */
     374             : /************************************************************************/
     375             : 
     376         249 : std::string CPCIDSKChannel::GetDescription()
     377             : 
     378             : {
     379         249 :     if( ih_offset == 0 )
     380           0 :         return "";
     381             : 
     382         498 :     PCIDSKBuffer ih_1(64);
     383         498 :     std::string ret;
     384             : 
     385         249 :     file->ReadFromFile( ih_1.buffer, ih_offset, 64 );
     386         249 :     ih_1.Get(0,64,ret);
     387             : 
     388         249 :     return ret;
     389             : }
     390             : 
     391             : /************************************************************************/
     392             : /*                           SetDescription()                           */
     393             : /************************************************************************/
     394             : 
     395           3 : void CPCIDSKChannel::SetDescription( const std::string &description )
     396             : 
     397             : {
     398           3 :     if( ih_offset == 0 )
     399           0 :         return ThrowPCIDSKException( "Description cannot be set on overviews." );
     400             : 
     401           6 :     PCIDSKBuffer ih_1(64);
     402           3 :     ih_1.Put( description.c_str(), 0, 64 );
     403           3 :     file->WriteToFile( ih_1.buffer, ih_offset, 64 );
     404             : }
     405             : 
     406             : /************************************************************************/
     407             : /*                            LoadHistory()                             */
     408             : /************************************************************************/
     409             : 
     410         242 : void CPCIDSKChannel::LoadHistory( const PCIDSKBuffer &image_header )
     411             : 
     412             : {
     413             :     // Read the history from the image header. PCIDSK supports
     414             :     // 8 history entries per channel.
     415             : 
     416         484 :     std::string hist_msg;
     417         242 :     history_.clear();
     418        2178 :     for (unsigned int i = 0; i < 8; i++)
     419             :     {
     420        1936 :         image_header.Get(384 + i * 80, 80, hist_msg);
     421             : 
     422             :         // Some programs seem to push history records with a trailing '\0'
     423             :         // so do some extra processing to cleanup.  FUN records on segment
     424             :         // 3 of eltoro.pix are an example of this.
     425        1936 :         size_t size = hist_msg.size();
     426           0 :         while( size > 0
     427        1936 :                && (hist_msg[size-1] == ' ' || hist_msg[size-1] == '\0') )
     428           0 :             size--;
     429             : 
     430        1936 :         hist_msg.resize(size);
     431             : 
     432        1936 :         history_.push_back(hist_msg);
     433             :     }
     434         242 : }
     435             : 
     436             : /************************************************************************/
     437             : /*                         GetHistoryEntries()                          */
     438             : /************************************************************************/
     439             : 
     440           0 : std::vector<std::string> CPCIDSKChannel::GetHistoryEntries() const
     441             : {
     442           0 :     return history_;
     443             : }
     444             : 
     445             : /************************************************************************/
     446             : /*                         SetHistoryEntries()                          */
     447             : /************************************************************************/
     448             : 
     449           0 : void CPCIDSKChannel::SetHistoryEntries(const std::vector<std::string> &entries)
     450             : 
     451             : {
     452           0 :     if( ih_offset == 0 )
     453           0 :         return ThrowPCIDSKException( "Attempt to update history on a raster that is not\na conventional band with an image header." );
     454             : 
     455           0 :     PCIDSKBuffer image_header(1024);
     456             : 
     457           0 :     file->ReadFromFile( image_header.buffer, ih_offset, 1024 );
     458             : 
     459           0 :     for( unsigned int i = 0; i < 8; i++ )
     460             :     {
     461           0 :         const char *msg = "";
     462           0 :         if( entries.size() > i )
     463           0 :             msg = entries[i].c_str();
     464             : 
     465           0 :         image_header.Put( msg, 384 + i * 80, 80 );
     466             :     }
     467             : 
     468           0 :     file->WriteToFile( image_header.buffer, ih_offset, 1024 );
     469             : 
     470             :     // Force reloading of history_
     471           0 :     LoadHistory( image_header );
     472             : }
     473             : 
     474             : /************************************************************************/
     475             : /*                            PushHistory()                             */
     476             : /************************************************************************/
     477             : 
     478           0 : void CPCIDSKChannel::PushHistory( const std::string &app,
     479             :                                   const std::string &message )
     480             : 
     481             : {
     482             : #define MY_MIN(a,b)      ((a<b) ? a : b)
     483             : 
     484             :     char current_time[17];
     485             :     char history[81];
     486             : 
     487           0 :     GetCurrentDateTime( current_time );
     488             : 
     489           0 :     memset( history, ' ', 80 );
     490           0 :     history[80] = '\0';
     491             : 
     492           0 :     memcpy( history + 0, app.c_str(), MY_MIN(app.size(),7) );
     493           0 :     history[7] = ':';
     494             : 
     495           0 :     memcpy( history + 8, message.c_str(), MY_MIN(message.size(),56) );
     496           0 :     memcpy( history + 64, current_time, 16 );
     497             : 
     498           0 :     std::vector<std::string> history_entries = GetHistoryEntries();
     499             : 
     500             :     // coverity[string_null]
     501           0 :     history_entries.insert( history_entries.begin(), history );
     502           0 :     history_entries.resize(8);
     503             : 
     504           0 :     SetHistoryEntries( history_entries );
     505           0 : }
     506             : 
     507             : /************************************************************************/
     508             : /*                            GetChanInfo()                             */
     509             : /************************************************************************/
     510           7 : void CPCIDSKChannel::GetChanInfo( std::string &filename, uint64 &image_offset,
     511             :                                   uint64 &pixel_offset, uint64 &line_offset,
     512             :                                   bool &little_endian ) const
     513             : 
     514             : {
     515           7 :     image_offset = 0;
     516           7 :     pixel_offset = 0;
     517           7 :     line_offset = 0;
     518           7 :     little_endian = true;
     519           7 :     filename = "";
     520           7 : }
     521             : 
     522             : /************************************************************************/
     523             : /*                            SetChanInfo()                             */
     524             : /************************************************************************/
     525             : 
     526           0 : void CPCIDSKChannel::SetChanInfo( CPL_UNUSED std::string filename,
     527             :                                   CPL_UNUSED uint64 image_offset,
     528             :                                   CPL_UNUSED uint64 pixel_offset,
     529             :                                   CPL_UNUSED uint64 line_offset,
     530             :                                   CPL_UNUSED bool little_endian )
     531             : {
     532           0 :     return ThrowPCIDSKException( "Attempt to SetChanInfo() on a channel that is not FILE interleaved." );
     533             : }
     534             : 
     535             : /************************************************************************/
     536             : /*                            GetEChanInfo()                            */
     537             : /************************************************************************/
     538           0 : void CPCIDSKChannel::GetEChanInfo( std::string &filename, int &echannel,
     539             :                                    int &exoff, int &eyoff,
     540             :                                    int &exsize, int &eysize ) const
     541             : 
     542             : {
     543           0 :     echannel = 0;
     544           0 :     exoff = 0;
     545           0 :     eyoff = 0;
     546           0 :     exsize = 0;
     547           0 :     eysize = 0;
     548           0 :     filename = "";
     549           0 : }
     550             : 
     551             : /************************************************************************/
     552             : /*                            SetEChanInfo()                            */
     553             : /************************************************************************/
     554             : 
     555           0 : void CPCIDSKChannel::SetEChanInfo( CPL_UNUSED std::string filename,
     556             :                                    CPL_UNUSED int echannel,
     557             :                                    CPL_UNUSED int exoff,
     558             :                                    CPL_UNUSED int eyoff,
     559             :                                    CPL_UNUSED int exsize,
     560             :                                    CPL_UNUSED int eysize )
     561             : {
     562           0 :     return ThrowPCIDSKException( "Attempt to SetEChanInfo() on a channel that is not FILE interleaved." );
     563             : }

Generated by: LCOV version 1.14