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