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