Line data Source code
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, 90 Allstate Parkway, Markham, Ontario, 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 : #include <climits>
47 : #include <limits>
48 :
49 : using namespace PCIDSK;
50 :
51 : /************************************************************************/
52 : /* CBandInterleavedChannel() */
53 : /************************************************************************/
54 :
55 215 : CBandInterleavedChannel::CBandInterleavedChannel( PCIDSKBuffer &image_header,
56 : uint64 ih_offsetIn,
57 : CPL_UNUSED PCIDSKBuffer &file_header,
58 : int channelnum,
59 : CPCIDSKFile *fileIn,
60 : uint64 image_offset,
61 215 : eChanType pixel_typeIn )
62 215 : : CPCIDSKChannel( image_header, ih_offsetIn, fileIn, pixel_typeIn, channelnum)
63 :
64 : {
65 215 : io_handle_p = nullptr;
66 215 : io_mutex_p = nullptr;
67 :
68 : /* -------------------------------------------------------------------- */
69 : /* Establish the data layout. */
70 : /* -------------------------------------------------------------------- */
71 215 : if( strcmp(file->GetInterleaving().c_str(),"FILE") == 0 )
72 : {
73 3 : start_byte = atouint64(image_header.Get( 168, 16 ));
74 3 : pixel_offset = atouint64(image_header.Get( 184, 8 ));
75 3 : line_offset = atouint64(image_header.Get( 192, 8 ));
76 : }
77 : else
78 : {
79 212 : start_byte = image_offset;
80 212 : pixel_offset = DataTypeSize(pixel_type);
81 212 : line_offset = pixel_offset * width;
82 : }
83 :
84 : /* -------------------------------------------------------------------- */
85 : /* Establish the file we will be accessing. */
86 : /* -------------------------------------------------------------------- */
87 215 : image_header.Get(64,64,filename);
88 :
89 215 : filename = MassageLink( filename );
90 :
91 215 : if( filename.length() == 0 )
92 212 : file->GetIODetails( &io_handle_p, &io_mutex_p );
93 :
94 : else
95 9 : filename = file->GetInterfaces()->MergeRelativePath( file->GetInterfaces()->io,
96 3 : file->GetFilename(),
97 6 : filename );
98 215 : }
99 :
100 : /************************************************************************/
101 : /* ~CBandInterleavedChannel() */
102 : /************************************************************************/
103 :
104 430 : CBandInterleavedChannel::~CBandInterleavedChannel()
105 :
106 : {
107 430 : }
108 :
109 : /************************************************************************/
110 : /* ReadBlock() */
111 : /************************************************************************/
112 :
113 232 : int CBandInterleavedChannel::ReadBlock( int block_index, void *buffer,
114 : int xoff, int yoff,
115 : int xsize, int ysize )
116 :
117 : {
118 232 : PCIDSKInterfaces *interfaces = file->GetInterfaces();
119 :
120 : /* -------------------------------------------------------------------- */
121 : /* Check if we are reading from a valid channel. */
122 : /* -------------------------------------------------------------------- */
123 232 : if (line_offset > std::numeric_limits<uint64>::max() / height)
124 : {
125 0 : return ThrowPCIDSKException(0, "Invalid line_offset: " PCIDSK_FRMT_UINT64,
126 0 : line_offset);
127 : }
128 :
129 232 : if (start_byte > std::numeric_limits<uint64>::max() - line_offset * height)
130 : {
131 0 : return ThrowPCIDSKException(0, "Invalid start_byte: " PCIDSK_FRMT_UINT64,
132 0 : start_byte);
133 : }
134 :
135 : /* -------------------------------------------------------------------- */
136 : /* Default window if needed. */
137 : /* -------------------------------------------------------------------- */
138 232 : if( xoff == -1 && yoff == -1 && xsize == -1 && ysize == -1 )
139 : {
140 232 : xoff = 0;
141 232 : yoff = 0;
142 232 : xsize = GetBlockWidth();
143 232 : ysize = GetBlockHeight();
144 : }
145 :
146 : /* -------------------------------------------------------------------- */
147 : /* Validate Window */
148 : /* -------------------------------------------------------------------- */
149 232 : if( xoff < 0 || xoff + xsize > GetBlockWidth()
150 464 : || yoff < 0 || yoff + ysize > GetBlockHeight() )
151 : {
152 0 : return ThrowPCIDSKException( 0,
153 : "Invalid window in ReadBlock(): xoff=%d,yoff=%d,xsize=%d,ysize=%d",
154 0 : xoff, yoff, xsize, ysize );
155 : }
156 :
157 : /* -------------------------------------------------------------------- */
158 : /* Establish region to read. */
159 : /* -------------------------------------------------------------------- */
160 232 : int pixel_size = DataTypeSize( pixel_type );
161 :
162 232 : if (pixel_offset == 0 || pixel_size == 0)
163 : {
164 0 : return ThrowPCIDSKException( 0, "Invalid data type." );
165 : }
166 232 : if( xsize > 1 && pixel_offset > static_cast<uint64>(INT_MAX / (xsize - 1)) )
167 : {
168 0 : return ThrowPCIDSKException( 0, "Int overflow in ReadBlock() ");
169 : }
170 232 : if( pixel_offset*(xsize-1) > static_cast<uint64>(INT_MAX - pixel_size) )
171 : {
172 0 : return ThrowPCIDSKException( 0, "Int overflow in ReadBlock() ");
173 : }
174 232 : int window_size = (int) (pixel_offset*(xsize-1) + pixel_size);
175 :
176 : /* -------------------------------------------------------------------- */
177 : /* Get file access handles if we don't already have them. */
178 : /* -------------------------------------------------------------------- */
179 232 : if( io_handle_p == nullptr )
180 1 : file->GetIODetails( &io_handle_p, &io_mutex_p, filename.c_str(),
181 1 : file->GetUpdatable() );
182 :
183 : /* -------------------------------------------------------------------- */
184 : /* If the imagery is packed, we can read directly into the */
185 : /* target buffer. */
186 : /* -------------------------------------------------------------------- */
187 232 : uint64 offset = start_byte + line_offset * block_index
188 232 : + pixel_offset * xoff;
189 :
190 232 : if( pixel_size == (int) pixel_offset )
191 : {
192 464 : MutexHolder holder( *io_mutex_p );
193 :
194 232 : interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
195 232 : interfaces->io->Read( buffer, 1, window_size, *io_handle_p );
196 : }
197 :
198 : /* -------------------------------------------------------------------- */
199 : /* Otherwise we allocate a working buffer that holds the whole */
200 : /* line, read into that, and pick out our data of interest. */
201 : /* -------------------------------------------------------------------- */
202 : else
203 : {
204 : int i;
205 0 : PCIDSKBuffer line_from_disk( window_size );
206 : char *this_pixel;
207 :
208 0 : MutexHolder holder( *io_mutex_p );
209 :
210 0 : interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
211 0 : interfaces->io->Read( line_from_disk.buffer,
212 0 : 1, line_from_disk.buffer_size,
213 0 : *io_handle_p );
214 :
215 0 : for( i = 0, this_pixel = line_from_disk.buffer; i < xsize; i++ )
216 : {
217 0 : memcpy( ((char *) buffer) + pixel_size * i,
218 : this_pixel, pixel_size );
219 0 : this_pixel += pixel_offset;
220 : }
221 : }
222 :
223 : /* -------------------------------------------------------------------- */
224 : /* Do byte swapping if needed. */
225 : /* -------------------------------------------------------------------- */
226 232 : if( needs_swap )
227 20 : SwapPixels( buffer, pixel_type, xsize );
228 :
229 232 : return 1;
230 : }
231 :
232 : /************************************************************************/
233 : /* WriteBlock() */
234 : /************************************************************************/
235 :
236 508 : int CBandInterleavedChannel::WriteBlock( int block_index, void *buffer )
237 :
238 : {
239 508 : PCIDSKInterfaces *interfaces = file->GetInterfaces();
240 :
241 : /* -------------------------------------------------------------------- */
242 : /* Check if we are writing to a valid channel. */
243 : /* -------------------------------------------------------------------- */
244 508 : if (line_offset > std::numeric_limits<uint64>::max() / height)
245 : {
246 0 : return ThrowPCIDSKException(0, "Invalid line_offset: " PCIDSK_FRMT_UINT64,
247 0 : line_offset);
248 : }
249 :
250 508 : if (pixel_offset > line_offset)
251 : {
252 0 : return ThrowPCIDSKException(0, "Invalid pixel_offset: " PCIDSK_FRMT_UINT64,
253 0 : pixel_offset);
254 : }
255 :
256 508 : if (start_byte > std::numeric_limits<uint64>::max() - line_offset * height)
257 : {
258 0 : return ThrowPCIDSKException(0, "Invalid start_byte: " PCIDSK_FRMT_UINT64,
259 0 : start_byte);
260 : }
261 :
262 508 : if( !file->GetUpdatable() )
263 0 : return ThrowPCIDSKException(0, "File not open for update in WriteBlock()" );
264 :
265 508 : InvalidateOverviews();
266 :
267 : /* -------------------------------------------------------------------- */
268 : /* Establish region to read. */
269 : /* -------------------------------------------------------------------- */
270 508 : int pixel_size = DataTypeSize( pixel_type );
271 :
272 508 : if (pixel_offset == 0 || pixel_size == 0)
273 0 : return ThrowPCIDSKException( 0, "Invalid data type." );
274 :
275 508 : uint64 offset = start_byte + line_offset * block_index;
276 508 : int window_size = (int) (pixel_offset*(width-1) + pixel_size);
277 :
278 : /* -------------------------------------------------------------------- */
279 : /* Get file access handles if we don't already have them. */
280 : /* -------------------------------------------------------------------- */
281 508 : if( io_handle_p == nullptr )
282 1 : file->GetIODetails( &io_handle_p, &io_mutex_p, filename.c_str(),
283 1 : file->GetUpdatable() );
284 :
285 : /* -------------------------------------------------------------------- */
286 : /* If the imagery is packed, we can read directly into the */
287 : /* target buffer. */
288 : /* -------------------------------------------------------------------- */
289 508 : if( pixel_size == (int) pixel_offset )
290 : {
291 1016 : MutexHolder holder( *io_mutex_p );
292 :
293 508 : if( needs_swap ) // swap before write.
294 70 : SwapPixels( buffer, pixel_type, width );
295 :
296 508 : interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
297 508 : interfaces->io->Write( buffer, 1, window_size, *io_handle_p );
298 :
299 508 : if( needs_swap ) // restore to original order.
300 70 : SwapPixels( buffer, pixel_type, width );
301 : }
302 :
303 : /* -------------------------------------------------------------------- */
304 : /* Otherwise we allocate a working buffer that holds the whole */
305 : /* line, read into that, and pick out our data of interest. */
306 : /* -------------------------------------------------------------------- */
307 : else
308 : {
309 : int i;
310 0 : PCIDSKBuffer line_from_disk( window_size );
311 : char *this_pixel;
312 :
313 0 : MutexHolder holder( *io_mutex_p );
314 :
315 0 : interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
316 0 : interfaces->io->Read( buffer, 1, line_from_disk.buffer_size,
317 0 : *io_handle_p );
318 :
319 0 : for( i = 0, this_pixel = line_from_disk.buffer; i < width; i++ )
320 : {
321 0 : memcpy( this_pixel, ((char *) buffer) + pixel_size * i,
322 : pixel_size );
323 :
324 0 : if( needs_swap ) // swap before write.
325 0 : SwapPixels( this_pixel, pixel_type, 1 );
326 :
327 0 : this_pixel += pixel_size;
328 : }
329 :
330 0 : interfaces->io->Seek( *io_handle_p, offset, SEEK_SET );
331 0 : interfaces->io->Write( buffer, 1, line_from_disk.buffer_size,
332 0 : *io_handle_p );
333 : }
334 :
335 : /* -------------------------------------------------------------------- */
336 : /* Do byte swapping if needed. */
337 : /* -------------------------------------------------------------------- */
338 :
339 508 : return 1;
340 : }
341 :
342 : /************************************************************************/
343 : /* GetChanInfo() */
344 : /************************************************************************/
345 13 : void CBandInterleavedChannel
346 : ::GetChanInfo( std::string &filename_ret, uint64 &image_offset,
347 : uint64 &pixel_offsetOut, uint64 &line_offsetOut,
348 : bool &little_endian ) const
349 :
350 : {
351 13 : image_offset = start_byte;
352 13 : pixel_offsetOut = this->pixel_offset;
353 13 : line_offsetOut = this->line_offset;
354 13 : little_endian = (byte_order == 'S');
355 :
356 : /* -------------------------------------------------------------------- */
357 : /* We fetch the filename from the header since it will be the */
358 : /* "clean" version without any paths. */
359 : /* -------------------------------------------------------------------- */
360 13 : PCIDSKBuffer ih(64);
361 13 : file->ReadFromFile( ih.buffer, ih_offset+64, 64 );
362 :
363 13 : ih.Get(0,64,filename_ret);
364 13 : filename_ret = MassageLink( filename_ret );
365 13 : }
366 :
367 : /************************************************************************/
368 : /* SetChanInfo() */
369 : /************************************************************************/
370 :
371 1 : void CBandInterleavedChannel
372 : ::SetChanInfo( std::string filenameIn, uint64 image_offset,
373 : uint64 pixel_offsetIn, uint64 line_offsetIn,
374 : bool little_endian )
375 :
376 : {
377 1 : if( ih_offset == 0 )
378 0 : return ThrowPCIDSKException( "No Image Header available for this channel." );
379 :
380 : /* -------------------------------------------------------------------- */
381 : /* Fetch the existing image header. */
382 : /* -------------------------------------------------------------------- */
383 2 : PCIDSKBuffer ih(1024);
384 :
385 1 : file->ReadFromFile( ih.buffer, ih_offset, 1024 );
386 :
387 : /* -------------------------------------------------------------------- */
388 : /* If the linked filename is too long to fit in the 64 */
389 : /* character IHi.2 field, then we need to use a link segment to */
390 : /* store the filename. */
391 : /* -------------------------------------------------------------------- */
392 2 : std::string IHi2_filename;
393 :
394 1 : if( filenameIn.size() > 64 )
395 : {
396 : int link_segment;
397 :
398 0 : ih.Get( 64, 64, IHi2_filename );
399 :
400 0 : if( IHi2_filename.substr(0,3) == "LNK" )
401 : {
402 0 : link_segment = std::atoi( IHi2_filename.c_str() + 4 );
403 : }
404 : else
405 : {
406 : char link_filename[64];
407 :
408 : link_segment =
409 0 : file->CreateSegment( "Link ",
410 : "Long external channel filename link.",
411 : SEG_SYS, 1 );
412 :
413 0 : snprintf( link_filename, sizeof(link_filename), "LNK %4d", link_segment );
414 0 : IHi2_filename = link_filename;
415 : }
416 :
417 : CLinkSegment *link =
418 0 : dynamic_cast<CLinkSegment*>( file->GetSegment( link_segment ) );
419 :
420 0 : if( link != nullptr )
421 : {
422 0 : link->SetPath( filenameIn );
423 0 : link->Synchronize();
424 : }
425 : }
426 :
427 : /* -------------------------------------------------------------------- */
428 : /* If we used to have a link segment but no longer need it, we */
429 : /* need to delete the link segment. */
430 : /* -------------------------------------------------------------------- */
431 : else
432 : {
433 1 : ih.Get( 64, 64, IHi2_filename );
434 :
435 1 : if( IHi2_filename.substr(0,3) == "LNK" )
436 : {
437 0 : int link_segment = std::atoi( IHi2_filename.c_str() + 4 );
438 :
439 0 : file->DeleteSegment( link_segment );
440 : }
441 :
442 1 : IHi2_filename = filenameIn;
443 : }
444 :
445 : /* -------------------------------------------------------------------- */
446 : /* Update the image header. */
447 : /* -------------------------------------------------------------------- */
448 : // IHi.2
449 1 : ih.Put( IHi2_filename.c_str(), 64, 64 );
450 :
451 : // IHi.6.1
452 1 : ih.Put( image_offset, 168, 16 );
453 :
454 : // IHi.6.2
455 1 : ih.Put( pixel_offsetIn, 184, 8 );
456 :
457 : // IHi.6.3
458 1 : ih.Put( line_offsetIn, 192, 8 );
459 :
460 : // IHi.6.5
461 1 : if( little_endian )
462 1 : ih.Put( "S", 201, 1 );
463 : else
464 0 : ih.Put( "N", 201, 1 );
465 :
466 1 : file->WriteToFile( ih.buffer, ih_offset, 1024 );
467 :
468 : /* -------------------------------------------------------------------- */
469 : /* Update local configuration. */
470 : /* -------------------------------------------------------------------- */
471 3 : this->filename = file->GetInterfaces()->MergeRelativePath( file->GetInterfaces()->io,
472 2 : file->GetFilename(),
473 1 : filenameIn );
474 :
475 1 : start_byte = image_offset;
476 1 : this->pixel_offset = pixel_offsetIn;
477 1 : this->line_offset = line_offsetIn;
478 :
479 1 : if( little_endian )
480 1 : byte_order = 'S';
481 : else
482 0 : byte_order = 'N';
483 :
484 : /* -------------------------------------------------------------------- */
485 : /* Determine if we need byte swapping. */
486 : /* -------------------------------------------------------------------- */
487 1 : unsigned short test_value = 1;
488 :
489 1 : if( ((uint8 *) &test_value)[0] == 1 )
490 1 : needs_swap = (byte_order != 'S');
491 : else
492 0 : needs_swap = (byte_order == 'S');
493 :
494 1 : if( pixel_type == CHN_8U )
495 0 : needs_swap = 0;
496 : }
497 :
498 : /************************************************************************/
499 : /* MassageLink() */
500 : /* */
501 : /* Return the filename after applying translation of long */
502 : /* linked filenames using a link segment. */
503 : /************************************************************************/
504 :
505 228 : std::string CBandInterleavedChannel::MassageLink( std::string filename_in ) const
506 :
507 : {
508 228 : if (filename_in.find("LNK") == 0)
509 : {
510 0 : std::string seg_str(filename_in, 4, 4);
511 0 : unsigned int seg_num = std::atoi(seg_str.c_str());
512 :
513 0 : if (seg_num == 0)
514 : {
515 0 : ThrowPCIDSKException("Unable to find link segment. Link name: %s",
516 : filename_in.c_str());
517 0 : return "";
518 : }
519 :
520 : CLinkSegment* link_seg =
521 0 : dynamic_cast<CLinkSegment*>(file->GetSegment(seg_num));
522 0 : if (link_seg == nullptr)
523 : {
524 0 : ThrowPCIDSKException("Failed to get Link Information Segment.");
525 0 : return "";
526 : }
527 :
528 0 : filename_in = link_seg->GetPath();
529 : }
530 :
531 228 : return filename_in;
532 : }
|