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