Coverage Report

Created: 2026-02-14 09:00

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