LCOV - code coverage report
Current view: directory - frmts/pcidsk/sdk/core - cpcidskfile.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 388 205 52.8 %
Date: 2010-01-09 Functions: 24 16 66.7 %

       1                 : /******************************************************************************
       2                 :  *
       3                 :  * Purpose:  Implementation of the CPCIDSKFile class.
       4                 :  * 
       5                 :  ******************************************************************************
       6                 :  * Copyright (c) 2009
       7                 :  * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada
       8                 :  *
       9                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      10                 :  * copy of this software and associated documentation files (the "Software"),
      11                 :  * to deal in the Software without restriction, including without limitation
      12                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      13                 :  * and/or sell copies of the Software, and to permit persons to whom the
      14                 :  * Software is furnished to do so, subject to the following conditions:
      15                 :  *
      16                 :  * The above copyright notice and this permission notice shall be included
      17                 :  * in all copies or substantial portions of the Software.
      18                 :  *
      19                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      20                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      21                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      22                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      23                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      24                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      25                 :  * DEALINGS IN THE SOFTWARE.
      26                 :  ****************************************************************************/
      27                 : #include "pcidsk_file.h"
      28                 : #include "pcidsk_exception.h"
      29                 : #include "pcidsk_channel.h"
      30                 : #include "pcidsk_segment.h"
      31                 : #include "core/mutexholder.h"
      32                 : #include "core/pcidsk_utils.h"
      33                 : #include "core/cpcidskfile.h"
      34                 : 
      35                 : // Channel types
      36                 : #include "channel/cbandinterleavedchannel.h"
      37                 : #include "channel/cpixelinterleavedchannel.h"
      38                 : #include "channel/ctiledchannel.h"
      39                 : 
      40                 : // Segment types
      41                 : #include "segment/cpcidskgeoref.h"
      42                 : #include "segment/cpcidskpct.h"
      43                 : #include "segment/cpcidskvectorsegment.h"
      44                 : #include "segment/metadatasegment.h"
      45                 : #include "segment/sysblockmap.h"
      46                 : #include "segment/cpcidskrpcmodel.h"
      47                 : 
      48                 : #include <cassert>
      49                 : #include <cstdlib>
      50                 : #include <cstring>
      51                 : #include <cstdio>
      52                 : #include <string>
      53                 : 
      54                 : #include <iostream>
      55                 : 
      56                 : using namespace PCIDSK;
      57                 : 
      58                 : /************************************************************************/
      59                 : /*                             CPCIDSKFile()                             */
      60                 : /************************************************************************/
      61                 : 
      62              78 : CPCIDSKFile::CPCIDSKFile()
      63                 : 
      64                 : {
      65              78 :     io_handle = NULL;
      66              78 :     io_mutex = NULL;
      67              78 :     updatable = false;
      68                 : 
      69                 : /* -------------------------------------------------------------------- */
      70                 : /*      Initialize the metadata object, but do not try to load till     */
      71                 : /*      needed.                                                         */
      72                 : /* -------------------------------------------------------------------- */
      73              78 :     metadata.Initialize( this, "FIL", 0 );
      74              78 : }
      75                 : 
      76                 : /************************************************************************/
      77                 : /*                            ~CPCIDSKFile()                             */
      78                 : /************************************************************************/
      79                 : 
      80             156 : CPCIDSKFile::~CPCIDSKFile()
      81                 : 
      82                 : {
      83                 : /* -------------------------------------------------------------------- */
      84                 : /*      Cleanup last line caching stuff for pixel interleaved data.     */
      85                 : /* -------------------------------------------------------------------- */
      86              78 :     if( last_block_data != NULL )
      87                 :     {
      88               0 :         FlushBlock();
      89               0 :         last_block_index = -1;
      90               0 :         free( last_block_data );
      91               0 :         last_block_data = NULL;
      92               0 :         delete last_block_mutex;
      93                 :     }
      94                 : 
      95                 : /* -------------------------------------------------------------------- */
      96                 : /*      Cleanup channels and segments.                                  */
      97                 : /* -------------------------------------------------------------------- */
      98                 :     size_t i;
      99             202 :     for( i = 0; i < channels.size(); i++ )
     100                 :     {
     101             124 :         delete channels[i];
     102             124 :         channels[i] = NULL;
     103                 :     }
     104                 :     
     105           80028 :     for( i = 0; i < segments.size(); i++ )
     106                 :     {
     107           79950 :         delete segments[i];
     108           79950 :         segments[i] = NULL;
     109                 :     }
     110                 :     
     111                 : /* -------------------------------------------------------------------- */
     112                 : /*      Close and cleanup IO stuff.                                     */
     113                 : /* -------------------------------------------------------------------- */
     114                 :     {
     115              78 :         MutexHolder oHolder( io_mutex );
     116                 : 
     117              78 :         if( io_handle )
     118                 :         {
     119              78 :             interfaces.io->Close( io_handle );
     120              78 :             io_handle = NULL;
     121              78 :         }
     122                 :     }
     123                 : 
     124                 :     size_t i_file;
     125                 : 
     126              78 :     for( i_file=0; i_file < file_list.size(); i_file++ )
     127                 :     {
     128               0 :         delete file_list[i_file].io_mutex;
     129               0 :         file_list[i_file].io_mutex = NULL;
     130                 : 
     131               0 :         interfaces.io->Close( file_list[i_file].io_handle );
     132               0 :         file_list[i_file].io_handle = NULL;
     133                 :     }
     134                 : 
     135              78 :     delete io_mutex;
     136             156 : }
     137                 : 
     138                 : /************************************************************************/
     139                 : /*                             GetChannel()                             */
     140                 : /************************************************************************/
     141                 : 
     142              69 : PCIDSKChannel *CPCIDSKFile::GetChannel( int band )
     143                 : 
     144                 : {
     145              69 :     if( band < 1 || band > channel_count )
     146                 :         ThrowPCIDSKException( "Out of range band (%d) requested.", 
     147               0 :                               band );
     148                 : 
     149              69 :     return channels[band-1];
     150                 : }
     151                 : 
     152                 : /************************************************************************/
     153                 : /*                             GetSegment()                             */
     154                 : /************************************************************************/
     155                 : 
     156             115 : PCIDSK::PCIDSKSegment *CPCIDSKFile::GetSegment( int segment )
     157                 : 
     158                 : {
     159                 : /* -------------------------------------------------------------------- */
     160                 : /*      Is this a valid segment?                                        */
     161                 : /* -------------------------------------------------------------------- */
     162             115 :     if( segment < 1 || segment > segment_count )
     163               0 :         return NULL;
     164                 : 
     165             115 :     const char *segment_pointer = segment_pointers.buffer + (segment-1) * 32;
     166                 : 
     167             115 :     if( segment_pointer[0] != 'A' && segment_pointer[0] != 'L' )
     168               0 :         return NULL;
     169                 : 
     170                 : /* -------------------------------------------------------------------- */
     171                 : /*      Do we already have a corresponding object?                      */
     172                 : /* -------------------------------------------------------------------- */
     173             115 :     if( segments[segment] != NULL )
     174              30 :         return segments[segment];
     175                 : 
     176                 : /* -------------------------------------------------------------------- */
     177                 : /*      Instantiate per the type.                                       */
     178                 : /* -------------------------------------------------------------------- */
     179              85 :     int segment_type = segment_pointers.GetInt((segment-1)*32+1,3);
     180              85 :     PCIDSKSegment *segobj = NULL;
     181                 : 
     182              85 :     switch( segment_type )
     183                 :     {
     184                 :       case SEG_GEO:
     185              52 :         segobj = new CPCIDSKGeoref( this, segment, segment_pointer );
     186              52 :         break;
     187                 : 
     188                 :       case SEG_PCT:
     189               2 :         segobj = new CPCIDSK_PCT( this, segment, segment_pointer );
     190               2 :         break;
     191                 : 
     192                 :       case SEG_VEC:
     193               0 :         segobj = new CPCIDSKVectorSegment( this, segment, segment_pointer );
     194               0 :         break;
     195                 : 
     196                 :       case SEG_SYS:
     197              31 :         if( strncmp(segment_pointer + 4, "SysBMDir",8) == 0 )
     198               3 :             segobj = new SysBlockMap( this, segment, segment_pointer );
     199              28 :         else if( strncmp(segment_pointer + 4, "METADATA",8) == 0 )
     200              25 :             segobj = new MetadataSegment( this, segment, segment_pointer );
     201                 :         else
     202               3 :             segobj = new CPCIDSKSegment( this, segment, segment_pointer );
     203                 : 
     204              31 :         break;
     205                 :     
     206                 :       case SEG_BIN:
     207               0 :         if (!strncmp(segment_pointer + 4, "RFMODEL ", 8))
     208               0 :             segobj = new CPCIDSKRPCModelSegment( this, segment, segment_pointer );
     209                 :         break;
     210                 :         
     211                 :     }
     212                 :     
     213              85 :     if (segobj == NULL)
     214               0 :         segobj = new CPCIDSKSegment( this, segment, segment_pointer );
     215                 :         
     216              85 :     segments[segment] = segobj;
     217                 : 
     218              85 :     return segobj;
     219                 : }
     220                 : 
     221                 : /************************************************************************/
     222                 : /*                             GetSegment()                             */
     223                 : /*                                                                      */
     224                 : /*      Find segment by type/name.                                      */
     225                 : /************************************************************************/
     226                 : 
     227             146 : PCIDSK::PCIDSKSegment *CPCIDSKFile::GetSegment( int type, std::string name, 
     228                 :                                                 int previous )
     229                 : 
     230                 : {
     231                 :     int  i;
     232                 :     char type_str[4];
     233                 : 
     234             146 :     name += "        "; // white space pad name.
     235                 : 
     236             146 :     sprintf( type_str, "%03d", type );
     237                 : 
     238          149606 :     for( i = previous; i < segment_count; i++ )
     239                 :     {
     240          149498 :         if( type != SEG_UNKNOWN 
     241                 :             && strncmp(segment_pointers.buffer+i*32+1,type_str,3) != 0 )
     242          149452 :             continue;
     243                 : 
     244              46 :         if( name != "        " 
     245                 :             && strncmp(segment_pointers.buffer+i*32+4,name.c_str(),8) != 0 )
     246               8 :             continue;
     247                 : 
     248              38 :         return GetSegment(i+1);
     249                 :     }
     250                 : 
     251             108 :     return NULL;
     252                 : }
     253                 : 
     254                 : 
     255                 : /************************************************************************/
     256                 : /*                            GetSegments()                             */
     257                 : /************************************************************************/
     258                 : 
     259               0 : std::vector<PCIDSK::PCIDSKSegment *> CPCIDSKFile::GetSegments()
     260                 : 
     261                 : {
     262               0 :     PCIDSK::ThrowPCIDSKException( "Objects list access not implemented yet." );
     263                 : 
     264               0 :     std::vector<PCIDSK::PCIDSKSegment *> list;
     265                 :     return list;
     266                 : }
     267                 : 
     268                 : /************************************************************************/
     269                 : /*                        InitializeFromHeader()                        */
     270                 : /************************************************************************/
     271                 : 
     272              78 : void CPCIDSKFile::InitializeFromHeader()
     273                 : 
     274                 : {
     275                 : /* -------------------------------------------------------------------- */
     276                 : /*      Process the file header.                                        */
     277                 : /* -------------------------------------------------------------------- */
     278              78 :     PCIDSKBuffer fh(512);
     279                 : 
     280              78 :     ReadFromFile( fh.buffer, 0, 512 );
     281                 : 
     282              78 :     width = atoi(fh.Get(384,8));
     283              78 :     height = atoi(fh.Get(392,8));
     284              78 :     channel_count = atoi(fh.Get(376,8));
     285              78 :     file_size = fh.GetUInt64(16,16);
     286                 : 
     287              78 :     uint64 ih_start_block = atouint64(fh.Get(336,16));
     288              78 :     uint64 image_start_block = atouint64(fh.Get(304,16));
     289              78 :     fh.Get(360,8,interleaving);
     290                 : 
     291              78 :     uint64 image_offset = (image_start_block-1) * 512;
     292                 : 
     293              78 :     block_size = 0;
     294              78 :     last_block_index = -1;
     295              78 :     last_block_dirty = 0;
     296              78 :     last_block_data = NULL;
     297              78 :     last_block_mutex = NULL;
     298                 : 
     299                 : /* -------------------------------------------------------------------- */
     300                 : /*      Load the segment pointers into a PCIDSKBuffer.  For now we      */
     301                 : /*      try to avoid doing too much other processing on them.           */
     302                 : /* -------------------------------------------------------------------- */
     303              78 :     int segment_block_count = atoi(fh.Get(456,8));
     304                 :     
     305              78 :     segment_count = (segment_block_count * 512) / 32;
     306              78 :     segment_pointers.SetSize( segment_block_count * 512 );
     307              78 :     segment_pointers_offset = atouint64(fh.Get(440,16)) * 512 - 512;
     308                 :     ReadFromFile( segment_pointers.buffer, segment_pointers_offset,
     309              78 :                   segment_block_count * 512 );
     310                 : 
     311              78 :     segments.resize( segment_count + 1 );
     312                 : 
     313                 : /* -------------------------------------------------------------------- */
     314                 : /*      Get the number of each channel type - only used for some        */
     315                 : /*      interleaving cases.                                             */
     316                 : /* -------------------------------------------------------------------- */
     317              78 :     int count_8u = atoi(fh.Get(464,4));
     318              78 :     int count_16s = atoi(fh.Get(468,4));
     319              78 :     int count_16u = atoi(fh.Get(472,4));
     320              78 :     int count_32r = atoi(fh.Get(476,4));
     321                 : 
     322                 : /* -------------------------------------------------------------------- */
     323                 : /*      for pixel interleaved files we need to compute the length of    */
     324                 : /*      a scanline padded out to a 512 byte boundary.                   */
     325                 : /* -------------------------------------------------------------------- */
     326              78 :     if( interleaving == "PIXEL" )
     327                 :     {
     328               0 :         first_line_offset = image_offset;
     329               0 :         pixel_group_size = count_8u + count_16s*2 + count_16u*2 + count_32r*4;
     330                 :         
     331               0 :         block_size = pixel_group_size * width;
     332               0 :         if( block_size % 512 != 0 )
     333               0 :             block_size += 512 - (block_size % 512);
     334                 : 
     335               0 :         last_block_data = malloc((size_t) block_size);
     336               0 :         if( last_block_data == NULL )
     337                 :             ThrowPCIDSKException( "Allocating %d bytes for scanline buffer failed.", 
     338               0 :                                        (int) block_size );
     339                 : 
     340               0 :         last_block_mutex = interfaces.CreateMutex();
     341               0 :         image_offset = 0;
     342                 :     }
     343                 : 
     344                 : /* -------------------------------------------------------------------- */
     345                 : /*      Initialize the list of channels.                                */
     346                 : /* -------------------------------------------------------------------- */
     347                 :     int channelnum;
     348                 : 
     349             202 :     for( channelnum = 1; channelnum <= channel_count; channelnum++ )
     350                 :     {
     351             124 :         PCIDSKBuffer ih(1024);
     352             124 :         PCIDSKChannel *channel = NULL;
     353                 :         
     354                 :         ReadFromFile( ih.buffer, 
     355                 :                       (ih_start_block-1)*512 + (channelnum-1)*1024, 
     356             124 :                       1024);
     357                 : 
     358                 :         // fetch the filename, if there is one.
     359             124 :         std::string filename;
     360             124 :         ih.Get(64,64,filename);
     361                 : 
     362                 :         // work out channel type from header
     363                 :         eChanType pixel_type;
     364             124 :         const char *pixel_type_string = ih.Get( 160, 8 );
     365                 :     
     366             124 :         if( strcmp(pixel_type_string,"8U      ") == 0 )
     367             100 :             pixel_type = CHN_8U;
     368              24 :         else if( strcmp(pixel_type_string,"16S     ") == 0 )
     369               4 :             pixel_type = CHN_16S;
     370              20 :         else if( strcmp(pixel_type_string,"16U     ") == 0 )
     371              16 :             pixel_type = CHN_16U;
     372               4 :         else if( strcmp(pixel_type_string,"32R     ") == 0 )
     373               4 :             pixel_type = CHN_32R;
     374                 :         else
     375               0 :             pixel_type = CHN_UNKNOWN; // should we throw an exception?  
     376                 : 
     377                 :         // if we didn't get channel type in header, work out from counts (old)
     378                 : 
     379             124 :         if( channelnum <= count_8u )
     380             100 :             pixel_type = CHN_8U;
     381              24 :         else if( channelnum <= count_8u + count_16s )
     382               4 :             pixel_type = CHN_16S;
     383              20 :         else if( channelnum <= count_8u + count_16s + count_16u )
     384              16 :             pixel_type = CHN_16U;
     385                 :         else 
     386               4 :             pixel_type = CHN_32R;
     387                 :             
     388             124 :         if( interleaving == "BAND" )
     389                 :         {
     390                 :             channel = new CBandInterleavedChannel( ih, fh, channelnum, this,
     391             124 :                                                    image_offset, pixel_type );
     392                 : 
     393                 :             
     394             124 :             image_offset += DataTypeSize(channel->GetType())
     395             372 :                 * width * height;
     396                 :         }
     397                 : 
     398               0 :         else if( interleaving == "PIXEL" )
     399                 :         {
     400                 :             channel = new CPixelInterleavedChannel( ih, fh, channelnum, this,
     401                 :                                                     (int) image_offset, 
     402               0 :                                                     pixel_type );
     403               0 :             image_offset += DataTypeSize(pixel_type);
     404                 :         }
     405                 : 
     406               0 :         else if( interleaving == "FILE" 
     407                 :                  && strncmp(filename.c_str(),"/SIS=",5) == 0 )
     408                 :         {
     409               0 :             channel = new CTiledChannel( ih, fh, channelnum, this, pixel_type );
     410                 :         }
     411                 : 
     412               0 :         else if( interleaving == "FILE" )
     413                 :         {
     414                 :             channel = new CBandInterleavedChannel( ih, fh, channelnum, this,
     415               0 :                                                    0, pixel_type );
     416                 :         }
     417                 : 
     418                 :         else
     419                 :             ThrowPCIDSKException( "Unsupported interleaving:%s", 
     420               0 :                                        interleaving.c_str() );
     421                 : 
     422             124 :         channels.push_back( channel );
     423              78 :     }
     424              78 : }
     425                 : 
     426                 : /************************************************************************/
     427                 : /*                            ReadFromFile()                            */
     428                 : /************************************************************************/
     429                 : 
     430             519 : void CPCIDSKFile::ReadFromFile( void *buffer, uint64 offset, uint64 size )
     431                 : 
     432                 : {
     433             519 :     MutexHolder oHolder( io_mutex );
     434                 : 
     435             519 :     interfaces.io->Seek( io_handle, offset, SEEK_SET );
     436             519 :     if( interfaces.io->Read( buffer, 1, size, io_handle ) != size )
     437                 :         ThrowPCIDSKException( "PCIDSKFile:Failed to read %d bytes at %d.", 
     438               0 :                                    (int) size, (int) offset );
     439             519 : }
     440                 : 
     441                 : /************************************************************************/
     442                 : /*                            WriteToFile()                             */
     443                 : /************************************************************************/
     444                 : 
     445             363 : void CPCIDSKFile::WriteToFile( const void *buffer, uint64 offset, uint64 size )
     446                 : 
     447                 : {
     448             363 :     if( !GetUpdatable() )
     449               0 :         throw PCIDSKException( "File not open for update in WriteToFile()" );
     450                 : 
     451             363 :     MutexHolder oHolder( io_mutex );
     452                 : 
     453             363 :     interfaces.io->Seek( io_handle, offset, SEEK_SET );
     454             363 :     if( interfaces.io->Write( buffer, 1, size, io_handle ) != size )
     455                 :         ThrowPCIDSKException( "PCIDSKFile:Failed to write %d bytes at %d.",
     456               0 :                                    (int) size, (int) offset );
     457             363 : }
     458                 : 
     459                 : /************************************************************************/
     460                 : /*                          ReadAndLockBlock()                          */
     461                 : /************************************************************************/
     462                 : 
     463               0 : void *CPCIDSKFile::ReadAndLockBlock( int block_index, 
     464                 :                                      int win_xoff, int win_xsize )
     465                 : 
     466                 : {
     467               0 :     if( last_block_data == NULL )
     468               0 :         ThrowPCIDSKException( "ReadAndLockBlock() called on a file that is not pixel interleaved." );
     469                 : 
     470                 : /* -------------------------------------------------------------------- */
     471                 : /*      Default, and validate windowing.                                */
     472                 : /* -------------------------------------------------------------------- */
     473               0 :     if( win_xoff == -1 && win_xsize == -1 )
     474                 :     {
     475               0 :         win_xoff = 0;
     476               0 :         win_xsize = GetWidth();
     477                 :     }
     478                 : 
     479               0 :     if( win_xoff < 0 || win_xoff+win_xsize > GetWidth() )
     480                 :     {
     481                 :         ThrowPCIDSKException( "CPCIDSKFile::ReadAndLockBlock(): Illegal window - xoff=%d, xsize=%d", 
     482               0 :                                    win_xoff, win_xsize );
     483                 :     }
     484                 : 
     485               0 :     if( block_index == last_block_index 
     486                 :         && win_xoff == last_block_xoff
     487                 :         && win_xsize == last_block_xsize )
     488                 :     {
     489               0 :         last_block_mutex->Acquire();
     490               0 :         return last_block_data;
     491                 :     }
     492                 : 
     493                 : /* -------------------------------------------------------------------- */
     494                 : /*      Flush any dirty writable data.                                  */
     495                 : /* -------------------------------------------------------------------- */
     496               0 :     FlushBlock();
     497                 : 
     498                 : /* -------------------------------------------------------------------- */
     499                 : /*      Read the requested window.                                      */
     500                 : /* -------------------------------------------------------------------- */
     501               0 :     last_block_mutex->Acquire();
     502                 : 
     503                 :     ReadFromFile( last_block_data, 
     504                 :                   first_line_offset + block_index*block_size
     505                 :                   + win_xoff * pixel_group_size,
     506               0 :                   pixel_group_size * win_xsize );
     507               0 :     last_block_index = block_index;
     508               0 :     last_block_xoff = win_xoff;
     509               0 :     last_block_xsize = win_xsize;
     510                 :     
     511               0 :     return last_block_data;
     512                 : }
     513                 : 
     514                 : /************************************************************************/
     515                 : /*                            UnlockBlock()                             */
     516                 : /************************************************************************/
     517                 : 
     518               0 : void CPCIDSKFile::UnlockBlock( bool mark_dirty )
     519                 : 
     520                 : {
     521               0 :     if( last_block_mutex == NULL )
     522               0 :         return;
     523                 : 
     524               0 :     last_block_dirty |= mark_dirty;
     525               0 :     last_block_mutex->Release();
     526                 : }
     527                 : 
     528                 : /************************************************************************/
     529                 : /*                             WriteBlock()                             */
     530                 : /************************************************************************/
     531                 : 
     532               0 : void CPCIDSKFile::WriteBlock( int block_index, void *buffer )
     533                 : 
     534                 : {
     535               0 :     if( !GetUpdatable() )
     536               0 :         throw PCIDSKException( "File not open for update in WriteBlock()" );
     537                 : 
     538               0 :     if( last_block_data == NULL )
     539               0 :         ThrowPCIDSKException( "WriteBlock() called on a file that is not pixel interleaved." );
     540                 : 
     541                 :     WriteToFile( buffer,
     542                 :                  first_line_offset + block_index*block_size,
     543               0 :                  block_size );
     544               0 : }
     545                 : 
     546                 : /************************************************************************/
     547                 : /*                             FlushBlock()                             */
     548                 : /************************************************************************/
     549                 : 
     550               0 : void CPCIDSKFile::FlushBlock()
     551                 : 
     552                 : {
     553               0 :     if( last_block_dirty ) 
     554                 :     {
     555               0 :         last_block_mutex->Acquire();
     556               0 :         if( last_block_dirty ) // is it still dirty?
     557                 :         {
     558               0 :             WriteBlock( last_block_index, last_block_data );
     559               0 :             last_block_dirty = 0;
     560                 :         }
     561               0 :         last_block_mutex->Release();
     562                 :     }
     563               0 : }
     564                 : 
     565                 : /************************************************************************/
     566                 : /*                            GetIODetails()                            */
     567                 : /************************************************************************/
     568                 : 
     569             124 : void CPCIDSKFile::GetIODetails( void ***io_handle_pp, 
     570                 :                                 Mutex ***io_mutex_pp, 
     571                 :                                 std::string filename )
     572                 : 
     573                 : {
     574             124 :     *io_handle_pp = NULL;
     575             124 :     *io_mutex_pp = NULL;
     576                 : 
     577                 : /* -------------------------------------------------------------------- */
     578                 : /*      Does this reference the PCIDSK file itself?                     */
     579                 : /* -------------------------------------------------------------------- */
     580             124 :     if( filename.size() == 0 )
     581                 :     {
     582             124 :         *io_handle_pp = &io_handle;
     583             124 :         *io_mutex_pp = &io_mutex;
     584             124 :         return;
     585                 :     }
     586                 : 
     587                 : /* -------------------------------------------------------------------- */
     588                 : /*      Does the file exist already in our file list?                   */
     589                 : /* -------------------------------------------------------------------- */
     590                 :     unsigned int i;
     591                 : 
     592               0 :     for( i = 0; i < file_list.size(); i++ )
     593                 :     {
     594               0 :         if( file_list[i].filename == filename )
     595                 :         {
     596               0 :             *io_handle_pp = &(file_list[i].io_handle);
     597               0 :             *io_mutex_pp = &(file_list[i].io_mutex);
     598               0 :             return;
     599                 :         }
     600                 :     }
     601                 : 
     602                 : /* -------------------------------------------------------------------- */
     603                 : /*      If not, we need to try and open the file.  Eventually we        */
     604                 : /*      will need better rules about read or update access.             */
     605                 : /* -------------------------------------------------------------------- */
     606               0 :     ProtectedFile new_file;
     607                 :     
     608               0 :     new_file.io_handle = interfaces.io->Open( filename, "r" );
     609               0 :     if( new_file.io_handle == NULL )
     610                 :         ThrowPCIDSKException( "Unable to open file '%s'.", 
     611               0 :                               filename.c_str() );
     612                 : 
     613                 : /* -------------------------------------------------------------------- */
     614                 : /*      Push the new file into the list of files managed for this       */
     615                 : /*      PCIDSK file.                                                    */
     616                 : /* -------------------------------------------------------------------- */
     617               0 :     new_file.io_mutex = interfaces.CreateMutex();
     618               0 :     new_file.filename = filename;
     619                 : 
     620               0 :     file_list.push_back( new_file );
     621                 : 
     622               0 :     *io_handle_pp = &(file_list[file_list.size()-1].io_handle);
     623               0 :     *io_mutex_pp  = &(file_list[file_list.size()-1].io_mutex);
     624                 : }
     625                 : 
     626                 : /************************************************************************/
     627                 : /*                           DeleteSegment()                            */
     628                 : /************************************************************************/
     629                 : 
     630               1 : void CPCIDSKFile::DeleteSegment( int segment )
     631                 : 
     632                 : {
     633                 : /* -------------------------------------------------------------------- */
     634                 : /*      Is this an existing segment?                                    */
     635                 : /* -------------------------------------------------------------------- */
     636               1 :     PCIDSKSegment *poSeg = GetSegment( segment );
     637                 : 
     638               1 :     if( poSeg == NULL )
     639               0 :         ThrowPCIDSKException( "DeleteSegment(%d) failed, segment does not exist.", segment );
     640                 : 
     641                 : /* -------------------------------------------------------------------- */
     642                 : /*      Wipe associated metadata.                                       */
     643                 : /* -------------------------------------------------------------------- */
     644               1 :     std::vector<std::string> md_keys = poSeg->GetMetadataKeys();
     645                 :     unsigned int i;
     646                 : 
     647               2 :     for( i = 0; i < md_keys.size(); i++ )
     648               0 :         poSeg->SetMetadataValue( md_keys[i], "" );
     649                 : 
     650                 : /* -------------------------------------------------------------------- */
     651                 : /*      Remove the segment object from the segment object cache.  I     */
     652                 : /*      hope the application is not retaining any references to this    */
     653                 : /*      segment!                                                        */
     654                 : /* -------------------------------------------------------------------- */
     655               1 :     segments[segment] = NULL;
     656               1 :     delete poSeg;
     657                 : 
     658                 : /* -------------------------------------------------------------------- */
     659                 : /*      Mark the segment pointer as deleted.                            */
     660                 : /* -------------------------------------------------------------------- */
     661               1 :     segment_pointers.buffer[(segment-1)*32] = 'D';
     662                 : 
     663                 :     // write the updated segment pointer back to the file. 
     664                 :     WriteToFile( segment_pointers.buffer + (segment-1)*32, 
     665                 :                  segment_pointers_offset + (segment-1)*32, 
     666               1 :                  32 );
     667               1 : }
     668                 : 
     669                 : /************************************************************************/
     670                 : /*                           CreateSegment()                            */
     671                 : /************************************************************************/
     672                 : 
     673              52 : int CPCIDSKFile::CreateSegment( std::string name, std::string description,
     674                 :                                 eSegType seg_type, int data_blocks )
     675                 : 
     676                 : {
     677                 : /* -------------------------------------------------------------------- */
     678                 : /*  Set the size of fixed length segments.        */
     679                 : /* -------------------------------------------------------------------- */
     680              52 :     int expected_data_blocks = 0;
     681                 : 
     682              52 :     switch( seg_type )
     683                 :     {
     684                 :       case SEG_LUT:
     685               0 :   expected_data_blocks = 2;
     686               0 :   break;
     687                 : 
     688                 :       case SEG_PCT:
     689               1 :   expected_data_blocks = 6;
     690               1 :   break;
     691                 : 
     692                 :       case SEG_SIG:
     693               0 :   expected_data_blocks = 12;
     694               0 :   break;
     695                 : 
     696                 :       case SEG_GCP2:
     697                 :   // expected_data_blocks = 67;
     698                 :   // Change seg type to new GCP segment type
     699               0 :   expected_data_blocks = 129;
     700               0 :   break;
     701                 :   
     702                 :       case SEG_GEO:
     703              34 :   expected_data_blocks = 6;
     704                 :   break;
     705                 : 
     706                 : /* -------------------------------------------------------------------- */
     707                 : /*      We do some complicated stuff here to avoid exceeding the        */
     708                 : /*      largest number representable in a int32 (2GB).                  */
     709                 : /* -------------------------------------------------------------------- */
     710                 : #ifdef notdef
     711                 :       case SEG_BIT:
     712                 :         {
     713                 :             int   nBlocksPerScanline, nExtraPixels;
     714                 :             int   nBlocksPerPixel, nExtraScanlines;
     715                 :             
     716                 :             nExtraScanlines = IDB->lines % 4096;
     717                 :             nBlocksPerPixel = IDB->lines / 4096;
     718                 :             
     719                 :             nExtraPixels = IDB->pixels % 4096;
     720                 :             nBlocksPerScanline = IDB->pixels / 4096;
     721                 :             
     722                 :             nBlocks = (nExtraPixels * nExtraScanlines + 4095) / 4096
     723                 :                 + nBlocksPerScanline * IDB->lines
     724                 :                 + nBlocksPerPixel * nExtraPixels
     725                 :                 + 2;
     726                 : 
     727                 :       if ((double)IDB->pixels * (double)IDB->lines/8.0 > (double)512*(double)2147483647)
     728                 :     IMPError( 68, ERRTYP_UFATAL,
     729                 :                       MkName(NLSLookup("@CantCreatePCIDSKWithBMPlarger1024GcurrentBMP_NUM_NUM_NUM_:Cannot "
     730                 :           "create PCIDSK with a bitmap larger than 1024GB in size.\n"
     731                 :                       "The bitmap is %dp x %dl~= %6.1fMB.\n"),
     732                 :                       IDB->pixels, IDB->lines , 
     733                 :                       IDB->pixels * (double) IDB->lines
     734                 :                       / 1000000.0 ));
     735                 :         }
     736                 :         break
     737                 : 
     738                 :       case SEG_TEX:
     739                 :         if( nBlocks < 66 )
     740                 :             nBlocks = 66;
     741                 :         break;
     742                 : #endif
     743                 : 
     744                 :       default:
     745                 :         break;
     746                 :     }
     747                 : 
     748              52 :     if( data_blocks == 0 && expected_data_blocks != 0 )
     749               1 :         data_blocks = expected_data_blocks;
     750                 : 
     751                 : /* -------------------------------------------------------------------- */
     752                 : /*      Find an empty Segment Pointer.  For System segments we start    */
     753                 : /*      at the end, instead of the beginning to avoid using up          */
     754                 : /*      segment numbers that the user would notice.                     */
     755                 : /* -------------------------------------------------------------------- */
     756              52 :     int segment = 1;
     757              52 :     int64 seg_start = -1;
     758              52 :     PCIDSKBuffer segptr( 32 );
     759                 : 
     760              52 :     if( seg_type == SEG_SYS )
     761                 :     {
     762              17 :         for( segment=segment_count; segment >= 1; segment-- )
     763                 :         {
     764              17 :             memcpy( segptr.buffer, segment_pointers.buffer+(segment-1)*32, 32);
     765                 : 
     766              17 :             uint64 this_seg_size = segptr.GetUInt64(23,9);
     767              17 :             char flag = (char) segptr.buffer[0];
     768                 : 
     769              17 :             if( flag == 'D' 
     770                 :                 && (uint64) data_blocks+2 == this_seg_size 
     771                 :                 && this_seg_size > 0 )
     772               0 :                 seg_start = segptr.GetUInt64(12,11) - 1;
     773              17 :             else if( flag == ' ' )
     774              17 :                 seg_start = 0;
     775               0 :             else if( flag && this_seg_size == 0 )
     776               0 :                 seg_start = 0;
     777                 : 
     778              17 :             if( seg_start != -1 )
     779              17 :                 break;
     780                 :         }
     781                 :     }
     782                 :     else
     783                 :     {
     784              36 :         for( segment=1; segment <= segment_count; segment++ )
     785                 :         {
     786              36 :             memcpy( segptr.buffer, segment_pointers.buffer+(segment-1)*32, 32);
     787                 : 
     788              36 :             uint64 this_seg_size = segptr.GetUInt64(23,9);
     789              36 :             char flag = (char) segptr.buffer[0];
     790                 : 
     791              36 :             if( flag == 'D' 
     792                 :                 && (uint64) data_blocks+2 == this_seg_size 
     793                 :                 && this_seg_size > 0 )
     794               0 :                 seg_start = segptr.GetUInt64(12,11) - 1;
     795              36 :             else if( flag == ' ' )
     796              35 :                 seg_start = 0;
     797               1 :             else if( flag && this_seg_size == 0 )
     798               0 :                 seg_start = 0;
     799                 : 
     800              36 :             if( seg_start != -1 )
     801              35 :                 break;
     802                 :         }
     803                 :     }
     804                 :     
     805              52 :     if( segment > segment_count )
     806               0 :         ThrowPCIDSKException( "All %d segment pointers in use.", segment_count);
     807                 : 
     808                 : /* -------------------------------------------------------------------- */
     809                 : /*      If the segment does not have a data area already, identify      */
     810                 : /*      it's location at the end of the file, and extend the file to    */
     811                 : /*      the desired length.                                             */
     812                 : /* -------------------------------------------------------------------- */
     813              52 :     if( seg_start == 0 )
     814                 :     {
     815              52 :         seg_start = GetFileSize();
     816              52 :         ExtendFile( data_blocks + 2 );
     817                 :     }
     818                 : 
     819                 : /* -------------------------------------------------------------------- */
     820                 : /*      Update the segment pointer information.                         */
     821                 : /* -------------------------------------------------------------------- */
     822                 :     // SP1.1 - Flag
     823              52 :     segptr.Put( "A", 0, 1 );
     824                 : 
     825                 :     // SP1.2 - Type
     826              52 :     segptr.Put( (int) seg_type, 1, 3 );
     827                 : 
     828                 :     // SP1.3 - Name
     829              52 :     segptr.Put( name.c_str(), 4, 8 );
     830                 :     
     831                 :     // SP1.4 - start block
     832              52 :     segptr.Put( (uint64) (seg_start + 1), 12, 11 );
     833                 :     
     834                 :     // SP1.5 - data blocks.
     835              52 :     segptr.Put( data_blocks+2, 23, 9 );
     836                 : 
     837                 :     // Update in memory copy of segment pointers.
     838              52 :     memcpy( segment_pointers.buffer+(segment-1)*32, segptr.buffer, 32);
     839                 : 
     840                 :     // Update on disk. 
     841                 :     WriteToFile( segptr.buffer, 
     842              52 :                  segment_pointers_offset + (segment-1)*32, 32 );
     843                 :     
     844                 : /* -------------------------------------------------------------------- */
     845                 : /*      Prepare segment header.                                         */
     846                 : /* -------------------------------------------------------------------- */
     847              52 :     PCIDSKBuffer sh(1024);
     848                 : 
     849                 :     char current_time[17];
     850              52 :     GetCurrentDateTime( current_time );
     851                 : 
     852              52 :     sh.Put( " ", 0, 1024 );
     853                 : 
     854                 :     // SH1 - segment content description
     855              52 :     sh.Put( description.c_str(), 0, 64 );
     856                 : 
     857                 :     // SH3 - Creation time/date
     858              52 :     sh.Put( current_time, 128, 16 );
     859                 : 
     860                 :     // SH4 - Last Update time/date
     861              52 :     sh.Put( current_time, 144, 16 );
     862                 : 
     863                 : /* -------------------------------------------------------------------- */
     864                 : /*      Write segment header.                                           */
     865                 : /* -------------------------------------------------------------------- */
     866              52 :     WriteToFile( sh.buffer, seg_start * 512, 1024 );
     867                 : 
     868              52 :     return segment;
     869                 : }
     870                 : 
     871                 : /************************************************************************/
     872                 : /*                             ExtendFile()                             */
     873                 : /************************************************************************/
     874                 : 
     875              69 : void CPCIDSKFile::ExtendFile( uint64 blocks_requested, bool prezero )
     876                 : 
     877                 : {
     878              69 :     if( prezero )
     879                 :     {
     880               0 :         std::vector<uint8> zeros;
     881               0 :         uint64 blocks_to_zero = blocks_requested;
     882                 :         
     883               0 :         zeros.resize( 512 * 32 );
     884                 :         
     885               0 :         while( blocks_to_zero > 0 )
     886                 :         {
     887               0 :             uint64 this_time = blocks_to_zero;
     888               0 :             if( this_time > 32 )
     889               0 :                 this_time = 32;
     890                 : 
     891               0 :             WriteToFile( &(zeros[0]), file_size * 512, this_time*512 );
     892               0 :             blocks_to_zero -= this_time;
     893               0 :             file_size += this_time;
     894               0 :         }
     895                 :     }
     896                 :     else
     897                 :     {
     898              69 :         WriteToFile( "\0", (file_size + blocks_requested) * 512 - 1, 1 );
     899              69 :         file_size += blocks_requested;
     900                 :     }
     901                 : 
     902              69 :     PCIDSKBuffer fh3( 16 );
     903              69 :     fh3.Put( file_size, 0, 16 );
     904              69 :     WriteToFile( fh3.buffer, 16, 16 );
     905              69 : }
     906                 : 
     907                 : /************************************************************************/
     908                 : /*                           ExtendSegment()                            */
     909                 : /************************************************************************/
     910                 : 
     911              17 : void CPCIDSKFile::ExtendSegment( int segment, uint64 blocks_requested,
     912                 :                                  bool prezero )
     913                 : 
     914                 : {
     915                 :     // for now we take it for granted that the segment is valid and at th
     916                 :     // end of the file - later we should support moving it. 
     917                 : 
     918              17 :     ExtendFile( blocks_requested, prezero );
     919                 : 
     920                 :     // Update the block count. 
     921                 :     segment_pointers.Put( 
     922                 :         segment_pointers.GetUInt64((segment-1)*32+23,9) + blocks_requested,
     923              17 :         (segment-1)*32+23, 9 );
     924                 : 
     925                 :     // write the updated segment pointer back to the file. 
     926                 :     WriteToFile( segment_pointers.buffer + (segment-1)*32, 
     927                 :                  segment_pointers_offset + (segment-1)*32, 
     928              17 :                  32 );
     929              17 : }
     930                 : 
     931                 : /************************************************************************/
     932                 : /*                          MoveSegmentToEOF()                          */
     933                 : /************************************************************************/
     934                 : 
     935               0 : void CPCIDSKFile::MoveSegmentToEOF( int segment )
     936                 : 
     937                 : {
     938               0 :     int segptr_off = (segment - 1) * 32;
     939                 :     uint64 seg_start, seg_size;
     940                 :     uint64 new_seg_start;
     941                 : 
     942               0 :     seg_start = segment_pointers.GetUInt64( segptr_off + 12, 11 );
     943               0 :     seg_size = segment_pointers.GetUInt64( segptr_off + 23, 9 );
     944                 : 
     945                 :     // Are we already at the end of the file?
     946               0 :     if( (seg_start + seg_size - 1) == file_size )
     947               0 :         return;
     948                 : 
     949               0 :     new_seg_start = file_size + 1;
     950                 : 
     951                 :     // Grow the file to hold the segment at the end.
     952               0 :     ExtendFile( seg_size, false );
     953                 : 
     954                 :     // Move the segment data to the new location.
     955                 :     uint8 copy_buf[16384];
     956                 :     uint64 srcoff, dstoff, bytes_to_go;
     957                 : 
     958               0 :     bytes_to_go = seg_size * 512;
     959               0 :     srcoff = (seg_start - 1) * 512;
     960               0 :     dstoff = (new_seg_start - 1) * 512;
     961                 : 
     962               0 :     while( bytes_to_go > 0 )
     963                 :     {
     964               0 :         uint64 bytes_this_chunk = sizeof(copy_buf);
     965               0 :         if( bytes_to_go < bytes_this_chunk )
     966               0 :             bytes_this_chunk = bytes_to_go;
     967                 : 
     968               0 :         ReadFromFile( copy_buf, srcoff, bytes_this_chunk );
     969               0 :         WriteToFile( copy_buf, dstoff, bytes_this_chunk );
     970                 : 
     971               0 :         srcoff += bytes_this_chunk;
     972               0 :         dstoff += bytes_this_chunk;
     973               0 :         bytes_to_go -= bytes_this_chunk;
     974                 :     }
     975                 : 
     976                 :     // Update segment pointer in memory and on disk. 
     977               0 :     segment_pointers.Put( new_seg_start, segptr_off + 12, 11 );
     978                 : 
     979                 :     WriteToFile( segment_pointers.buffer + segptr_off, 
     980                 :                  segment_pointers_offset + segptr_off, 
     981               0 :                  32 );
     982                 :     
     983                 :     // Update the segments own information.
     984               0 :     if( segments[segment] != NULL )
     985                 :     {
     986                 :         CPCIDSKSegment *seg = 
     987               0 :             dynamic_cast<CPCIDSKSegment *>( segments[segment] );
     988                 : 
     989               0 :         seg->LoadSegmentPointer( segment_pointers.buffer + segptr_off );
     990                 :     }
     991                 : }
     992                 : 
     993                 : /************************************************************************/
     994                 : /*                          CreateOverviews()                           */
     995                 : /************************************************************************/
     996                 : /*
     997                 :  const char *pszResampling;
     998                 :        Either "NEAREST" for Nearest Neighbour resampling (the fastest),
     999                 :              "AVERAGE" for block averaging or "MODE" for block mode.  This
    1000                 :              establishing the type of resampling to be applied when preparing
    1001                 :              the decimated overviews.  
    1002                 : */
    1003                 : 
    1004               0 : void CPCIDSKFile::CreateOverviews( int chan_count, int *chan_list, 
    1005                 :                                    int factor, std::string resampling )
    1006                 : 
    1007                 : {
    1008               0 :     std::vector<int> default_chan_list;
    1009                 : 
    1010                 : /* -------------------------------------------------------------------- */
    1011                 : /*      Validate resampling method.                                     */
    1012                 : /* -------------------------------------------------------------------- */
    1013               0 :     UCaseStr( resampling );
    1014                 : 
    1015               0 :     if( resampling != "NEAREST" 
    1016                 :         && resampling != "AVERAGE"
    1017                 :         && resampling != "MODE" )
    1018                 :     {
    1019                 :         ThrowPCIDSKException( "Requested overview resampling '%s' not supported.\nUse one of NEAREST, AVERAGE or MODE.",
    1020               0 :                               resampling.c_str() );
    1021                 :     }
    1022                 : 
    1023                 : /* -------------------------------------------------------------------- */
    1024                 : /*      Default to processing all bands.                                */
    1025                 : /* -------------------------------------------------------------------- */
    1026               0 :     if( chan_count == 0 )
    1027                 :     {
    1028               0 :         chan_count = channel_count;
    1029               0 :         default_chan_list.resize( chan_count );
    1030                 : 
    1031               0 :         for( int i = 0; i < chan_count; i++ )
    1032               0 :             default_chan_list[i] = i+1;
    1033                 : 
    1034               0 :         chan_list = &(default_chan_list[0]);
    1035                 :     }
    1036                 : 
    1037                 : /* -------------------------------------------------------------------- */
    1038                 : /*      Work out the creation options that should apply for the         */
    1039                 : /*      overview.                                                       */
    1040                 : /* -------------------------------------------------------------------- */
    1041               0 :     std::string layout = GetMetadataValue( "_DBLayout" );
    1042               0 :     int         blocksize = 127;
    1043               0 :     std::string compression = "NONE";
    1044                 : 
    1045               0 :     if( strncmp( layout.c_str(), "TILED", 5 ) == 0 )
    1046                 :     {
    1047               0 :         ParseTileFormat( layout, blocksize, compression );
    1048                 :     }
    1049                 : 
    1050                 : /* -------------------------------------------------------------------- */
    1051                 : /*      Make sure we have a blockmap segment for managing the tiled     */
    1052                 : /*      layers.                                                         */
    1053                 : /* -------------------------------------------------------------------- */
    1054               0 :     PCIDSKSegment *bm_seg = GetSegment( SEG_SYS, "SysBMDir" );
    1055                 :     SysBlockMap *bm;
    1056                 : 
    1057               0 :     if( bm_seg == NULL )
    1058                 :     {
    1059                 :         CreateSegment( "SysBMDir", 
    1060                 :                        "System Block Map Directory - Do not modify.",
    1061               0 :                        SEG_SYS, 0 );
    1062               0 :         bm_seg = GetSegment( SEG_SYS, "SysBMDir" );
    1063               0 :         bm = dynamic_cast<SysBlockMap *>(bm_seg);
    1064               0 :         bm->Initialize();
    1065                 :     }
    1066                 :     else
    1067               0 :         bm = dynamic_cast<SysBlockMap *>(bm_seg);
    1068                 :         
    1069                 : /* ==================================================================== */
    1070                 : /*      Loop over the channels.                                         */
    1071                 : /* ==================================================================== */
    1072               0 :     for( int chan_index = 0; chan_index < chan_count; chan_index++ )
    1073                 :     {
    1074               0 :         int channel_number = chan_list[chan_index];
    1075               0 :         PCIDSKChannel *channel = GetChannel( channel_number );
    1076                 :         
    1077                 : /* -------------------------------------------------------------------- */
    1078                 : /*      Do we have a preexisting overview that corresponds to this      */
    1079                 : /*      factor?  If so, throw an exception.  Would it be better to      */
    1080                 : /*      just return quietly?                                            */
    1081                 : /* -------------------------------------------------------------------- */
    1082               0 :         for( int i = channel->GetOverviewCount()-1; i >= 0; i-- )
    1083                 :         {
    1084               0 :             PCIDSKChannel *overview = channel->GetOverview( i );
    1085                 :  
    1086               0 :             if( overview->GetWidth() == channel->GetWidth() / factor
    1087               0 :                 && overview->GetHeight() == channel->GetHeight() / factor )
    1088                 :             {
    1089                 :                 ThrowPCIDSKException( "Channel %d already has a factor %d overview.",
    1090               0 :                                       channel_number, factor );
    1091                 :             }
    1092                 :         }
    1093                 : 
    1094                 : /* -------------------------------------------------------------------- */
    1095                 : /*      Create the overview as a tiled image layer.                     */
    1096                 : /* -------------------------------------------------------------------- */
    1097                 :         int virtual_image = 
    1098               0 :             bm->CreateVirtualImageFile( channel->GetWidth() / factor, 
    1099               0 :                                         channel->GetHeight() / factor,
    1100                 :                                         blocksize, blocksize, 
    1101               0 :                                         channel->GetType(), compression );
    1102                 : 
    1103                 : /* -------------------------------------------------------------------- */
    1104                 : /*      Attach reference to this overview as metadata.                  */
    1105                 : /* -------------------------------------------------------------------- */
    1106                 :         char overview_md_value[128];
    1107                 :         char overview_md_key[128];
    1108                 : 
    1109               0 :         sprintf( overview_md_key, "_Overview_%d", factor );
    1110               0 :         sprintf( overview_md_value, "%d 0 %s",virtual_image,resampling.c_str());
    1111                 :                  
    1112               0 :         channel->SetMetadataValue( overview_md_key, overview_md_value );
    1113                 : 
    1114                 : /* -------------------------------------------------------------------- */
    1115                 : /*      Force channel to invalidate it's loaded overview list.          */
    1116                 : /* -------------------------------------------------------------------- */
    1117               0 :         dynamic_cast<CPCIDSKChannel *>(channel)->InvalidateOverviewInfo();
    1118               0 :     }
    1119            1140 : }

Generated by: LCOV version 1.7