Coverage Report

Created: 2025-06-09 08:44

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