/src/gdal/frmts/pcidsk/sdk/channel/cbandinterleavedchannel.cpp
Line | Count | Source |
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 | | 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 | | eChanType pixel_typeIn ) |
46 | 795k | : CPCIDSKChannel( image_header, ih_offsetIn, fileIn, pixel_typeIn, channelnum) |
47 | | |
48 | 795k | { |
49 | 795k | io_handle_p = nullptr; |
50 | 795k | io_mutex_p = nullptr; |
51 | | |
52 | | /* -------------------------------------------------------------------- */ |
53 | | /* Establish the data layout. */ |
54 | | /* -------------------------------------------------------------------- */ |
55 | 795k | if( strcmp(file->GetInterleaving().c_str(),"FILE") == 0 ) |
56 | 4 | { |
57 | 4 | start_byte = atouint64(image_header.Get( 168, 16 )); |
58 | 4 | pixel_offset = atouint64(image_header.Get( 184, 8 )); |
59 | 4 | line_offset = atouint64(image_header.Get( 192, 8 )); |
60 | 4 | } |
61 | 795k | else |
62 | 795k | { |
63 | 795k | start_byte = image_offset; |
64 | 795k | pixel_offset = DataTypeSize(pixel_type); |
65 | 795k | line_offset = pixel_offset * width; |
66 | 795k | } |
67 | | |
68 | | /* -------------------------------------------------------------------- */ |
69 | | /* Establish the file we will be accessing. */ |
70 | | /* -------------------------------------------------------------------- */ |
71 | 795k | image_header.Get(64,64,filename); |
72 | | |
73 | 795k | filename = MassageLink( filename ); |
74 | | |
75 | 795k | if( filename.length() == 0 ) |
76 | 13.1k | file->GetIODetails( &io_handle_p, &io_mutex_p ); |
77 | | |
78 | 782k | else |
79 | 782k | filename = file->GetInterfaces()->MergeRelativePath( file->GetInterfaces()->io, |
80 | 782k | file->GetFilename(), |
81 | 782k | filename ); |
82 | 795k | } |
83 | | |
84 | | /************************************************************************/ |
85 | | /* ~CBandInterleavedChannel() */ |
86 | | /************************************************************************/ |
87 | | |
88 | | CBandInterleavedChannel::~CBandInterleavedChannel() |
89 | | |
90 | 795k | { |
91 | 795k | } |
92 | | |
93 | | /************************************************************************/ |
94 | | /* ReadBlock() */ |
95 | | /************************************************************************/ |
96 | | |
97 | | int CBandInterleavedChannel::ReadBlock( int block_index, void *buffer, |
98 | | int xoff, int yoff, |
99 | | int xsize, int ysize ) |
100 | | |
101 | 3.28k | { |
102 | 3.28k | PCIDSKInterfaces *interfaces = file->GetInterfaces(); |
103 | | |
104 | | /* -------------------------------------------------------------------- */ |
105 | | /* Check if we are reading from a valid channel. */ |
106 | | /* -------------------------------------------------------------------- */ |
107 | 3.28k | if (line_offset > std::numeric_limits<uint64>::max() / height) |
108 | 0 | { |
109 | 0 | return ThrowPCIDSKException(0, "Invalid line_offset: " PCIDSK_FRMT_UINT64, |
110 | 0 | line_offset); |
111 | 0 | } |
112 | | |
113 | 3.28k | if (start_byte > std::numeric_limits<uint64>::max() - line_offset * height) |
114 | 0 | { |
115 | 0 | return ThrowPCIDSKException(0, "Invalid start_byte: " PCIDSK_FRMT_UINT64, |
116 | 0 | start_byte); |
117 | 0 | } |
118 | | |
119 | | /* -------------------------------------------------------------------- */ |
120 | | /* Default window if needed. */ |
121 | | /* -------------------------------------------------------------------- */ |
122 | 3.28k | if( xoff == -1 && yoff == -1 && xsize == -1 && ysize == -1 ) |
123 | 3.28k | { |
124 | 3.28k | xoff = 0; |
125 | 3.28k | yoff = 0; |
126 | 3.28k | xsize = GetBlockWidth(); |
127 | 3.28k | ysize = GetBlockHeight(); |
128 | 3.28k | } |
129 | | |
130 | | /* -------------------------------------------------------------------- */ |
131 | | /* Validate Window */ |
132 | | /* -------------------------------------------------------------------- */ |
133 | 3.28k | if( xoff < 0 || xoff + xsize > GetBlockWidth() |
134 | 3.28k | || yoff < 0 || yoff + ysize > GetBlockHeight() ) |
135 | 0 | { |
136 | 0 | return ThrowPCIDSKException( 0, |
137 | 0 | "Invalid window in ReadBlock(): xoff=%d,yoff=%d,xsize=%d,ysize=%d", |
138 | 0 | xoff, yoff, xsize, ysize ); |
139 | 0 | } |
140 | | |
141 | | /* -------------------------------------------------------------------- */ |
142 | | /* Establish region to read. */ |
143 | | /* -------------------------------------------------------------------- */ |
144 | 3.28k | int pixel_size = DataTypeSize( pixel_type ); |
145 | | |
146 | 3.28k | if (pixel_offset == 0 || pixel_size == 0) |
147 | 1 | { |
148 | 1 | return ThrowPCIDSKException( 0, "Invalid data type." ); |
149 | 1 | } |
150 | 3.28k | if( xsize > 1 && pixel_offset > static_cast<uint64>(INT_MAX / (xsize - 1)) ) |
151 | 0 | { |
152 | 0 | return ThrowPCIDSKException( 0, "Int overflow in ReadBlock() "); |
153 | 0 | } |
154 | 3.28k | if( pixel_offset*(xsize-1) > static_cast<uint64>(INT_MAX - pixel_size) ) |
155 | 0 | { |
156 | 0 | return ThrowPCIDSKException( 0, "Int overflow in ReadBlock() "); |
157 | 0 | } |
158 | 3.28k | 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 | 3.28k | if( io_handle_p == nullptr ) |
164 | 721 | file->GetIODetails( &io_handle_p, &io_mutex_p, filename.c_str(), |
165 | 721 | file->GetUpdatable() ); |
166 | | |
167 | | /* -------------------------------------------------------------------- */ |
168 | | /* If the imagery is packed, we can read directly into the */ |
169 | | /* target buffer. */ |
170 | | /* -------------------------------------------------------------------- */ |
171 | 3.28k | uint64 offset = start_byte + line_offset * block_index |
172 | 3.28k | + pixel_offset * xoff; |
173 | | |
174 | 3.28k | if( pixel_size == (int) pixel_offset ) |
175 | 2.70k | { |
176 | 2.70k | MutexHolder holder( *io_mutex_p ); |
177 | | |
178 | 2.70k | interfaces->io->Seek( *io_handle_p, offset, SEEK_SET ); |
179 | 2.70k | interfaces->io->Read( buffer, 1, window_size, *io_handle_p ); |
180 | 2.70k | } |
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 | 588 | else |
187 | 588 | { |
188 | 588 | int i; |
189 | 588 | PCIDSKBuffer line_from_disk( window_size ); |
190 | 588 | char *this_pixel; |
191 | | |
192 | 588 | MutexHolder holder( *io_mutex_p ); |
193 | | |
194 | 588 | interfaces->io->Seek( *io_handle_p, offset, SEEK_SET ); |
195 | 588 | interfaces->io->Read( line_from_disk.buffer, |
196 | 588 | 1, line_from_disk.buffer_size, |
197 | 588 | *io_handle_p ); |
198 | | |
199 | 588 | for( i = 0, this_pixel = line_from_disk.buffer; i < xsize; i++ ) |
200 | 0 | { |
201 | 0 | memcpy( ((char *) buffer) + pixel_size * i, |
202 | 0 | this_pixel, pixel_size ); |
203 | 0 | this_pixel += pixel_offset; |
204 | 0 | } |
205 | 588 | } |
206 | | |
207 | | /* -------------------------------------------------------------------- */ |
208 | | /* Do byte swapping if needed. */ |
209 | | /* -------------------------------------------------------------------- */ |
210 | 3.28k | if( needs_swap ) |
211 | 18 | SwapPixels( buffer, pixel_type, xsize ); |
212 | | |
213 | 3.28k | return 1; |
214 | 3.28k | } |
215 | | |
216 | | /************************************************************************/ |
217 | | /* WriteBlock() */ |
218 | | /************************************************************************/ |
219 | | |
220 | | int CBandInterleavedChannel::WriteBlock( int block_index, void *buffer ) |
221 | | |
222 | 0 | { |
223 | 0 | PCIDSKInterfaces *interfaces = file->GetInterfaces(); |
224 | | |
225 | | /* -------------------------------------------------------------------- */ |
226 | | /* Check if we are writing to a valid channel. */ |
227 | | /* -------------------------------------------------------------------- */ |
228 | 0 | if (line_offset > std::numeric_limits<uint64>::max() / height) |
229 | 0 | { |
230 | 0 | return ThrowPCIDSKException(0, "Invalid line_offset: " PCIDSK_FRMT_UINT64, |
231 | 0 | line_offset); |
232 | 0 | } |
233 | | |
234 | 0 | if (pixel_offset > line_offset) |
235 | 0 | { |
236 | 0 | return ThrowPCIDSKException(0, "Invalid pixel_offset: " PCIDSK_FRMT_UINT64, |
237 | 0 | pixel_offset); |
238 | 0 | } |
239 | | |
240 | 0 | if (start_byte > std::numeric_limits<uint64>::max() - line_offset * height) |
241 | 0 | { |
242 | 0 | return ThrowPCIDSKException(0, "Invalid start_byte: " PCIDSK_FRMT_UINT64, |
243 | 0 | start_byte); |
244 | 0 | } |
245 | | |
246 | 0 | if( !file->GetUpdatable() ) |
247 | 0 | return ThrowPCIDSKException(0, "File not open for update in WriteBlock()" ); |
248 | | |
249 | 0 | InvalidateOverviews(); |
250 | | |
251 | | /* -------------------------------------------------------------------- */ |
252 | | /* Establish region to read. */ |
253 | | /* -------------------------------------------------------------------- */ |
254 | 0 | int pixel_size = DataTypeSize( pixel_type ); |
255 | |
|
256 | 0 | if (pixel_offset == 0 || pixel_size == 0) |
257 | 0 | return ThrowPCIDSKException( 0, "Invalid data type." ); |
258 | | |
259 | 0 | uint64 offset = start_byte + line_offset * block_index; |
260 | 0 | 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 | 0 | if( io_handle_p == nullptr ) |
266 | 0 | file->GetIODetails( &io_handle_p, &io_mutex_p, filename.c_str(), |
267 | 0 | file->GetUpdatable() ); |
268 | | |
269 | | /* -------------------------------------------------------------------- */ |
270 | | /* If the imagery is packed, we can read directly into the */ |
271 | | /* target buffer. */ |
272 | | /* -------------------------------------------------------------------- */ |
273 | 0 | if( pixel_size == (int) pixel_offset ) |
274 | 0 | { |
275 | 0 | MutexHolder holder( *io_mutex_p ); |
276 | |
|
277 | 0 | if( needs_swap ) // swap before write. |
278 | 0 | SwapPixels( buffer, pixel_type, width ); |
279 | |
|
280 | 0 | interfaces->io->Seek( *io_handle_p, offset, SEEK_SET ); |
281 | 0 | interfaces->io->Write( buffer, 1, window_size, *io_handle_p ); |
282 | |
|
283 | 0 | if( needs_swap ) // restore to original order. |
284 | 0 | SwapPixels( buffer, pixel_type, width ); |
285 | 0 | } |
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 | 0 | else |
292 | 0 | { |
293 | 0 | int i; |
294 | 0 | PCIDSKBuffer line_from_disk( window_size ); |
295 | 0 | 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 | 0 | { |
305 | 0 | memcpy( this_pixel, ((char *) buffer) + pixel_size * i, |
306 | 0 | 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 | 0 | } |
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 | 0 | } |
318 | | |
319 | | /* -------------------------------------------------------------------- */ |
320 | | /* Do byte swapping if needed. */ |
321 | | /* -------------------------------------------------------------------- */ |
322 | |
|
323 | 0 | return 1; |
324 | 0 | } |
325 | | |
326 | | /************************************************************************/ |
327 | | /* GetChanInfo() */ |
328 | | /************************************************************************/ |
329 | | 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 | 141k | { |
335 | 141k | image_offset = start_byte; |
336 | 141k | pixel_offsetOut = this->pixel_offset; |
337 | 141k | line_offsetOut = this->line_offset; |
338 | 141k | 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 | 141k | PCIDSKBuffer ih(64); |
345 | 141k | file->ReadFromFile( ih.buffer, ih_offset+64, 64 ); |
346 | | |
347 | 141k | ih.Get(0,64,filename_ret); |
348 | 141k | filename_ret = MassageLink( filename_ret ); |
349 | 141k | } |
350 | | |
351 | | /************************************************************************/ |
352 | | /* SetChanInfo() */ |
353 | | /************************************************************************/ |
354 | | |
355 | | void CBandInterleavedChannel |
356 | | ::SetChanInfo( std::string filenameIn, uint64 image_offset, |
357 | | uint64 pixel_offsetIn, uint64 line_offsetIn, |
358 | | bool little_endian ) |
359 | | |
360 | 0 | { |
361 | 0 | 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 | 0 | PCIDSKBuffer ih(1024); |
368 | |
|
369 | 0 | 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 | 0 | std::string IHi2_filename; |
377 | |
|
378 | 0 | if( filenameIn.size() > 64 ) |
379 | 0 | { |
380 | 0 | int link_segment; |
381 | |
|
382 | 0 | ih.Get( 64, 64, IHi2_filename ); |
383 | |
|
384 | 0 | if( IHi2_filename.substr(0,3) == "LNK" ) |
385 | 0 | { |
386 | 0 | link_segment = std::atoi( IHi2_filename.c_str() + 4 ); |
387 | 0 | } |
388 | 0 | else |
389 | 0 | { |
390 | 0 | char link_filename[64]; |
391 | |
|
392 | 0 | link_segment = |
393 | 0 | file->CreateSegment( "Link ", |
394 | 0 | "Long external channel filename link.", |
395 | 0 | SEG_SYS, 1 ); |
396 | |
|
397 | 0 | snprintf( link_filename, sizeof(link_filename), "LNK %4d", link_segment ); |
398 | 0 | IHi2_filename = link_filename; |
399 | 0 | } |
400 | |
|
401 | 0 | CLinkSegment *link = |
402 | 0 | dynamic_cast<CLinkSegment*>( file->GetSegment( link_segment ) ); |
403 | |
|
404 | 0 | if( link != nullptr ) |
405 | 0 | { |
406 | 0 | link->SetPath( filenameIn ); |
407 | 0 | link->Synchronize(); |
408 | 0 | } |
409 | 0 | } |
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 | 0 | else |
416 | 0 | { |
417 | 0 | ih.Get( 64, 64, IHi2_filename ); |
418 | |
|
419 | 0 | if( IHi2_filename.substr(0,3) == "LNK" ) |
420 | 0 | { |
421 | 0 | int link_segment = std::atoi( IHi2_filename.c_str() + 4 ); |
422 | |
|
423 | 0 | file->DeleteSegment( link_segment ); |
424 | 0 | } |
425 | |
|
426 | 0 | IHi2_filename = filenameIn; |
427 | 0 | } |
428 | | |
429 | | /* -------------------------------------------------------------------- */ |
430 | | /* Update the image header. */ |
431 | | /* -------------------------------------------------------------------- */ |
432 | | // IHi.2 |
433 | 0 | ih.Put( IHi2_filename.c_str(), 64, 64 ); |
434 | | |
435 | | // IHi.6.1 |
436 | 0 | ih.Put( image_offset, 168, 16 ); |
437 | | |
438 | | // IHi.6.2 |
439 | 0 | ih.Put( pixel_offsetIn, 184, 8 ); |
440 | | |
441 | | // IHi.6.3 |
442 | 0 | ih.Put( line_offsetIn, 192, 8 ); |
443 | | |
444 | | // IHi.6.5 |
445 | 0 | if( little_endian ) |
446 | 0 | ih.Put( "S", 201, 1 ); |
447 | 0 | else |
448 | 0 | ih.Put( "N", 201, 1 ); |
449 | |
|
450 | 0 | file->WriteToFile( ih.buffer, ih_offset, 1024 ); |
451 | | |
452 | | /* -------------------------------------------------------------------- */ |
453 | | /* Update local configuration. */ |
454 | | /* -------------------------------------------------------------------- */ |
455 | 0 | this->filename = file->GetInterfaces()->MergeRelativePath( file->GetInterfaces()->io, |
456 | 0 | file->GetFilename(), |
457 | 0 | filenameIn ); |
458 | |
|
459 | 0 | start_byte = image_offset; |
460 | 0 | this->pixel_offset = pixel_offsetIn; |
461 | 0 | this->line_offset = line_offsetIn; |
462 | |
|
463 | 0 | if( little_endian ) |
464 | 0 | byte_order = 'S'; |
465 | 0 | else |
466 | 0 | byte_order = 'N'; |
467 | | |
468 | | /* -------------------------------------------------------------------- */ |
469 | | /* Determine if we need byte swapping. */ |
470 | | /* -------------------------------------------------------------------- */ |
471 | 0 | unsigned short test_value = 1; |
472 | |
|
473 | 0 | if( (reinterpret_cast<uint8 *>(&test_value))[0] == 1 ) |
474 | 0 | needs_swap = (byte_order != 'S'); |
475 | 0 | else |
476 | 0 | needs_swap = (byte_order == 'S'); |
477 | |
|
478 | 0 | if( pixel_type == CHN_8U ) |
479 | 0 | needs_swap = 0; |
480 | 0 | } |
481 | | |
482 | | /************************************************************************/ |
483 | | /* MassageLink() */ |
484 | | /* */ |
485 | | /* Return the filename after applying translation of long */ |
486 | | /* linked filenames using a link segment. */ |
487 | | /************************************************************************/ |
488 | | |
489 | | std::string CBandInterleavedChannel::MassageLink( std::string filename_in ) const |
490 | | |
491 | 937k | { |
492 | 937k | if (filename_in.find("LNK") == 0) |
493 | 0 | { |
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 | 0 | { |
499 | 0 | ThrowPCIDSKException("Unable to find link segment. Link name: %s", |
500 | 0 | filename_in.c_str()); |
501 | 0 | return ""; |
502 | 0 | } |
503 | | |
504 | 0 | CLinkSegment* link_seg = |
505 | 0 | dynamic_cast<CLinkSegment*>(file->GetSegment(seg_num)); |
506 | 0 | if (link_seg == nullptr) |
507 | 0 | { |
508 | 0 | ThrowPCIDSKException("Failed to get Link Information Segment."); |
509 | 0 | return ""; |
510 | 0 | } |
511 | | |
512 | 0 | filename_in = link_seg->GetPath(); |
513 | 0 | } |
514 | | |
515 | 937k | return filename_in; |
516 | 937k | } |