1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the CPCIDSKFile class.
4 : *
5 : ******************************************************************************
6 : * Copyright (c) 2009
7 : * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, 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 : #include "pcidsk_file.h"
28 : #include "pcidsk_exception.h"
29 : #include "pcidsk_channel.h"
30 : #include "pcidsk_segment.h"
31 : #include "core/mutexholder.h"
32 : #include "core/pcidsk_utils.h"
33 : #include "core/cpcidskfile.h"
34 :
35 : // Channel types
36 : #include "channel/cbandinterleavedchannel.h"
37 : #include "channel/cpixelinterleavedchannel.h"
38 : #include "channel/ctiledchannel.h"
39 :
40 : // Segment types
41 : #include "segment/cpcidskgeoref.h"
42 : #include "segment/cpcidskpct.h"
43 : #include "segment/cpcidskvectorsegment.h"
44 : #include "segment/metadatasegment.h"
45 : #include "segment/sysblockmap.h"
46 : #include "segment/cpcidskrpcmodel.h"
47 :
48 : #include <cassert>
49 : #include <cstdlib>
50 : #include <cstring>
51 : #include <cstdio>
52 : #include <string>
53 :
54 : #include <iostream>
55 :
56 : using namespace PCIDSK;
57 :
58 : /************************************************************************/
59 : /* CPCIDSKFile() */
60 : /************************************************************************/
61 :
62 78 : CPCIDSKFile::CPCIDSKFile()
63 :
64 : {
65 78 : io_handle = NULL;
66 78 : io_mutex = NULL;
67 78 : updatable = false;
68 :
69 : /* -------------------------------------------------------------------- */
70 : /* Initialize the metadata object, but do not try to load till */
71 : /* needed. */
72 : /* -------------------------------------------------------------------- */
73 78 : metadata.Initialize( this, "FIL", 0 );
74 78 : }
75 :
76 : /************************************************************************/
77 : /* ~CPCIDSKFile() */
78 : /************************************************************************/
79 :
80 156 : CPCIDSKFile::~CPCIDSKFile()
81 :
82 : {
83 : /* -------------------------------------------------------------------- */
84 : /* Cleanup last line caching stuff for pixel interleaved data. */
85 : /* -------------------------------------------------------------------- */
86 78 : if( last_block_data != NULL )
87 : {
88 0 : FlushBlock();
89 0 : last_block_index = -1;
90 0 : free( last_block_data );
91 0 : last_block_data = NULL;
92 0 : delete last_block_mutex;
93 : }
94 :
95 : /* -------------------------------------------------------------------- */
96 : /* Cleanup channels and segments. */
97 : /* -------------------------------------------------------------------- */
98 : size_t i;
99 202 : for( i = 0; i < channels.size(); i++ )
100 : {
101 124 : delete channels[i];
102 124 : channels[i] = NULL;
103 : }
104 :
105 80028 : for( i = 0; i < segments.size(); i++ )
106 : {
107 79950 : delete segments[i];
108 79950 : segments[i] = NULL;
109 : }
110 :
111 : /* -------------------------------------------------------------------- */
112 : /* Close and cleanup IO stuff. */
113 : /* -------------------------------------------------------------------- */
114 : {
115 78 : MutexHolder oHolder( io_mutex );
116 :
117 78 : if( io_handle )
118 : {
119 78 : interfaces.io->Close( io_handle );
120 78 : io_handle = NULL;
121 78 : }
122 : }
123 :
124 : size_t i_file;
125 :
126 78 : for( i_file=0; i_file < file_list.size(); i_file++ )
127 : {
128 0 : delete file_list[i_file].io_mutex;
129 0 : file_list[i_file].io_mutex = NULL;
130 :
131 0 : interfaces.io->Close( file_list[i_file].io_handle );
132 0 : file_list[i_file].io_handle = NULL;
133 : }
134 :
135 78 : delete io_mutex;
136 156 : }
137 :
138 : /************************************************************************/
139 : /* GetChannel() */
140 : /************************************************************************/
141 :
142 69 : PCIDSKChannel *CPCIDSKFile::GetChannel( int band )
143 :
144 : {
145 69 : if( band < 1 || band > channel_count )
146 : ThrowPCIDSKException( "Out of range band (%d) requested.",
147 0 : band );
148 :
149 69 : return channels[band-1];
150 : }
151 :
152 : /************************************************************************/
153 : /* GetSegment() */
154 : /************************************************************************/
155 :
156 115 : PCIDSK::PCIDSKSegment *CPCIDSKFile::GetSegment( int segment )
157 :
158 : {
159 : /* -------------------------------------------------------------------- */
160 : /* Is this a valid segment? */
161 : /* -------------------------------------------------------------------- */
162 115 : if( segment < 1 || segment > segment_count )
163 0 : return NULL;
164 :
165 115 : const char *segment_pointer = segment_pointers.buffer + (segment-1) * 32;
166 :
167 115 : if( segment_pointer[0] != 'A' && segment_pointer[0] != 'L' )
168 0 : return NULL;
169 :
170 : /* -------------------------------------------------------------------- */
171 : /* Do we already have a corresponding object? */
172 : /* -------------------------------------------------------------------- */
173 115 : if( segments[segment] != NULL )
174 30 : return segments[segment];
175 :
176 : /* -------------------------------------------------------------------- */
177 : /* Instantiate per the type. */
178 : /* -------------------------------------------------------------------- */
179 85 : int segment_type = segment_pointers.GetInt((segment-1)*32+1,3);
180 85 : PCIDSKSegment *segobj = NULL;
181 :
182 85 : switch( segment_type )
183 : {
184 : case SEG_GEO:
185 52 : segobj = new CPCIDSKGeoref( this, segment, segment_pointer );
186 52 : break;
187 :
188 : case SEG_PCT:
189 2 : segobj = new CPCIDSK_PCT( this, segment, segment_pointer );
190 2 : break;
191 :
192 : case SEG_VEC:
193 0 : segobj = new CPCIDSKVectorSegment( this, segment, segment_pointer );
194 0 : break;
195 :
196 : case SEG_SYS:
197 31 : if( strncmp(segment_pointer + 4, "SysBMDir",8) == 0 )
198 3 : segobj = new SysBlockMap( this, segment, segment_pointer );
199 28 : else if( strncmp(segment_pointer + 4, "METADATA",8) == 0 )
200 25 : segobj = new MetadataSegment( this, segment, segment_pointer );
201 : else
202 3 : segobj = new CPCIDSKSegment( this, segment, segment_pointer );
203 :
204 31 : break;
205 :
206 : case SEG_BIN:
207 0 : if (!strncmp(segment_pointer + 4, "RFMODEL ", 8))
208 0 : segobj = new CPCIDSKRPCModelSegment( this, segment, segment_pointer );
209 : break;
210 :
211 : }
212 :
213 85 : if (segobj == NULL)
214 0 : segobj = new CPCIDSKSegment( this, segment, segment_pointer );
215 :
216 85 : segments[segment] = segobj;
217 :
218 85 : return segobj;
219 : }
220 :
221 : /************************************************************************/
222 : /* GetSegment() */
223 : /* */
224 : /* Find segment by type/name. */
225 : /************************************************************************/
226 :
227 146 : PCIDSK::PCIDSKSegment *CPCIDSKFile::GetSegment( int type, std::string name,
228 : int previous )
229 :
230 : {
231 : int i;
232 : char type_str[4];
233 :
234 146 : name += " "; // white space pad name.
235 :
236 146 : sprintf( type_str, "%03d", type );
237 :
238 149606 : for( i = previous; i < segment_count; i++ )
239 : {
240 149498 : if( type != SEG_UNKNOWN
241 : && strncmp(segment_pointers.buffer+i*32+1,type_str,3) != 0 )
242 149452 : continue;
243 :
244 46 : if( name != " "
245 : && strncmp(segment_pointers.buffer+i*32+4,name.c_str(),8) != 0 )
246 8 : continue;
247 :
248 38 : return GetSegment(i+1);
249 : }
250 :
251 108 : return NULL;
252 : }
253 :
254 :
255 : /************************************************************************/
256 : /* GetSegments() */
257 : /************************************************************************/
258 :
259 0 : std::vector<PCIDSK::PCIDSKSegment *> CPCIDSKFile::GetSegments()
260 :
261 : {
262 0 : PCIDSK::ThrowPCIDSKException( "Objects list access not implemented yet." );
263 :
264 0 : std::vector<PCIDSK::PCIDSKSegment *> list;
265 : return list;
266 : }
267 :
268 : /************************************************************************/
269 : /* InitializeFromHeader() */
270 : /************************************************************************/
271 :
272 78 : void CPCIDSKFile::InitializeFromHeader()
273 :
274 : {
275 : /* -------------------------------------------------------------------- */
276 : /* Process the file header. */
277 : /* -------------------------------------------------------------------- */
278 78 : PCIDSKBuffer fh(512);
279 :
280 78 : ReadFromFile( fh.buffer, 0, 512 );
281 :
282 78 : width = atoi(fh.Get(384,8));
283 78 : height = atoi(fh.Get(392,8));
284 78 : channel_count = atoi(fh.Get(376,8));
285 78 : file_size = fh.GetUInt64(16,16);
286 :
287 78 : uint64 ih_start_block = atouint64(fh.Get(336,16));
288 78 : uint64 image_start_block = atouint64(fh.Get(304,16));
289 78 : fh.Get(360,8,interleaving);
290 :
291 78 : uint64 image_offset = (image_start_block-1) * 512;
292 :
293 78 : block_size = 0;
294 78 : last_block_index = -1;
295 78 : last_block_dirty = 0;
296 78 : last_block_data = NULL;
297 78 : last_block_mutex = NULL;
298 :
299 : /* -------------------------------------------------------------------- */
300 : /* Load the segment pointers into a PCIDSKBuffer. For now we */
301 : /* try to avoid doing too much other processing on them. */
302 : /* -------------------------------------------------------------------- */
303 78 : int segment_block_count = atoi(fh.Get(456,8));
304 :
305 78 : segment_count = (segment_block_count * 512) / 32;
306 78 : segment_pointers.SetSize( segment_block_count * 512 );
307 78 : segment_pointers_offset = atouint64(fh.Get(440,16)) * 512 - 512;
308 : ReadFromFile( segment_pointers.buffer, segment_pointers_offset,
309 78 : segment_block_count * 512 );
310 :
311 78 : segments.resize( segment_count + 1 );
312 :
313 : /* -------------------------------------------------------------------- */
314 : /* Get the number of each channel type - only used for some */
315 : /* interleaving cases. */
316 : /* -------------------------------------------------------------------- */
317 78 : int count_8u = atoi(fh.Get(464,4));
318 78 : int count_16s = atoi(fh.Get(468,4));
319 78 : int count_16u = atoi(fh.Get(472,4));
320 78 : int count_32r = atoi(fh.Get(476,4));
321 :
322 : /* -------------------------------------------------------------------- */
323 : /* for pixel interleaved files we need to compute the length of */
324 : /* a scanline padded out to a 512 byte boundary. */
325 : /* -------------------------------------------------------------------- */
326 78 : if( interleaving == "PIXEL" )
327 : {
328 0 : first_line_offset = image_offset;
329 0 : pixel_group_size = count_8u + count_16s*2 + count_16u*2 + count_32r*4;
330 :
331 0 : block_size = pixel_group_size * width;
332 0 : if( block_size % 512 != 0 )
333 0 : block_size += 512 - (block_size % 512);
334 :
335 0 : last_block_data = malloc((size_t) block_size);
336 0 : if( last_block_data == NULL )
337 : ThrowPCIDSKException( "Allocating %d bytes for scanline buffer failed.",
338 0 : (int) block_size );
339 :
340 0 : last_block_mutex = interfaces.CreateMutex();
341 0 : image_offset = 0;
342 : }
343 :
344 : /* -------------------------------------------------------------------- */
345 : /* Initialize the list of channels. */
346 : /* -------------------------------------------------------------------- */
347 : int channelnum;
348 :
349 202 : for( channelnum = 1; channelnum <= channel_count; channelnum++ )
350 : {
351 124 : PCIDSKBuffer ih(1024);
352 124 : PCIDSKChannel *channel = NULL;
353 :
354 : ReadFromFile( ih.buffer,
355 : (ih_start_block-1)*512 + (channelnum-1)*1024,
356 124 : 1024);
357 :
358 : // fetch the filename, if there is one.
359 124 : std::string filename;
360 124 : ih.Get(64,64,filename);
361 :
362 : // work out channel type from header
363 : eChanType pixel_type;
364 124 : const char *pixel_type_string = ih.Get( 160, 8 );
365 :
366 124 : if( strcmp(pixel_type_string,"8U ") == 0 )
367 100 : pixel_type = CHN_8U;
368 24 : else if( strcmp(pixel_type_string,"16S ") == 0 )
369 4 : pixel_type = CHN_16S;
370 20 : else if( strcmp(pixel_type_string,"16U ") == 0 )
371 16 : pixel_type = CHN_16U;
372 4 : else if( strcmp(pixel_type_string,"32R ") == 0 )
373 4 : pixel_type = CHN_32R;
374 : else
375 0 : pixel_type = CHN_UNKNOWN; // should we throw an exception?
376 :
377 : // if we didn't get channel type in header, work out from counts (old)
378 :
379 124 : if( channelnum <= count_8u )
380 100 : pixel_type = CHN_8U;
381 24 : else if( channelnum <= count_8u + count_16s )
382 4 : pixel_type = CHN_16S;
383 20 : else if( channelnum <= count_8u + count_16s + count_16u )
384 16 : pixel_type = CHN_16U;
385 : else
386 4 : pixel_type = CHN_32R;
387 :
388 124 : if( interleaving == "BAND" )
389 : {
390 : channel = new CBandInterleavedChannel( ih, fh, channelnum, this,
391 124 : image_offset, pixel_type );
392 :
393 :
394 124 : image_offset += DataTypeSize(channel->GetType())
395 372 : * width * height;
396 : }
397 :
398 0 : else if( interleaving == "PIXEL" )
399 : {
400 : channel = new CPixelInterleavedChannel( ih, fh, channelnum, this,
401 : (int) image_offset,
402 0 : pixel_type );
403 0 : image_offset += DataTypeSize(pixel_type);
404 : }
405 :
406 0 : else if( interleaving == "FILE"
407 : && strncmp(filename.c_str(),"/SIS=",5) == 0 )
408 : {
409 0 : channel = new CTiledChannel( ih, fh, channelnum, this, pixel_type );
410 : }
411 :
412 0 : else if( interleaving == "FILE" )
413 : {
414 : channel = new CBandInterleavedChannel( ih, fh, channelnum, this,
415 0 : 0, pixel_type );
416 : }
417 :
418 : else
419 : ThrowPCIDSKException( "Unsupported interleaving:%s",
420 0 : interleaving.c_str() );
421 :
422 124 : channels.push_back( channel );
423 78 : }
424 78 : }
425 :
426 : /************************************************************************/
427 : /* ReadFromFile() */
428 : /************************************************************************/
429 :
430 519 : void CPCIDSKFile::ReadFromFile( void *buffer, uint64 offset, uint64 size )
431 :
432 : {
433 519 : MutexHolder oHolder( io_mutex );
434 :
435 519 : interfaces.io->Seek( io_handle, offset, SEEK_SET );
436 519 : if( interfaces.io->Read( buffer, 1, size, io_handle ) != size )
437 : ThrowPCIDSKException( "PCIDSKFile:Failed to read %d bytes at %d.",
438 0 : (int) size, (int) offset );
439 519 : }
440 :
441 : /************************************************************************/
442 : /* WriteToFile() */
443 : /************************************************************************/
444 :
445 363 : void CPCIDSKFile::WriteToFile( const void *buffer, uint64 offset, uint64 size )
446 :
447 : {
448 363 : if( !GetUpdatable() )
449 0 : throw PCIDSKException( "File not open for update in WriteToFile()" );
450 :
451 363 : MutexHolder oHolder( io_mutex );
452 :
453 363 : interfaces.io->Seek( io_handle, offset, SEEK_SET );
454 363 : if( interfaces.io->Write( buffer, 1, size, io_handle ) != size )
455 : ThrowPCIDSKException( "PCIDSKFile:Failed to write %d bytes at %d.",
456 0 : (int) size, (int) offset );
457 363 : }
458 :
459 : /************************************************************************/
460 : /* ReadAndLockBlock() */
461 : /************************************************************************/
462 :
463 0 : void *CPCIDSKFile::ReadAndLockBlock( int block_index,
464 : int win_xoff, int win_xsize )
465 :
466 : {
467 0 : if( last_block_data == NULL )
468 0 : ThrowPCIDSKException( "ReadAndLockBlock() called on a file that is not pixel interleaved." );
469 :
470 : /* -------------------------------------------------------------------- */
471 : /* Default, and validate windowing. */
472 : /* -------------------------------------------------------------------- */
473 0 : if( win_xoff == -1 && win_xsize == -1 )
474 : {
475 0 : win_xoff = 0;
476 0 : win_xsize = GetWidth();
477 : }
478 :
479 0 : if( win_xoff < 0 || win_xoff+win_xsize > GetWidth() )
480 : {
481 : ThrowPCIDSKException( "CPCIDSKFile::ReadAndLockBlock(): Illegal window - xoff=%d, xsize=%d",
482 0 : win_xoff, win_xsize );
483 : }
484 :
485 0 : if( block_index == last_block_index
486 : && win_xoff == last_block_xoff
487 : && win_xsize == last_block_xsize )
488 : {
489 0 : last_block_mutex->Acquire();
490 0 : return last_block_data;
491 : }
492 :
493 : /* -------------------------------------------------------------------- */
494 : /* Flush any dirty writable data. */
495 : /* -------------------------------------------------------------------- */
496 0 : FlushBlock();
497 :
498 : /* -------------------------------------------------------------------- */
499 : /* Read the requested window. */
500 : /* -------------------------------------------------------------------- */
501 0 : last_block_mutex->Acquire();
502 :
503 : ReadFromFile( last_block_data,
504 : first_line_offset + block_index*block_size
505 : + win_xoff * pixel_group_size,
506 0 : pixel_group_size * win_xsize );
507 0 : last_block_index = block_index;
508 0 : last_block_xoff = win_xoff;
509 0 : last_block_xsize = win_xsize;
510 :
511 0 : return last_block_data;
512 : }
513 :
514 : /************************************************************************/
515 : /* UnlockBlock() */
516 : /************************************************************************/
517 :
518 0 : void CPCIDSKFile::UnlockBlock( bool mark_dirty )
519 :
520 : {
521 0 : if( last_block_mutex == NULL )
522 0 : return;
523 :
524 0 : last_block_dirty |= mark_dirty;
525 0 : last_block_mutex->Release();
526 : }
527 :
528 : /************************************************************************/
529 : /* WriteBlock() */
530 : /************************************************************************/
531 :
532 0 : void CPCIDSKFile::WriteBlock( int block_index, void *buffer )
533 :
534 : {
535 0 : if( !GetUpdatable() )
536 0 : throw PCIDSKException( "File not open for update in WriteBlock()" );
537 :
538 0 : if( last_block_data == NULL )
539 0 : ThrowPCIDSKException( "WriteBlock() called on a file that is not pixel interleaved." );
540 :
541 : WriteToFile( buffer,
542 : first_line_offset + block_index*block_size,
543 0 : block_size );
544 0 : }
545 :
546 : /************************************************************************/
547 : /* FlushBlock() */
548 : /************************************************************************/
549 :
550 0 : void CPCIDSKFile::FlushBlock()
551 :
552 : {
553 0 : if( last_block_dirty )
554 : {
555 0 : last_block_mutex->Acquire();
556 0 : if( last_block_dirty ) // is it still dirty?
557 : {
558 0 : WriteBlock( last_block_index, last_block_data );
559 0 : last_block_dirty = 0;
560 : }
561 0 : last_block_mutex->Release();
562 : }
563 0 : }
564 :
565 : /************************************************************************/
566 : /* GetIODetails() */
567 : /************************************************************************/
568 :
569 124 : void CPCIDSKFile::GetIODetails( void ***io_handle_pp,
570 : Mutex ***io_mutex_pp,
571 : std::string filename )
572 :
573 : {
574 124 : *io_handle_pp = NULL;
575 124 : *io_mutex_pp = NULL;
576 :
577 : /* -------------------------------------------------------------------- */
578 : /* Does this reference the PCIDSK file itself? */
579 : /* -------------------------------------------------------------------- */
580 124 : if( filename.size() == 0 )
581 : {
582 124 : *io_handle_pp = &io_handle;
583 124 : *io_mutex_pp = &io_mutex;
584 124 : return;
585 : }
586 :
587 : /* -------------------------------------------------------------------- */
588 : /* Does the file exist already in our file list? */
589 : /* -------------------------------------------------------------------- */
590 : unsigned int i;
591 :
592 0 : for( i = 0; i < file_list.size(); i++ )
593 : {
594 0 : if( file_list[i].filename == filename )
595 : {
596 0 : *io_handle_pp = &(file_list[i].io_handle);
597 0 : *io_mutex_pp = &(file_list[i].io_mutex);
598 0 : return;
599 : }
600 : }
601 :
602 : /* -------------------------------------------------------------------- */
603 : /* If not, we need to try and open the file. Eventually we */
604 : /* will need better rules about read or update access. */
605 : /* -------------------------------------------------------------------- */
606 0 : ProtectedFile new_file;
607 :
608 0 : new_file.io_handle = interfaces.io->Open( filename, "r" );
609 0 : if( new_file.io_handle == NULL )
610 : ThrowPCIDSKException( "Unable to open file '%s'.",
611 0 : filename.c_str() );
612 :
613 : /* -------------------------------------------------------------------- */
614 : /* Push the new file into the list of files managed for this */
615 : /* PCIDSK file. */
616 : /* -------------------------------------------------------------------- */
617 0 : new_file.io_mutex = interfaces.CreateMutex();
618 0 : new_file.filename = filename;
619 :
620 0 : file_list.push_back( new_file );
621 :
622 0 : *io_handle_pp = &(file_list[file_list.size()-1].io_handle);
623 0 : *io_mutex_pp = &(file_list[file_list.size()-1].io_mutex);
624 : }
625 :
626 : /************************************************************************/
627 : /* DeleteSegment() */
628 : /************************************************************************/
629 :
630 1 : void CPCIDSKFile::DeleteSegment( int segment )
631 :
632 : {
633 : /* -------------------------------------------------------------------- */
634 : /* Is this an existing segment? */
635 : /* -------------------------------------------------------------------- */
636 1 : PCIDSKSegment *poSeg = GetSegment( segment );
637 :
638 1 : if( poSeg == NULL )
639 0 : ThrowPCIDSKException( "DeleteSegment(%d) failed, segment does not exist.", segment );
640 :
641 : /* -------------------------------------------------------------------- */
642 : /* Wipe associated metadata. */
643 : /* -------------------------------------------------------------------- */
644 1 : std::vector<std::string> md_keys = poSeg->GetMetadataKeys();
645 : unsigned int i;
646 :
647 2 : for( i = 0; i < md_keys.size(); i++ )
648 0 : poSeg->SetMetadataValue( md_keys[i], "" );
649 :
650 : /* -------------------------------------------------------------------- */
651 : /* Remove the segment object from the segment object cache. I */
652 : /* hope the application is not retaining any references to this */
653 : /* segment! */
654 : /* -------------------------------------------------------------------- */
655 1 : segments[segment] = NULL;
656 1 : delete poSeg;
657 :
658 : /* -------------------------------------------------------------------- */
659 : /* Mark the segment pointer as deleted. */
660 : /* -------------------------------------------------------------------- */
661 1 : segment_pointers.buffer[(segment-1)*32] = 'D';
662 :
663 : // write the updated segment pointer back to the file.
664 : WriteToFile( segment_pointers.buffer + (segment-1)*32,
665 : segment_pointers_offset + (segment-1)*32,
666 1 : 32 );
667 1 : }
668 :
669 : /************************************************************************/
670 : /* CreateSegment() */
671 : /************************************************************************/
672 :
673 52 : int CPCIDSKFile::CreateSegment( std::string name, std::string description,
674 : eSegType seg_type, int data_blocks )
675 :
676 : {
677 : /* -------------------------------------------------------------------- */
678 : /* Set the size of fixed length segments. */
679 : /* -------------------------------------------------------------------- */
680 52 : int expected_data_blocks = 0;
681 :
682 52 : switch( seg_type )
683 : {
684 : case SEG_LUT:
685 0 : expected_data_blocks = 2;
686 0 : break;
687 :
688 : case SEG_PCT:
689 1 : expected_data_blocks = 6;
690 1 : break;
691 :
692 : case SEG_SIG:
693 0 : expected_data_blocks = 12;
694 0 : break;
695 :
696 : case SEG_GCP2:
697 : // expected_data_blocks = 67;
698 : // Change seg type to new GCP segment type
699 0 : expected_data_blocks = 129;
700 0 : break;
701 :
702 : case SEG_GEO:
703 34 : expected_data_blocks = 6;
704 : break;
705 :
706 : /* -------------------------------------------------------------------- */
707 : /* We do some complicated stuff here to avoid exceeding the */
708 : /* largest number representable in a int32 (2GB). */
709 : /* -------------------------------------------------------------------- */
710 : #ifdef notdef
711 : case SEG_BIT:
712 : {
713 : int nBlocksPerScanline, nExtraPixels;
714 : int nBlocksPerPixel, nExtraScanlines;
715 :
716 : nExtraScanlines = IDB->lines % 4096;
717 : nBlocksPerPixel = IDB->lines / 4096;
718 :
719 : nExtraPixels = IDB->pixels % 4096;
720 : nBlocksPerScanline = IDB->pixels / 4096;
721 :
722 : nBlocks = (nExtraPixels * nExtraScanlines + 4095) / 4096
723 : + nBlocksPerScanline * IDB->lines
724 : + nBlocksPerPixel * nExtraPixels
725 : + 2;
726 :
727 : if ((double)IDB->pixels * (double)IDB->lines/8.0 > (double)512*(double)2147483647)
728 : IMPError( 68, ERRTYP_UFATAL,
729 : MkName(NLSLookup("@CantCreatePCIDSKWithBMPlarger1024GcurrentBMP_NUM_NUM_NUM_:Cannot "
730 : "create PCIDSK with a bitmap larger than 1024GB in size.\n"
731 : "The bitmap is %dp x %dl~= %6.1fMB.\n"),
732 : IDB->pixels, IDB->lines ,
733 : IDB->pixels * (double) IDB->lines
734 : / 1000000.0 ));
735 : }
736 : break
737 :
738 : case SEG_TEX:
739 : if( nBlocks < 66 )
740 : nBlocks = 66;
741 : break;
742 : #endif
743 :
744 : default:
745 : break;
746 : }
747 :
748 52 : if( data_blocks == 0 && expected_data_blocks != 0 )
749 1 : data_blocks = expected_data_blocks;
750 :
751 : /* -------------------------------------------------------------------- */
752 : /* Find an empty Segment Pointer. For System segments we start */
753 : /* at the end, instead of the beginning to avoid using up */
754 : /* segment numbers that the user would notice. */
755 : /* -------------------------------------------------------------------- */
756 52 : int segment = 1;
757 52 : int64 seg_start = -1;
758 52 : PCIDSKBuffer segptr( 32 );
759 :
760 52 : if( seg_type == SEG_SYS )
761 : {
762 17 : for( segment=segment_count; segment >= 1; segment-- )
763 : {
764 17 : memcpy( segptr.buffer, segment_pointers.buffer+(segment-1)*32, 32);
765 :
766 17 : uint64 this_seg_size = segptr.GetUInt64(23,9);
767 17 : char flag = (char) segptr.buffer[0];
768 :
769 17 : if( flag == 'D'
770 : && (uint64) data_blocks+2 == this_seg_size
771 : && this_seg_size > 0 )
772 0 : seg_start = segptr.GetUInt64(12,11) - 1;
773 17 : else if( flag == ' ' )
774 17 : seg_start = 0;
775 0 : else if( flag && this_seg_size == 0 )
776 0 : seg_start = 0;
777 :
778 17 : if( seg_start != -1 )
779 17 : break;
780 : }
781 : }
782 : else
783 : {
784 36 : for( segment=1; segment <= segment_count; segment++ )
785 : {
786 36 : memcpy( segptr.buffer, segment_pointers.buffer+(segment-1)*32, 32);
787 :
788 36 : uint64 this_seg_size = segptr.GetUInt64(23,9);
789 36 : char flag = (char) segptr.buffer[0];
790 :
791 36 : if( flag == 'D'
792 : && (uint64) data_blocks+2 == this_seg_size
793 : && this_seg_size > 0 )
794 0 : seg_start = segptr.GetUInt64(12,11) - 1;
795 36 : else if( flag == ' ' )
796 35 : seg_start = 0;
797 1 : else if( flag && this_seg_size == 0 )
798 0 : seg_start = 0;
799 :
800 36 : if( seg_start != -1 )
801 35 : break;
802 : }
803 : }
804 :
805 52 : if( segment > segment_count )
806 0 : ThrowPCIDSKException( "All %d segment pointers in use.", segment_count);
807 :
808 : /* -------------------------------------------------------------------- */
809 : /* If the segment does not have a data area already, identify */
810 : /* it's location at the end of the file, and extend the file to */
811 : /* the desired length. */
812 : /* -------------------------------------------------------------------- */
813 52 : if( seg_start == 0 )
814 : {
815 52 : seg_start = GetFileSize();
816 52 : ExtendFile( data_blocks + 2 );
817 : }
818 :
819 : /* -------------------------------------------------------------------- */
820 : /* Update the segment pointer information. */
821 : /* -------------------------------------------------------------------- */
822 : // SP1.1 - Flag
823 52 : segptr.Put( "A", 0, 1 );
824 :
825 : // SP1.2 - Type
826 52 : segptr.Put( (int) seg_type, 1, 3 );
827 :
828 : // SP1.3 - Name
829 52 : segptr.Put( name.c_str(), 4, 8 );
830 :
831 : // SP1.4 - start block
832 52 : segptr.Put( (uint64) (seg_start + 1), 12, 11 );
833 :
834 : // SP1.5 - data blocks.
835 52 : segptr.Put( data_blocks+2, 23, 9 );
836 :
837 : // Update in memory copy of segment pointers.
838 52 : memcpy( segment_pointers.buffer+(segment-1)*32, segptr.buffer, 32);
839 :
840 : // Update on disk.
841 : WriteToFile( segptr.buffer,
842 52 : segment_pointers_offset + (segment-1)*32, 32 );
843 :
844 : /* -------------------------------------------------------------------- */
845 : /* Prepare segment header. */
846 : /* -------------------------------------------------------------------- */
847 52 : PCIDSKBuffer sh(1024);
848 :
849 : char current_time[17];
850 52 : GetCurrentDateTime( current_time );
851 :
852 52 : sh.Put( " ", 0, 1024 );
853 :
854 : // SH1 - segment content description
855 52 : sh.Put( description.c_str(), 0, 64 );
856 :
857 : // SH3 - Creation time/date
858 52 : sh.Put( current_time, 128, 16 );
859 :
860 : // SH4 - Last Update time/date
861 52 : sh.Put( current_time, 144, 16 );
862 :
863 : /* -------------------------------------------------------------------- */
864 : /* Write segment header. */
865 : /* -------------------------------------------------------------------- */
866 52 : WriteToFile( sh.buffer, seg_start * 512, 1024 );
867 :
868 52 : return segment;
869 : }
870 :
871 : /************************************************************************/
872 : /* ExtendFile() */
873 : /************************************************************************/
874 :
875 69 : void CPCIDSKFile::ExtendFile( uint64 blocks_requested, bool prezero )
876 :
877 : {
878 69 : if( prezero )
879 : {
880 0 : std::vector<uint8> zeros;
881 0 : uint64 blocks_to_zero = blocks_requested;
882 :
883 0 : zeros.resize( 512 * 32 );
884 :
885 0 : while( blocks_to_zero > 0 )
886 : {
887 0 : uint64 this_time = blocks_to_zero;
888 0 : if( this_time > 32 )
889 0 : this_time = 32;
890 :
891 0 : WriteToFile( &(zeros[0]), file_size * 512, this_time*512 );
892 0 : blocks_to_zero -= this_time;
893 0 : file_size += this_time;
894 0 : }
895 : }
896 : else
897 : {
898 69 : WriteToFile( "\0", (file_size + blocks_requested) * 512 - 1, 1 );
899 69 : file_size += blocks_requested;
900 : }
901 :
902 69 : PCIDSKBuffer fh3( 16 );
903 69 : fh3.Put( file_size, 0, 16 );
904 69 : WriteToFile( fh3.buffer, 16, 16 );
905 69 : }
906 :
907 : /************************************************************************/
908 : /* ExtendSegment() */
909 : /************************************************************************/
910 :
911 17 : void CPCIDSKFile::ExtendSegment( int segment, uint64 blocks_requested,
912 : bool prezero )
913 :
914 : {
915 : // for now we take it for granted that the segment is valid and at th
916 : // end of the file - later we should support moving it.
917 :
918 17 : ExtendFile( blocks_requested, prezero );
919 :
920 : // Update the block count.
921 : segment_pointers.Put(
922 : segment_pointers.GetUInt64((segment-1)*32+23,9) + blocks_requested,
923 17 : (segment-1)*32+23, 9 );
924 :
925 : // write the updated segment pointer back to the file.
926 : WriteToFile( segment_pointers.buffer + (segment-1)*32,
927 : segment_pointers_offset + (segment-1)*32,
928 17 : 32 );
929 17 : }
930 :
931 : /************************************************************************/
932 : /* MoveSegmentToEOF() */
933 : /************************************************************************/
934 :
935 0 : void CPCIDSKFile::MoveSegmentToEOF( int segment )
936 :
937 : {
938 0 : int segptr_off = (segment - 1) * 32;
939 : uint64 seg_start, seg_size;
940 : uint64 new_seg_start;
941 :
942 0 : seg_start = segment_pointers.GetUInt64( segptr_off + 12, 11 );
943 0 : seg_size = segment_pointers.GetUInt64( segptr_off + 23, 9 );
944 :
945 : // Are we already at the end of the file?
946 0 : if( (seg_start + seg_size - 1) == file_size )
947 0 : return;
948 :
949 0 : new_seg_start = file_size + 1;
950 :
951 : // Grow the file to hold the segment at the end.
952 0 : ExtendFile( seg_size, false );
953 :
954 : // Move the segment data to the new location.
955 : uint8 copy_buf[16384];
956 : uint64 srcoff, dstoff, bytes_to_go;
957 :
958 0 : bytes_to_go = seg_size * 512;
959 0 : srcoff = (seg_start - 1) * 512;
960 0 : dstoff = (new_seg_start - 1) * 512;
961 :
962 0 : while( bytes_to_go > 0 )
963 : {
964 0 : uint64 bytes_this_chunk = sizeof(copy_buf);
965 0 : if( bytes_to_go < bytes_this_chunk )
966 0 : bytes_this_chunk = bytes_to_go;
967 :
968 0 : ReadFromFile( copy_buf, srcoff, bytes_this_chunk );
969 0 : WriteToFile( copy_buf, dstoff, bytes_this_chunk );
970 :
971 0 : srcoff += bytes_this_chunk;
972 0 : dstoff += bytes_this_chunk;
973 0 : bytes_to_go -= bytes_this_chunk;
974 : }
975 :
976 : // Update segment pointer in memory and on disk.
977 0 : segment_pointers.Put( new_seg_start, segptr_off + 12, 11 );
978 :
979 : WriteToFile( segment_pointers.buffer + segptr_off,
980 : segment_pointers_offset + segptr_off,
981 0 : 32 );
982 :
983 : // Update the segments own information.
984 0 : if( segments[segment] != NULL )
985 : {
986 : CPCIDSKSegment *seg =
987 0 : dynamic_cast<CPCIDSKSegment *>( segments[segment] );
988 :
989 0 : seg->LoadSegmentPointer( segment_pointers.buffer + segptr_off );
990 : }
991 : }
992 :
993 : /************************************************************************/
994 : /* CreateOverviews() */
995 : /************************************************************************/
996 : /*
997 : const char *pszResampling;
998 : Either "NEAREST" for Nearest Neighbour resampling (the fastest),
999 : "AVERAGE" for block averaging or "MODE" for block mode. This
1000 : establishing the type of resampling to be applied when preparing
1001 : the decimated overviews.
1002 : */
1003 :
1004 0 : void CPCIDSKFile::CreateOverviews( int chan_count, int *chan_list,
1005 : int factor, std::string resampling )
1006 :
1007 : {
1008 0 : std::vector<int> default_chan_list;
1009 :
1010 : /* -------------------------------------------------------------------- */
1011 : /* Validate resampling method. */
1012 : /* -------------------------------------------------------------------- */
1013 0 : UCaseStr( resampling );
1014 :
1015 0 : if( resampling != "NEAREST"
1016 : && resampling != "AVERAGE"
1017 : && resampling != "MODE" )
1018 : {
1019 : ThrowPCIDSKException( "Requested overview resampling '%s' not supported.\nUse one of NEAREST, AVERAGE or MODE.",
1020 0 : resampling.c_str() );
1021 : }
1022 :
1023 : /* -------------------------------------------------------------------- */
1024 : /* Default to processing all bands. */
1025 : /* -------------------------------------------------------------------- */
1026 0 : if( chan_count == 0 )
1027 : {
1028 0 : chan_count = channel_count;
1029 0 : default_chan_list.resize( chan_count );
1030 :
1031 0 : for( int i = 0; i < chan_count; i++ )
1032 0 : default_chan_list[i] = i+1;
1033 :
1034 0 : chan_list = &(default_chan_list[0]);
1035 : }
1036 :
1037 : /* -------------------------------------------------------------------- */
1038 : /* Work out the creation options that should apply for the */
1039 : /* overview. */
1040 : /* -------------------------------------------------------------------- */
1041 0 : std::string layout = GetMetadataValue( "_DBLayout" );
1042 0 : int blocksize = 127;
1043 0 : std::string compression = "NONE";
1044 :
1045 0 : if( strncmp( layout.c_str(), "TILED", 5 ) == 0 )
1046 : {
1047 0 : ParseTileFormat( layout, blocksize, compression );
1048 : }
1049 :
1050 : /* -------------------------------------------------------------------- */
1051 : /* Make sure we have a blockmap segment for managing the tiled */
1052 : /* layers. */
1053 : /* -------------------------------------------------------------------- */
1054 0 : PCIDSKSegment *bm_seg = GetSegment( SEG_SYS, "SysBMDir" );
1055 : SysBlockMap *bm;
1056 :
1057 0 : if( bm_seg == NULL )
1058 : {
1059 : CreateSegment( "SysBMDir",
1060 : "System Block Map Directory - Do not modify.",
1061 0 : SEG_SYS, 0 );
1062 0 : bm_seg = GetSegment( SEG_SYS, "SysBMDir" );
1063 0 : bm = dynamic_cast<SysBlockMap *>(bm_seg);
1064 0 : bm->Initialize();
1065 : }
1066 : else
1067 0 : bm = dynamic_cast<SysBlockMap *>(bm_seg);
1068 :
1069 : /* ==================================================================== */
1070 : /* Loop over the channels. */
1071 : /* ==================================================================== */
1072 0 : for( int chan_index = 0; chan_index < chan_count; chan_index++ )
1073 : {
1074 0 : int channel_number = chan_list[chan_index];
1075 0 : PCIDSKChannel *channel = GetChannel( channel_number );
1076 :
1077 : /* -------------------------------------------------------------------- */
1078 : /* Do we have a preexisting overview that corresponds to this */
1079 : /* factor? If so, throw an exception. Would it be better to */
1080 : /* just return quietly? */
1081 : /* -------------------------------------------------------------------- */
1082 0 : for( int i = channel->GetOverviewCount()-1; i >= 0; i-- )
1083 : {
1084 0 : PCIDSKChannel *overview = channel->GetOverview( i );
1085 :
1086 0 : if( overview->GetWidth() == channel->GetWidth() / factor
1087 0 : && overview->GetHeight() == channel->GetHeight() / factor )
1088 : {
1089 : ThrowPCIDSKException( "Channel %d already has a factor %d overview.",
1090 0 : channel_number, factor );
1091 : }
1092 : }
1093 :
1094 : /* -------------------------------------------------------------------- */
1095 : /* Create the overview as a tiled image layer. */
1096 : /* -------------------------------------------------------------------- */
1097 : int virtual_image =
1098 0 : bm->CreateVirtualImageFile( channel->GetWidth() / factor,
1099 0 : channel->GetHeight() / factor,
1100 : blocksize, blocksize,
1101 0 : channel->GetType(), compression );
1102 :
1103 : /* -------------------------------------------------------------------- */
1104 : /* Attach reference to this overview as metadata. */
1105 : /* -------------------------------------------------------------------- */
1106 : char overview_md_value[128];
1107 : char overview_md_key[128];
1108 :
1109 0 : sprintf( overview_md_key, "_Overview_%d", factor );
1110 0 : sprintf( overview_md_value, "%d 0 %s",virtual_image,resampling.c_str());
1111 :
1112 0 : channel->SetMetadataValue( overview_md_key, overview_md_value );
1113 :
1114 : /* -------------------------------------------------------------------- */
1115 : /* Force channel to invalidate it's loaded overview list. */
1116 : /* -------------------------------------------------------------------- */
1117 0 : dynamic_cast<CPCIDSKChannel *>(channel)->InvalidateOverviewInfo();
1118 0 : }
1119 1140 : }
|