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