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 251 : CPCIDSKChannel::CPCIDSKChannel( PCIDSKBuffer &image_header,
33 : uint64 ih_offsetIn,
34 : CPCIDSKFile *fileIn,
35 : eChanType pixel_typeIn,
36 251 : int channel_numberIn )
37 :
38 : {
39 251 : this->pixel_type = pixel_typeIn;
40 251 : this->file = fileIn;
41 251 : this->channel_number = channel_numberIn;
42 251 : this->ih_offset = ih_offsetIn;
43 251 : is_locked = false;
44 251 : byte_order = 'N';
45 251 : needs_swap = !BigEndianSystem();
46 :
47 251 : width = file->GetWidth();
48 251 : height = file->GetHeight();
49 :
50 251 : block_width = width;
51 251 : block_height = 1;
52 :
53 : /* -------------------------------------------------------------------- */
54 : /* Establish if we need to byte swap the data on load/store. */
55 : /* -------------------------------------------------------------------- */
56 251 : if( channel_number != -1 )
57 : {
58 240 : unsigned short test_value = 1;
59 :
60 240 : is_locked = image_header.buffer[200] == 'W';
61 240 : byte_order = image_header.buffer[201];
62 240 : if( ((uint8 *) &test_value)[0] == 1 )
63 240 : needs_swap = (byte_order != 'S');
64 : else
65 0 : needs_swap = (byte_order == 'S');
66 :
67 240 : if( pixel_type == CHN_8U )
68 171 : needs_swap = 0;
69 :
70 240 : 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 240 : metadata.Initialize( file, "IMG", channel_number );
78 : }
79 :
80 : /* -------------------------------------------------------------------- */
81 : /* No overviews for unassociated files, so just mark them as */
82 : /* initialized. */
83 : /* -------------------------------------------------------------------- */
84 251 : overviews_initialized = (channel_number == -1);
85 251 : }
86 :
87 : /************************************************************************/
88 : /* ~CPCIDSKChannel() */
89 : /************************************************************************/
90 :
91 251 : CPCIDSKChannel::~CPCIDSKChannel()
92 :
93 : {
94 251 : InvalidateOverviewInfo();
95 251 : }
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 251 : void CPCIDSKChannel::InvalidateOverviewInfo()
105 :
106 : {
107 262 : 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 251 : overview_infos.clear();
117 251 : overview_bands.clear();
118 251 : overview_decimations.clear();
119 :
120 251 : overviews_initialized = false;
121 251 : }
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 1328 : void CPCIDSKChannel::EstablishOverviewInfo() const
144 :
145 : {
146 1328 : if( overviews_initialized )
147 1097 : return;
148 :
149 231 : overviews_initialized = true;
150 :
151 462 : std::vector<std::string> keys = GetMetadataKeys();
152 231 : std::sort(keys.begin(), keys.end(), SortOverviewComp); // sort overviews
153 : size_t i;
154 :
155 248 : for( i = 0; i < keys.size(); i++ )
156 : {
157 17 : if( !STARTS_WITH(keys[i].c_str(), "_Overview_") )
158 7 : continue;
159 :
160 10 : std::string value = GetMetadataValue( keys[i] );
161 :
162 10 : overview_infos.push_back( value );
163 10 : overview_bands.push_back( nullptr );
164 10 : overview_decimations.push_back( atoi(keys[i].c_str()+10) );
165 : }
166 : }
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 1 : void CPCIDSKChannel::UpdateOverviewInfo(const char *pszOverviewMDValue,
179 : int nFactor)
180 : {
181 1 : overview_infos.push_back( pszOverviewMDValue );
182 1 : overview_bands.push_back( nullptr );
183 1 : overview_decimations.push_back( nFactor );
184 1 : }
185 :
186 : /************************************************************************/
187 : /* GetBlockCount() */
188 : /************************************************************************/
189 :
190 0 : int CPCIDSKChannel::GetBlockCount() const
191 :
192 : {
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 = (GetWidth() + GetBlockWidth() - 1) / GetBlockWidth();
198 0 : int y_block_count = (GetHeight() + GetBlockHeight() - 1) / GetBlockHeight();
199 :
200 0 : return x_block_count * y_block_count;
201 : }
202 :
203 : /************************************************************************/
204 : /* GetOverviewCount() */
205 : /************************************************************************/
206 :
207 780 : int CPCIDSKChannel::GetOverviewCount()
208 :
209 : {
210 780 : EstablishOverviewInfo();
211 :
212 780 : return static_cast<int>(overview_infos.size());
213 : }
214 :
215 : /************************************************************************/
216 : /* GetOverview() */
217 : /************************************************************************/
218 :
219 11 : PCIDSKChannel *CPCIDSKChannel::GetOverview( int overview_index )
220 :
221 : {
222 11 : EstablishOverviewInfo();
223 :
224 11 : 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 11 : if( overview_bands[overview_index] == nullptr )
229 : {
230 22 : PCIDSKBuffer image_header(1024), file_header(1024);
231 : char pseudo_filename[65];
232 :
233 11 : snprintf( pseudo_filename, sizeof(pseudo_filename), "/SIS=%d",
234 11 : atoi(overview_infos[overview_index].c_str()) );
235 :
236 11 : image_header.Put( pseudo_filename, 64, 64 );
237 :
238 11 : overview_bands[overview_index] =
239 : new CTiledChannel( image_header, 0, file_header, -1, file,
240 11 : CHN_UNKNOWN );
241 : }
242 :
243 11 : return overview_bands[overview_index];
244 : }
245 :
246 : /************************************************************************/
247 : /* IsOverviewValid() */
248 : /************************************************************************/
249 :
250 0 : bool CPCIDSKChannel::IsOverviewValid( int overview_index )
251 :
252 : {
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 : &sis_id, &validity );
263 :
264 0 : return validity != 0;
265 : }
266 :
267 : /************************************************************************/
268 : /* GetOverviewResampling() */
269 : /************************************************************************/
270 :
271 0 : std::string CPCIDSKChannel::GetOverviewResampling( int overview_index )
272 :
273 : {
274 0 : EstablishOverviewInfo();
275 :
276 0 : if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
277 : {
278 0 : ThrowPCIDSKException( "Non existent overview (%d) requested.",
279 : overview_index );
280 0 : return "";
281 : }
282 :
283 0 : int sis_id, validity=0;
284 : char resampling[17];
285 :
286 0 : sscanf( overview_infos[overview_index].c_str(), "%d %d %16s",
287 : &sis_id, &validity, &(resampling[0]) );
288 :
289 0 : return resampling;
290 : }
291 :
292 : /************************************************************************/
293 : /* SetOverviewValidity() */
294 : /************************************************************************/
295 :
296 21 : void CPCIDSKChannel::SetOverviewValidity( int overview_index,
297 : bool new_validity )
298 :
299 : {
300 21 : EstablishOverviewInfo();
301 :
302 21 : if( overview_index < 0 || overview_index >= (int) overview_infos.size() )
303 0 : return ThrowPCIDSKException( "Non existent overview (%d) requested.",
304 19 : overview_index );
305 :
306 21 : int sis_id, validity=0;
307 : char resampling[17];
308 :
309 21 : sscanf( overview_infos[overview_index].c_str(), "%d %d %16s",
310 : &sis_id, &validity, &(resampling[0]) );
311 :
312 : // are we already set to this value?
313 21 : if( new_validity == (validity != 0) )
314 19 : return;
315 :
316 : char new_info[48];
317 :
318 2 : snprintf( new_info, sizeof(new_info), "%d %d %s",
319 : sis_id, (new_validity ? 1 : 0 ), resampling );
320 :
321 2 : overview_infos[overview_index] = new_info;
322 :
323 : // write back to metadata.
324 : char key[20];
325 2 : snprintf( key, sizeof(key), "_Overview_%d", overview_decimations[overview_index] );
326 :
327 2 : SetMetadataValue( key, new_info );
328 : }
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 516 : void CPCIDSKChannel::InvalidateOverviews()
338 :
339 : {
340 516 : EstablishOverviewInfo();
341 :
342 536 : for( int i = 0; i < GetOverviewCount(); i++ )
343 20 : SetOverviewValidity( i, false );
344 516 : }
345 :
346 : /************************************************************************/
347 : /* GetOverviewLevelMapping() */
348 : /************************************************************************/
349 :
350 0 : std::vector<int> CPCIDSKChannel::GetOverviewLevelMapping() const
351 : {
352 0 : EstablishOverviewInfo();
353 :
354 0 : return overview_decimations;
355 : }
356 :
357 : /************************************************************************/
358 : /* GetFilename() */
359 : /************************************************************************/
360 0 : std::string CPCIDSKChannel::GetFilename() const
361 : {
362 0 : return file->GetFilename();
363 : }
364 :
365 : /************************************************************************/
366 : /* GetDescription() */
367 : /************************************************************************/
368 :
369 247 : std::string CPCIDSKChannel::GetDescription()
370 :
371 : {
372 247 : if( ih_offset == 0 )
373 0 : return "";
374 :
375 494 : PCIDSKBuffer ih_1(64);
376 494 : std::string ret;
377 :
378 247 : file->ReadFromFile( ih_1.buffer, ih_offset, 64 );
379 247 : ih_1.Get(0,64,ret);
380 :
381 247 : return ret;
382 : }
383 :
384 : /************************************************************************/
385 : /* SetDescription() */
386 : /************************************************************************/
387 :
388 3 : void CPCIDSKChannel::SetDescription( const std::string &description )
389 :
390 : {
391 3 : if( ih_offset == 0 )
392 0 : return ThrowPCIDSKException( "Description cannot be set on overviews." );
393 :
394 6 : PCIDSKBuffer ih_1(64);
395 3 : ih_1.Put( description.c_str(), 0, 64 );
396 3 : file->WriteToFile( ih_1.buffer, ih_offset, 64 );
397 : }
398 :
399 : /************************************************************************/
400 : /* LoadHistory() */
401 : /************************************************************************/
402 :
403 240 : void CPCIDSKChannel::LoadHistory( const PCIDSKBuffer &image_header )
404 :
405 : {
406 : // Read the history from the image header. PCIDSK supports
407 : // 8 history entries per channel.
408 :
409 480 : std::string hist_msg;
410 240 : history_.clear();
411 2160 : for (unsigned int i = 0; i < 8; i++)
412 : {
413 1920 : 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 1920 : size_t size = hist_msg.size();
419 0 : while( size > 0
420 1920 : && (hist_msg[size-1] == ' ' || hist_msg[size-1] == '\0') )
421 0 : size--;
422 :
423 1920 : hist_msg.resize(size);
424 :
425 1920 : history_.push_back(hist_msg);
426 : }
427 240 : }
428 :
429 : /************************************************************************/
430 : /* GetHistoryEntries() */
431 : /************************************************************************/
432 :
433 0 : std::vector<std::string> CPCIDSKChannel::GetHistoryEntries() const
434 : {
435 0 : return history_;
436 : }
437 :
438 : /************************************************************************/
439 : /* SetHistoryEntries() */
440 : /************************************************************************/
441 :
442 0 : void CPCIDSKChannel::SetHistoryEntries(const std::vector<std::string> &entries)
443 :
444 : {
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 : {
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 : }
460 :
461 0 : file->WriteToFile( image_header.buffer, ih_offset, 1024 );
462 :
463 : // Force reloading of history_
464 0 : LoadHistory( image_header );
465 : }
466 :
467 : /************************************************************************/
468 : /* PushHistory() */
469 : /************************************************************************/
470 :
471 0 : void CPCIDSKChannel::PushHistory( const std::string &app,
472 : const std::string &message )
473 :
474 : {
475 : #define MY_MIN(a,b) ((a<b) ? a : b)
476 :
477 : char current_time[17];
478 : 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 7 : void CPCIDSKChannel::GetChanInfo( std::string &filename, uint64 &image_offset,
504 : uint64 &pixel_offset, uint64 &line_offset,
505 : bool &little_endian ) const
506 :
507 : {
508 7 : image_offset = 0;
509 7 : pixel_offset = 0;
510 7 : line_offset = 0;
511 7 : little_endian = true;
512 7 : filename = "";
513 7 : }
514 :
515 : /************************************************************************/
516 : /* SetChanInfo() */
517 : /************************************************************************/
518 :
519 0 : 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 : {
525 0 : return ThrowPCIDSKException( "Attempt to SetChanInfo() on a channel that is not FILE interleaved." );
526 : }
527 :
528 : /************************************************************************/
529 : /* GetEChanInfo() */
530 : /************************************************************************/
531 0 : void CPCIDSKChannel::GetEChanInfo( std::string &filename, int &echannel,
532 : int &exoff, int &eyoff,
533 : int &exsize, int &eysize ) const
534 :
535 : {
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 0 : 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 : {
555 0 : return ThrowPCIDSKException( "Attempt to SetEChanInfo() on a channel that is not FILE interleaved." );
556 : }
|