1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the CBandInterleavedChannel class.
4 : *
5 : * This class is used to implement band interleaved channels within a
6 : * PCIDSK file (which are always packed, and FILE interleaved data from
7 : * external raw files which may not be packed.
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2009
11 : * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada
12 : *
13 : * Permission is hereby granted, free of charge, to any person obtaining a
14 : * copy of this software and associated documentation files (the "Software"),
15 : * to deal in the Software without restriction, including without limitation
16 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 : * and/or sell copies of the Software, and to permit persons to whom the
18 : * Software is furnished to do so, subject to the following conditions:
19 : *
20 : * The above copyright notice and this permission notice shall be included
21 : * in all copies or substantial portions of the Software.
22 : *
23 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 : * DEALINGS IN THE SOFTWARE.
30 : ****************************************************************************/
31 :
32 : #include "pcidsk_config.h"
33 : #include "pcidsk_types.h"
34 : #include "pcidsk_channel.h"
35 : #include "pcidsk_buffer.h"
36 : #include "pcidsk_exception.h"
37 : #include "pcidsk_file.h"
38 : #include "core/pcidsk_utils.h"
39 : #include "core/cpcidskfile.h"
40 : #include "core/clinksegment.h"
41 : #include "channel/cbandinterleavedchannel.h"
42 : #include <cassert>
43 : #include <cstring>
44 : #include <cstdio>
45 : #include <cstdlib>
46 :
47 : using namespace PCIDSK;
48 :
49 : /************************************************************************/
50 : /* CBandInterleavedChannel() */
51 : /************************************************************************/
52 :
53 172 : CBandInterleavedChannel::CBandInterleavedChannel( PCIDSKBuffer &image_header,
54 : uint64 ih_offset,
55 : PCIDSKBuffer &file_header,
56 : int channelnum,
57 : CPCIDSKFile *file,
58 : uint64 image_offset,
59 : eChanType pixel_type )
60 172 : : CPCIDSKChannel( image_header, ih_offset, file, pixel_type, channelnum)
61 :
62 : {
63 172 : io_handle_p = NULL;
64 172 : io_mutex_p = NULL;
65 :
66 : /* -------------------------------------------------------------------- */
67 : /* Establish the data layout. */
68 : /* -------------------------------------------------------------------- */
69 172 : if( strcmp(file->GetInterleaving().c_str(),"FILE") == 0 )
70 : {
71 3 : start_byte = atouint64(image_header.Get( 168, 16 ));
72 3 : pixel_offset = atouint64(image_header.Get( 184, 8 ));
73 3 : line_offset = atouint64(image_header.Get( 192, 8 ));
74 : }
75 : else
76 : {
77 169 : start_byte = image_offset;
78 169 : pixel_offset = DataTypeSize(pixel_type);
79 169 : line_offset = pixel_offset * width;
80 : }
81 :
82 : /* -------------------------------------------------------------------- */
83 : /* Establish the file we will be accessing. */
84 : /* -------------------------------------------------------------------- */
85 172 : image_header.Get(64,64,filename);
86 :
87 172 : filename = MassageLink( filename );
88 :
89 172 : if( filename.length() == 0 )
90 169 : file->GetIODetails( &io_handle_p, &io_mutex_p );
91 :
92 : else
93 3 : filename = MergeRelativePath( file->GetInterfaces()->io,
94 : file->GetFilename(),
95 6 : filename );
96 172 : }
97 :
98 : /************************************************************************/
99 : /* ~CBandInterleavedChannel() */
100 : /************************************************************************/
101 :
102 172 : CBandInterleavedChannel::~CBandInterleavedChannel()
103 :
104 : {
105 172 : }
106 :
107 : /************************************************************************/
108 : /* ReadBlock() */
109 : /************************************************************************/
110 :
111 61 : int CBandInterleavedChannel::ReadBlock( int block_index, void *buffer,
112 : int xoff, int yoff,
113 : int xsize, int ysize )
114 :
115 : {
116 61 : PCIDSKInterfaces *interfaces = file->GetInterfaces();
117 :
118 : /* -------------------------------------------------------------------- */
119 : /* Default window if needed. */
120 : /* -------------------------------------------------------------------- */
121 61 : if( xoff == -1 && yoff == -1 && xsize == -1 && ysize == -1 )
122 : {
123 61 : xoff = 0;
124 61 : yoff = 0;
125 61 : xsize = GetBlockWidth();
126 61 : ysize = GetBlockHeight();
127 : }
128 :
129 : /* -------------------------------------------------------------------- */
130 : /* Validate Window */
131 : /* -------------------------------------------------------------------- */
132 122 : if( xoff < 0 || xoff + xsize > GetBlockWidth()
133 61 : || yoff < 0 || yoff + ysize > GetBlockHeight() )
134 : {
135 : ThrowPCIDSKException(
136 : "Invalid window in ReadBloc(): xoff=%d,yoff=%d,xsize=%d,ysize=%d",
137 0 : xoff, yoff, xsize, ysize );
138 : }
139 :
140 : /* -------------------------------------------------------------------- */
141 : /* Establish region to read. */
142 : /* -------------------------------------------------------------------- */
143 61 : int pixel_size = DataTypeSize( pixel_type );
144 : uint64 offset = start_byte + line_offset * block_index
145 61 : + pixel_offset * xoff;
146 61 : int window_size = (int) (pixel_offset*(xsize-1) + pixel_size);
147 :
148 : /* -------------------------------------------------------------------- */
149 : /* Get file access handles if we don't already have them. */
150 : /* -------------------------------------------------------------------- */
151 61 : if( io_handle_p == NULL )
152 : file->GetIODetails( &io_handle_p, &io_mutex_p, filename.c_str(),
153 1 : file->GetUpdatable() );
154 :
155 : /* -------------------------------------------------------------------- */
156 : /* If the imagery is packed, we can read directly into the */
157 : /* target buffer. */
158 : /* -------------------------------------------------------------------- */
159 61 : if( pixel_size == (int) pixel_offset )
160 : {
161 61 : MutexHolder holder( *io_mutex_p );
162 :
163 61 : interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
164 61 : interfaces->io->Read( buffer, 1, window_size, *io_handle_p );
165 : }
166 :
167 : /* -------------------------------------------------------------------- */
168 : /* Otherwise we allocate a working buffer that holds the whole */
169 : /* line, read into that, and pick out our data of interest. */
170 : /* -------------------------------------------------------------------- */
171 : else
172 : {
173 : int i;
174 0 : PCIDSKBuffer line_from_disk( window_size );
175 : char *this_pixel;
176 :
177 0 : MutexHolder holder( *io_mutex_p );
178 :
179 0 : interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
180 : interfaces->io->Read( line_from_disk.buffer,
181 : 1, line_from_disk.buffer_size,
182 0 : *io_handle_p );
183 :
184 0 : for( i = 0, this_pixel = line_from_disk.buffer; i < xsize; i++ )
185 : {
186 : memcpy( ((char *) buffer) + pixel_size * i,
187 0 : this_pixel, pixel_size );
188 0 : this_pixel += pixel_size;
189 0 : }
190 : }
191 :
192 : /* -------------------------------------------------------------------- */
193 : /* Do byte swapping if needed. */
194 : /* -------------------------------------------------------------------- */
195 61 : if( needs_swap )
196 20 : SwapPixels( buffer, pixel_type, xsize );
197 :
198 61 : return 1;
199 : }
200 :
201 : /************************************************************************/
202 : /* WriteBlock() */
203 : /************************************************************************/
204 :
205 317 : int CBandInterleavedChannel::WriteBlock( int block_index, void *buffer )
206 :
207 : {
208 317 : PCIDSKInterfaces *interfaces = file->GetInterfaces();
209 :
210 317 : if( !file->GetUpdatable() )
211 0 : throw PCIDSKException( "File not open for update in WriteBlock()" );
212 :
213 317 : InvalidateOverviews();
214 :
215 : /* -------------------------------------------------------------------- */
216 : /* Establish region to read. */
217 : /* -------------------------------------------------------------------- */
218 317 : int pixel_size = DataTypeSize( pixel_type );
219 317 : uint64 offset = start_byte + line_offset * block_index;
220 317 : int window_size = (int) (pixel_offset*(width-1) + pixel_size);
221 :
222 : /* -------------------------------------------------------------------- */
223 : /* Get file access handles if we don't already have them. */
224 : /* -------------------------------------------------------------------- */
225 317 : if( io_handle_p == NULL )
226 : file->GetIODetails( &io_handle_p, &io_mutex_p, filename.c_str(),
227 1 : file->GetUpdatable() );
228 :
229 : /* -------------------------------------------------------------------- */
230 : /* If the imagery is packed, we can read directly into the */
231 : /* target buffer. */
232 : /* -------------------------------------------------------------------- */
233 317 : if( pixel_size == (int) pixel_offset )
234 : {
235 317 : MutexHolder holder( *io_mutex_p );
236 :
237 317 : if( needs_swap ) // swap before write.
238 70 : SwapPixels( buffer, pixel_type, width );
239 :
240 317 : interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
241 317 : interfaces->io->Write( buffer, 1, window_size, *io_handle_p );
242 :
243 317 : if( needs_swap ) // restore to original order.
244 70 : SwapPixels( buffer, pixel_type, width );
245 : }
246 :
247 : /* -------------------------------------------------------------------- */
248 : /* Otherwise we allocate a working buffer that holds the whole */
249 : /* line, read into that, and pick out our data of interest. */
250 : /* -------------------------------------------------------------------- */
251 : else
252 : {
253 : int i;
254 0 : PCIDSKBuffer line_from_disk( window_size );
255 : char *this_pixel;
256 :
257 0 : MutexHolder holder( *io_mutex_p );
258 :
259 0 : interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
260 : interfaces->io->Read( buffer, 1, line_from_disk.buffer_size,
261 0 : *io_handle_p );
262 :
263 0 : for( i = 0, this_pixel = line_from_disk.buffer; i < width; i++ )
264 : {
265 : memcpy( this_pixel, ((char *) buffer) + pixel_size * i,
266 0 : pixel_size );
267 :
268 0 : if( needs_swap ) // swap before write.
269 0 : SwapPixels( this_pixel, pixel_type, 1 );
270 :
271 0 : this_pixel += pixel_size;
272 : }
273 :
274 0 : interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
275 : interfaces->io->Write( buffer, 1, line_from_disk.buffer_size,
276 0 : *io_handle_p );
277 : }
278 :
279 : /* -------------------------------------------------------------------- */
280 : /* Do byte swapping if needed. */
281 : /* -------------------------------------------------------------------- */
282 :
283 317 : return 1;
284 : }
285 :
286 : /************************************************************************/
287 : /* GetChanInfo() */
288 : /************************************************************************/
289 5 : void CBandInterleavedChannel
290 : ::GetChanInfo( std::string &filename_ret, uint64 &image_offset,
291 : uint64 &pixel_offset, uint64 &line_offset,
292 : bool &little_endian ) const
293 :
294 : {
295 5 : image_offset = start_byte;
296 5 : pixel_offset = this->pixel_offset;
297 5 : line_offset = this->line_offset;
298 5 : little_endian = (byte_order == 'S');
299 :
300 : /* -------------------------------------------------------------------- */
301 : /* We fetch the filename from the header since it will be the */
302 : /* "clean" version without any paths. */
303 : /* -------------------------------------------------------------------- */
304 5 : PCIDSKBuffer ih(64);
305 5 : file->ReadFromFile( ih.buffer, ih_offset+64, 64 );
306 :
307 5 : ih.Get(0,64,filename_ret);
308 5 : filename_ret = MassageLink( filename_ret );
309 5 : }
310 :
311 : /************************************************************************/
312 : /* SetChanInfo() */
313 : /************************************************************************/
314 :
315 1 : void CBandInterleavedChannel
316 : ::SetChanInfo( std::string filename, uint64 image_offset,
317 : uint64 pixel_offset, uint64 line_offset,
318 : bool little_endian )
319 :
320 : {
321 1 : if( ih_offset == 0 )
322 0 : ThrowPCIDSKException( "No Image Header available for this channel." );
323 :
324 : /* -------------------------------------------------------------------- */
325 : /* Fetch the existing image header. */
326 : /* -------------------------------------------------------------------- */
327 1 : PCIDSKBuffer ih(1024);
328 :
329 1 : file->ReadFromFile( ih.buffer, ih_offset, 1024 );
330 :
331 : /* -------------------------------------------------------------------- */
332 : /* If the linked filename is too long to fit in the 64 */
333 : /* character IHi.2 field, then we need to use a link segment to */
334 : /* store the filename. */
335 : /* -------------------------------------------------------------------- */
336 1 : std::string IHi2_filename;
337 :
338 1 : if( filename.size() > 64 )
339 : {
340 : int link_segment;
341 :
342 0 : ih.Get( 64, 64, IHi2_filename );
343 :
344 0 : if( IHi2_filename.substr(0,3) == "LNK" )
345 : {
346 0 : link_segment = std::atoi( IHi2_filename.c_str() + 4 );
347 : }
348 : else
349 : {
350 : char link_filename[64];
351 :
352 : link_segment =
353 : file->CreateSegment( "Link ",
354 : "Long external channel filename link.",
355 0 : SEG_SYS, 1 );
356 :
357 0 : sprintf( link_filename, "LNK %4d", link_segment );
358 0 : IHi2_filename = link_filename;
359 : }
360 :
361 : CLinkSegment *link =
362 0 : dynamic_cast<CLinkSegment*>( file->GetSegment( link_segment ) );
363 :
364 0 : if( link != NULL )
365 : {
366 0 : link->SetPath( filename );
367 0 : link->Synchronize();
368 : }
369 : }
370 :
371 : /* -------------------------------------------------------------------- */
372 : /* If we used to have a link segment but no longer need it, we */
373 : /* need to delete the link segment. */
374 : /* -------------------------------------------------------------------- */
375 : else
376 : {
377 1 : ih.Get( 64, 64, IHi2_filename );
378 :
379 1 : if( IHi2_filename.substr(0,3) == "LNK" )
380 : {
381 0 : int link_segment = std::atoi( IHi2_filename.c_str() + 4 );
382 :
383 0 : file->DeleteSegment( link_segment );
384 : }
385 :
386 1 : IHi2_filename = filename;
387 : }
388 :
389 : /* -------------------------------------------------------------------- */
390 : /* Update the image header. */
391 : /* -------------------------------------------------------------------- */
392 : // IHi.2
393 1 : ih.Put( IHi2_filename.c_str(), 64, 64 );
394 :
395 : // IHi.6.1
396 1 : ih.Put( image_offset, 168, 16 );
397 :
398 : // IHi.6.2
399 1 : ih.Put( pixel_offset, 184, 8 );
400 :
401 : // IHi.6.3
402 1 : ih.Put( line_offset, 192, 8 );
403 :
404 : // IHi.6.5
405 1 : if( little_endian )
406 1 : ih.Put( "S", 201, 1 );
407 : else
408 0 : ih.Put( "N", 201, 1 );
409 :
410 1 : file->WriteToFile( ih.buffer, ih_offset, 1024 );
411 :
412 : /* -------------------------------------------------------------------- */
413 : /* Update local configuration. */
414 : /* -------------------------------------------------------------------- */
415 1 : this->filename = MergeRelativePath( file->GetInterfaces()->io,
416 : file->GetFilename(),
417 2 : filename );
418 :
419 1 : start_byte = image_offset;
420 1 : this->pixel_offset = pixel_offset;
421 1 : this->line_offset = line_offset;
422 :
423 1 : if( little_endian )
424 1 : byte_order = 'S';
425 : else
426 0 : byte_order = 'N';
427 :
428 : /* -------------------------------------------------------------------- */
429 : /* Determine if we need byte swapping. */
430 : /* -------------------------------------------------------------------- */
431 1 : unsigned short test_value = 1;
432 :
433 1 : if( ((uint8 *) &test_value)[0] == 1 )
434 1 : needs_swap = (byte_order != 'S');
435 : else
436 0 : needs_swap = (byte_order == 'S');
437 :
438 1 : if( pixel_type == CHN_8U )
439 0 : needs_swap = 0;
440 1 : }
441 :
442 : /************************************************************************/
443 : /* MassageLink() */
444 : /* */
445 : /* Return the filename after applying translation of long */
446 : /* linked filenames using a link segment. */
447 : /************************************************************************/
448 :
449 177 : std::string CBandInterleavedChannel::MassageLink( std::string filename_in ) const
450 :
451 : {
452 177 : if (filename_in.find("LNK") == 0)
453 : {
454 0 : std::string seg_str(filename_in, 4, 4);
455 0 : unsigned int seg_num = std::atoi(seg_str.c_str());
456 :
457 0 : if (seg_num == 0)
458 : {
459 : throw PCIDSKException("Unable to find link segment. Link name: %s",
460 0 : filename_in.c_str());
461 : }
462 :
463 : CLinkSegment* link_seg =
464 0 : dynamic_cast<CLinkSegment*>(file->GetSegment(seg_num));
465 0 : if (link_seg == NULL)
466 : {
467 0 : throw PCIDSKException("Failed to get Link Information Segment.");
468 : }
469 :
470 0 : filename_in = link_seg->GetPath();
471 : }
472 :
473 177 : return filename_in;
474 : }
475 :
|