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 : /* EstablishOverviewInfo() */
127 : /************************************************************************/
128 1588 : void CPCIDSKChannel::EstablishOverviewInfo() const
129 :
130 : {
131 1588 : if( overviews_initialized )
132 1355 : return;
133 :
134 233 : overviews_initialized = true;
135 :
136 466 : std::vector<std::string> keys = GetMetadataKeys();
137 233 : if( !keys.empty() )
138 : {
139 12 : std::sort(keys.begin(), keys.end(),
140 10 : [] (const std::string &first,
141 : const std::string &second)
142 : {
143 10 : if( !STARTS_WITH(first.c_str(), "_Overview_") ||
144 0 : !STARTS_WITH(second.c_str(), "_Overview_") )
145 : {
146 10 : return false;
147 : }
148 0 : int nFirst = atoi(first.c_str() + 10);
149 0 : int nSecond = atoi(second.c_str() + 10);
150 0 : return nFirst < nSecond;
151 : });
152 : }
153 :
154 250 : for( const std::string& key: keys)
155 : {
156 17 : if( !STARTS_WITH(key.c_str(), "_Overview_") )
157 7 : continue;
158 :
159 10 : overview_infos.push_back( GetMetadataValue( key ) );
160 10 : overview_bands.push_back( nullptr );
161 10 : overview_decimations.push_back( atoi(key.c_str()+10) );
162 : }
163 : }
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 1 : void CPCIDSKChannel::UpdateOverviewInfo(const char *pszOverviewMDValue,
176 : int nFactor)
177 : {
178 1 : overview_infos.push_back( pszOverviewMDValue );
179 1 : overview_bands.push_back( nullptr );
180 1 : overview_decimations.push_back( nFactor );
181 1 : }
182 :
183 : /************************************************************************/
184 : /* GetBlockCount() */
185 : /************************************************************************/
186 :
187 0 : int CPCIDSKChannel::GetBlockCount() const
188 :
189 : {
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 : }
199 :
200 : /************************************************************************/
201 : /* GetOverviewCount() */
202 : /************************************************************************/
203 :
204 911 : int CPCIDSKChannel::GetOverviewCount()
205 :
206 : {
207 911 : EstablishOverviewInfo();
208 :
209 911 : return static_cast<int>(overview_infos.size());
210 : }
211 :
212 : /************************************************************************/
213 : /* GetOverview() */
214 : /************************************************************************/
215 :
216 11 : PCIDSKChannel *CPCIDSKChannel::GetOverview( int overview_index )
217 :
218 : {
219 11 : EstablishOverviewInfo();
220 :
221 11 : 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 11 : if( overview_bands[overview_index] == nullptr )
226 : {
227 22 : PCIDSKBuffer image_header(1024), file_header(1024);
228 : char pseudo_filename[65];
229 :
230 11 : snprintf( pseudo_filename, sizeof(pseudo_filename), "/SIS=%d",
231 11 : atoi(overview_infos[overview_index].c_str()) );
232 :
233 11 : image_header.Put( pseudo_filename, 64, 64 );
234 :
235 11 : overview_bands[overview_index] =
236 : new CTiledChannel( image_header, 0, file_header, -1, file,
237 11 : CHN_UNKNOWN );
238 : }
239 :
240 11 : return overview_bands[overview_index];
241 : }
242 :
243 : /************************************************************************/
244 : /* IsOverviewValid() */
245 : /************************************************************************/
246 :
247 0 : bool CPCIDSKChannel::IsOverviewValid( int overview_index )
248 :
249 : {
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 : &sis_id, &validity );
260 :
261 0 : return validity != 0;
262 : }
263 :
264 : /************************************************************************/
265 : /* GetOverviewResampling() */
266 : /************************************************************************/
267 :
268 0 : std::string CPCIDSKChannel::GetOverviewResampling( int overview_index )
269 :
270 : {
271 0 : EstablishOverviewInfo();
272 :
273 0 : if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
274 : {
275 0 : ThrowPCIDSKException( "Non existent overview (%d) requested.",
276 : overview_index );
277 0 : return "";
278 : }
279 :
280 0 : int sis_id, validity=0;
281 : char resampling[17];
282 :
283 0 : sscanf( overview_infos[overview_index].c_str(), "%d %d %16s",
284 : &sis_id, &validity, &(resampling[0]) );
285 :
286 0 : return resampling;
287 : }
288 :
289 : /************************************************************************/
290 : /* SetOverviewValidity() */
291 : /************************************************************************/
292 :
293 21 : void CPCIDSKChannel::SetOverviewValidity( int overview_index,
294 : bool new_validity )
295 :
296 : {
297 21 : EstablishOverviewInfo();
298 :
299 21 : if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
300 0 : return ThrowPCIDSKException( "Non existent overview (%d) requested.",
301 19 : overview_index );
302 :
303 21 : int sis_id, validity=0;
304 : char resampling[17];
305 :
306 21 : sscanf( overview_infos[overview_index].c_str(), "%d %d %16s",
307 : &sis_id, &validity, &(resampling[0]) );
308 :
309 : // are we already set to this value?
310 21 : if( new_validity == (validity != 0) )
311 19 : return;
312 :
313 : char new_info[48];
314 :
315 2 : snprintf( new_info, sizeof(new_info), "%d %d %s",
316 : sis_id, (new_validity ? 1 : 0 ), resampling );
317 :
318 2 : overview_infos[overview_index] = new_info;
319 :
320 : // write back to metadata.
321 : char key[20];
322 2 : snprintf( key, sizeof(key), "_Overview_%d", overview_decimations[overview_index] );
323 :
324 2 : SetMetadataValue( key, new_info );
325 : }
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 645 : void CPCIDSKChannel::InvalidateOverviews()
335 :
336 : {
337 645 : EstablishOverviewInfo();
338 :
339 665 : for( int i = 0; i < GetOverviewCount(); i++ )
340 20 : SetOverviewValidity( i, false );
341 645 : }
342 :
343 : /************************************************************************/
344 : /* GetOverviewLevelMapping() */
345 : /************************************************************************/
346 :
347 0 : std::vector<int> CPCIDSKChannel::GetOverviewLevelMapping() const
348 : {
349 0 : EstablishOverviewInfo();
350 :
351 0 : return overview_decimations;
352 : }
353 :
354 : /************************************************************************/
355 : /* GetFilename() */
356 : /************************************************************************/
357 0 : std::string CPCIDSKChannel::GetFilename() const
358 : {
359 0 : return file->GetFilename();
360 : }
361 :
362 : /************************************************************************/
363 : /* GetDescription() */
364 : /************************************************************************/
365 :
366 249 : std::string CPCIDSKChannel::GetDescription()
367 :
368 : {
369 249 : if( ih_offset == 0 )
370 0 : return "";
371 :
372 498 : PCIDSKBuffer ih_1(64);
373 498 : std::string ret;
374 :
375 249 : file->ReadFromFile( ih_1.buffer, ih_offset, 64 );
376 249 : ih_1.Get(0,64,ret);
377 :
378 249 : return ret;
379 : }
380 :
381 : /************************************************************************/
382 : /* SetDescription() */
383 : /************************************************************************/
384 :
385 3 : void CPCIDSKChannel::SetDescription( const std::string &description )
386 :
387 : {
388 3 : if( ih_offset == 0 )
389 0 : return ThrowPCIDSKException( "Description cannot be set on overviews." );
390 :
391 6 : PCIDSKBuffer ih_1(64);
392 3 : ih_1.Put( description.c_str(), 0, 64 );
393 3 : file->WriteToFile( ih_1.buffer, ih_offset, 64 );
394 : }
395 :
396 : /************************************************************************/
397 : /* LoadHistory() */
398 : /************************************************************************/
399 :
400 242 : void CPCIDSKChannel::LoadHistory( const PCIDSKBuffer &image_header )
401 :
402 : {
403 : // Read the history from the image header. PCIDSK supports
404 : // 8 history entries per channel.
405 :
406 484 : std::string hist_msg;
407 242 : history_.clear();
408 2178 : for (unsigned int i = 0; i < 8; i++)
409 : {
410 1936 : 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 1936 : size_t size = hist_msg.size();
416 0 : while( size > 0
417 1936 : && (hist_msg[size-1] == ' ' || hist_msg[size-1] == '\0') )
418 0 : size--;
419 :
420 1936 : hist_msg.resize(size);
421 :
422 1936 : history_.push_back(hist_msg);
423 : }
424 242 : }
425 :
426 : /************************************************************************/
427 : /* GetHistoryEntries() */
428 : /************************************************************************/
429 :
430 0 : std::vector<std::string> CPCIDSKChannel::GetHistoryEntries() const
431 : {
432 0 : return history_;
433 : }
434 :
435 : /************************************************************************/
436 : /* SetHistoryEntries() */
437 : /************************************************************************/
438 :
439 0 : void CPCIDSKChannel::SetHistoryEntries(const std::vector<std::string> &entries)
440 :
441 : {
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 : {
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 : }
457 :
458 0 : file->WriteToFile( image_header.buffer, ih_offset, 1024 );
459 :
460 : // Force reloading of history_
461 0 : LoadHistory( image_header );
462 : }
463 :
464 : /************************************************************************/
465 : /* PushHistory() */
466 : /************************************************************************/
467 :
468 0 : void CPCIDSKChannel::PushHistory( const std::string &app,
469 : const std::string &message )
470 :
471 : {
472 : #define MY_MIN(a,b) ((a<b) ? a : b)
473 :
474 : char current_time[17];
475 : 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 7 : void CPCIDSKChannel::GetChanInfo( std::string &filename, uint64 &image_offset,
501 : uint64 &pixel_offset, uint64 &line_offset,
502 : bool &little_endian ) const
503 :
504 : {
505 7 : image_offset = 0;
506 7 : pixel_offset = 0;
507 7 : line_offset = 0;
508 7 : little_endian = true;
509 7 : filename = "";
510 7 : }
511 :
512 : /************************************************************************/
513 : /* SetChanInfo() */
514 : /************************************************************************/
515 :
516 0 : 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 : {
522 0 : return ThrowPCIDSKException( "Attempt to SetChanInfo() on a channel that is not FILE interleaved." );
523 : }
524 :
525 : /************************************************************************/
526 : /* GetEChanInfo() */
527 : /************************************************************************/
528 0 : void CPCIDSKChannel::GetEChanInfo( std::string &filename, int &echannel,
529 : int &exoff, int &eyoff,
530 : int &exsize, int &eysize ) const
531 :
532 : {
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 0 : 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 : {
552 0 : return ThrowPCIDSKException( "Attempt to SetEChanInfo() on a channel that is not FILE interleaved." );
553 : }
|