1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the CTiledChannel 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_exception.h"
35 : #include "channel/ctiledchannel.h"
36 : #include "segment/sysblockmap.h"
37 : #include "core/sysvirtualfile.h"
38 : #include "core/cpcidskfile.h"
39 : #include "core/pcidsk_utils.h"
40 : #include <cassert>
41 : #include <cstdlib>
42 : #include <cstring>
43 :
44 : using namespace PCIDSK;
45 :
46 : /************************************************************************/
47 : /* CTiledChannel() */
48 : /************************************************************************/
49 :
50 14 : CTiledChannel::CTiledChannel( PCIDSKBuffer &image_header,
51 : uint64 ih_offset,
52 : PCIDSKBuffer &file_header,
53 : int channelnum,
54 : CPCIDSKFile *file,
55 : eChanType pixel_type )
56 14 : : CPCIDSKChannel( image_header, ih_offset, file, pixel_type, channelnum)
57 :
58 : {
59 : /* -------------------------------------------------------------------- */
60 : /* Establish the virtual file we will be accessing. */
61 : /* -------------------------------------------------------------------- */
62 14 : std::string filename;
63 :
64 14 : image_header.Get(64,64,filename);
65 :
66 14 : assert( strstr(filename.c_str(),"SIS=") != NULL );
67 :
68 14 : image = atoi(strstr(filename.c_str(),"SIS=") + 4);
69 :
70 14 : vfile = NULL;
71 :
72 : /* -------------------------------------------------------------------- */
73 : /* If this is an unassociated channel (ie. an overview), we */
74 : /* will set the size and blocksize values to something */
75 : /* unreasonable and set them properly in EstablishAccess() */
76 : /* -------------------------------------------------------------------- */
77 14 : if( channelnum == -1 )
78 : {
79 5 : width = -1;
80 5 : height = -1;
81 5 : block_width = -1;
82 5 : block_height = -1;
83 14 : }
84 14 : }
85 :
86 : /************************************************************************/
87 : /* ~CTiledChannel() */
88 : /************************************************************************/
89 :
90 14 : CTiledChannel::~CTiledChannel()
91 :
92 : {
93 14 : Synchronize();
94 14 : }
95 :
96 : /************************************************************************/
97 : /* EstablishAccess() */
98 : /************************************************************************/
99 :
100 114 : void CTiledChannel::EstablishAccess() const
101 :
102 : {
103 114 : if( vfile != NULL )
104 100 : return;
105 :
106 : /* -------------------------------------------------------------------- */
107 : /* Establish the virtual file to access this image. */
108 : /* -------------------------------------------------------------------- */
109 : SysBlockMap *bmap = dynamic_cast<SysBlockMap*>(
110 14 : file->GetSegment( SEG_SYS, "SysBMDir" ));
111 :
112 14 : if( bmap == NULL )
113 0 : ThrowPCIDSKException( "Unable to find SysBMDir segment." );
114 :
115 14 : vfile = bmap->GetVirtualFile( image );
116 :
117 : /* -------------------------------------------------------------------- */
118 : /* Parse the header. */
119 : /* -------------------------------------------------------------------- */
120 14 : PCIDSKBuffer theader(128);
121 14 : std::string data_type;
122 :
123 14 : vfile->ReadFromFile( theader.buffer, 0, 128 );
124 :
125 14 : width = theader.GetInt(0,8);
126 14 : height = theader.GetInt(8,8);
127 14 : block_width = theader.GetInt(16,8);
128 14 : block_height = theader.GetInt(24,8);
129 :
130 14 : theader.Get(32,4,data_type);
131 14 : theader.Get(54, 8, compression);
132 :
133 14 : pixel_type = GetDataTypeFromName(data_type);
134 14 : if (pixel_type == CHN_UNKNOWN)
135 : {
136 : ThrowPCIDSKException( "Unknown channel type: %s",
137 0 : data_type.c_str() );
138 : }
139 :
140 : /* -------------------------------------------------------------------- */
141 : /* Compute information on the tiles. */
142 : /* -------------------------------------------------------------------- */
143 14 : tiles_per_row = (width + block_width - 1) / block_width;
144 14 : tiles_per_col = (height + block_height - 1) / block_height;
145 14 : tile_count = tiles_per_row * tiles_per_col;
146 :
147 : /* -------------------------------------------------------------------- */
148 : /* Resize our tile info cache. */
149 : /* -------------------------------------------------------------------- */
150 : int tile_block_info_count =
151 14 : (tile_count + tile_block_size - 1) / tile_block_size;
152 :
153 14 : tile_offsets.resize( tile_block_info_count );
154 14 : tile_sizes.resize( tile_block_info_count );
155 14 : tile_info_dirty.resize( tile_block_info_count, false );
156 :
157 : /* -------------------------------------------------------------------- */
158 : /* Establish byte swapping. Tiled data files are always big */
159 : /* endian, regardless of what the headers might imply. */
160 : /* -------------------------------------------------------------------- */
161 14 : unsigned short test_value = 1;
162 :
163 14 : if( ((uint8 *) &test_value)[0] == 1 )
164 14 : needs_swap = pixel_type != CHN_8U;
165 : else
166 0 : needs_swap = false;
167 : }
168 :
169 : /************************************************************************/
170 : /* LoadTileInfoBlock() */
171 : /************************************************************************/
172 :
173 8 : void CTiledChannel::LoadTileInfoBlock( int block )
174 :
175 : {
176 8 : assert( tile_offsets[block].size() == 0 );
177 :
178 : /* -------------------------------------------------------------------- */
179 : /* How many tiles in this block? */
180 : /* -------------------------------------------------------------------- */
181 8 : int tiles_in_block = tile_block_size;
182 :
183 8 : if( block * tile_block_size + tiles_in_block > tile_count )
184 8 : tiles_in_block = tile_count - block * tile_block_size;
185 :
186 : /* -------------------------------------------------------------------- */
187 : /* Resize the vectors for this block. */
188 : /* -------------------------------------------------------------------- */
189 8 : tile_offsets[block].resize( tiles_in_block );
190 8 : tile_sizes[block].resize( tiles_in_block );
191 :
192 : /* -------------------------------------------------------------------- */
193 : /* Read the offset and size data from disk. */
194 : /* -------------------------------------------------------------------- */
195 8 : PCIDSKBuffer offset_map( tiles_in_block * 12 + 1 );
196 8 : PCIDSKBuffer size_map( tiles_in_block * 8 + 1 );
197 :
198 : vfile->ReadFromFile( offset_map.buffer,
199 : 128 + block * tile_block_size * 12,
200 8 : tiles_in_block * 12 );
201 : vfile->ReadFromFile( size_map.buffer,
202 : 128 + tile_count * 12 + block * tile_block_size * 8,
203 8 : tiles_in_block * 8 );
204 :
205 16 : for( int i = 0; i < tiles_in_block; i++ )
206 : {
207 : char chSaved;
208 8 : char *target = offset_map.buffer + i*12;
209 :
210 8 : chSaved = target[12];
211 8 : target[12] = '\0';
212 8 : tile_offsets[block][i] = atouint64(target);
213 8 : target[12] = chSaved;
214 :
215 8 : target = size_map.buffer + i*8;
216 8 : chSaved = target[8];
217 8 : target[8] = '\0';
218 8 : tile_sizes[block][i] = atoi(target);
219 8 : target[8] = chSaved;
220 8 : }
221 8 : }
222 :
223 : /************************************************************************/
224 : /* SaveTileInfoBlock() */
225 : /************************************************************************/
226 :
227 4 : void CTiledChannel::SaveTileInfoBlock( int block )
228 :
229 : {
230 4 : assert( tile_offsets[block].size() != 0 );
231 4 : int tiles_in_block = tile_offsets[block].size();
232 :
233 : /* -------------------------------------------------------------------- */
234 : /* Write the offset and size data to disk. */
235 : /* -------------------------------------------------------------------- */
236 4 : PCIDSKBuffer offset_map( tiles_in_block * 12 + 1 );
237 4 : PCIDSKBuffer size_map( tiles_in_block * 8 + 1 );
238 :
239 8 : for( int i = 0; i < tiles_in_block; i++ )
240 : {
241 4 : if( tile_offsets[block][i] == (uint64) -1
242 : || tile_offsets[block][i] == 0 )
243 0 : offset_map.Put( -1, i*12, 12 );
244 : else
245 4 : offset_map.Put( tile_offsets[block][i], i*12, 12 );
246 :
247 4 : size_map.Put( tile_sizes[block][i], i*8, 8 );
248 : }
249 :
250 : vfile->WriteToFile( offset_map.buffer,
251 : 128 + block * tile_block_size * 12,
252 4 : tiles_in_block * 12 );
253 : vfile->WriteToFile( size_map.buffer,
254 : 128 + tile_count * 12 + block * tile_block_size * 8,
255 4 : tiles_in_block * 8 );
256 :
257 4 : tile_info_dirty[block] = false;
258 4 : }
259 :
260 : /************************************************************************/
261 : /* GetTileInfo() */
262 : /* */
263 : /* Fetch the tile offset and size for the indicated tile. */
264 : /************************************************************************/
265 :
266 13 : void CTiledChannel::GetTileInfo( int tile_index, uint64 &offset, int &size )
267 :
268 : {
269 13 : int block = tile_index / tile_block_size;
270 13 : int index_within_block = tile_index - block * tile_block_size;
271 :
272 13 : if( tile_offsets[block].size() == 0 )
273 8 : LoadTileInfoBlock( block );
274 :
275 13 : offset = tile_offsets[block][index_within_block];
276 13 : size = tile_sizes[block][index_within_block];
277 13 : }
278 :
279 : /************************************************************************/
280 : /* SetTileInfo() */
281 : /************************************************************************/
282 :
283 4 : void CTiledChannel::SetTileInfo( int tile_index, uint64 offset, int size )
284 :
285 : {
286 4 : int block = tile_index / tile_block_size;
287 4 : int index_within_block = tile_index - block * tile_block_size;
288 :
289 4 : if( tile_offsets[block].size() == 0 )
290 0 : LoadTileInfoBlock( block );
291 :
292 4 : if( offset != tile_offsets[block][index_within_block]
293 : || size != tile_sizes[block][index_within_block] )
294 : {
295 4 : tile_offsets[block][index_within_block] = offset;
296 4 : tile_sizes[block][index_within_block] = size;
297 :
298 4 : tile_info_dirty[block] = true;
299 : }
300 4 : }
301 :
302 : /************************************************************************/
303 : /* Synchronize() */
304 : /* */
305 : /* Flush updated blockmap to disk if it is dirty. */
306 : /************************************************************************/
307 :
308 20 : void CTiledChannel::Synchronize()
309 :
310 : {
311 20 : if( tile_info_dirty.size() == 0 )
312 0 : return;
313 :
314 : int i;
315 :
316 40 : for( i = 0; i < (int) tile_info_dirty.size(); i++ )
317 : {
318 20 : if( tile_info_dirty[i] )
319 4 : SaveTileInfoBlock( i );
320 : }
321 :
322 20 : vfile->Synchronize();
323 : }
324 :
325 : /************************************************************************/
326 : /* ReadBlock() */
327 : /************************************************************************/
328 :
329 9 : int CTiledChannel::ReadBlock( int block_index, void *buffer,
330 : int xoff, int yoff,
331 : int xsize, int ysize )
332 :
333 : {
334 9 : int pixel_size = DataTypeSize(GetType());
335 :
336 : /* -------------------------------------------------------------------- */
337 : /* Default window if needed. */
338 : /* -------------------------------------------------------------------- */
339 9 : if( xoff == -1 && yoff == -1 && xsize == -1 && ysize == -1 )
340 : {
341 9 : xoff = 0;
342 9 : yoff = 0;
343 9 : xsize = GetBlockWidth();
344 9 : ysize = GetBlockHeight();
345 : }
346 :
347 : /* -------------------------------------------------------------------- */
348 : /* Validate Window */
349 : /* -------------------------------------------------------------------- */
350 18 : if( xoff < 0 || xoff + xsize > GetBlockWidth()
351 9 : || yoff < 0 || yoff + ysize > GetBlockHeight() )
352 : {
353 : ThrowPCIDSKException(
354 : "Invalid window in ReadBloc(): xoff=%d,yoff=%d,xsize=%d,ysize=%d",
355 0 : xoff, yoff, xsize, ysize );
356 : }
357 :
358 9 : if( block_index < 0 || block_index >= tile_count )
359 : {
360 : ThrowPCIDSKException( "Requested non-existant block (%d)",
361 0 : block_index );
362 : }
363 :
364 : /* -------------------------------------------------------------------- */
365 : /* Does this tile exist? If not return a zeroed buffer. */
366 : /* -------------------------------------------------------------------- */
367 : uint64 tile_offset;
368 : int tile_size;
369 :
370 9 : GetTileInfo( block_index, tile_offset, tile_size );
371 :
372 9 : if( tile_size == 0 )
373 : {
374 4 : memset( buffer, 0, GetBlockWidth() * GetBlockHeight() * pixel_size );
375 4 : return 1;
376 : }
377 :
378 : /* -------------------------------------------------------------------- */
379 : /* The simpliest case it an uncompressed direct and complete */
380 : /* tile read into the destination buffer. */
381 : /* -------------------------------------------------------------------- */
382 10 : if( xoff == 0 && xsize == GetBlockWidth()
383 5 : && yoff == 0 && ysize == GetBlockHeight()
384 : && tile_size == xsize * ysize * pixel_size
385 : && compression == "NONE" )
386 : {
387 3 : vfile->ReadFromFile( buffer, tile_offset, tile_size );
388 :
389 : // Do byte swapping if needed.
390 3 : if( needs_swap )
391 1 : SwapPixels( buffer, pixel_type, xsize * ysize );
392 :
393 3 : return 1;
394 : }
395 :
396 : /* -------------------------------------------------------------------- */
397 : /* Load uncompressed data, one scanline at a time, into the */
398 : /* target buffer. */
399 : /* -------------------------------------------------------------------- */
400 2 : if( compression == "NONE" )
401 : {
402 : int iy;
403 :
404 0 : for( iy = 0; iy < ysize; iy++ )
405 : {
406 : vfile->ReadFromFile( ((uint8 *) buffer)
407 : + iy * xsize * pixel_size,
408 : tile_offset
409 : + ((iy+yoff)*block_width + xoff) * pixel_size,
410 0 : xsize * pixel_size );
411 : }
412 :
413 : // Do byte swapping if needed.
414 0 : if( needs_swap )
415 0 : SwapPixels( buffer, pixel_type, xsize * ysize );
416 :
417 0 : return 1;
418 : }
419 :
420 : /* -------------------------------------------------------------------- */
421 : /* Load the whole compressed data into a working buffer. */
422 : /* -------------------------------------------------------------------- */
423 2 : PCIDSKBuffer oCompressedData( tile_size );
424 2 : PCIDSKBuffer oUncompressedData( pixel_size * block_width * block_height );
425 :
426 2 : vfile->ReadFromFile( oCompressedData.buffer, tile_offset, tile_size );
427 :
428 : /* -------------------------------------------------------------------- */
429 : /* Handle decompression. */
430 : /* -------------------------------------------------------------------- */
431 2 : if( compression == "RLE" )
432 : {
433 1 : RLEDecompressBlock( oCompressedData, oUncompressedData );
434 : }
435 1 : else if( strncmp(compression.c_str(),"JPEG",4) == 0 )
436 : {
437 1 : JPEGDecompressBlock( oCompressedData, oUncompressedData );
438 : }
439 : else
440 : {
441 : ThrowPCIDSKException(
442 : "Unable to read tile of unsupported compression type: %s",
443 0 : compression.c_str() );
444 : }
445 :
446 : /* -------------------------------------------------------------------- */
447 : /* Swap if necessary. TODO: there is some reason to doubt that */
448 : /* the old implementation properly byte swapped compressed */
449 : /* data. Perhaps this should be conditional? */
450 : /* -------------------------------------------------------------------- */
451 2 : if( needs_swap )
452 : SwapPixels( oUncompressedData.buffer, pixel_type,
453 1 : GetBlockWidth() * GetBlockHeight() );
454 :
455 : /* -------------------------------------------------------------------- */
456 : /* Copy out the desired subwindow. */
457 : /* -------------------------------------------------------------------- */
458 : int iy;
459 :
460 161 : for( iy = 0; iy < ysize; iy++ )
461 : {
462 : memcpy( ((uint8 *) buffer) + iy * xsize * pixel_size,
463 : oUncompressedData.buffer
464 : + ((iy+yoff)*block_width + xoff) * pixel_size,
465 159 : xsize * pixel_size );
466 : }
467 :
468 2 : return 1;
469 : }
470 :
471 : /************************************************************************/
472 : /* IsTileEmpty() */
473 : /************************************************************************/
474 4 : bool CTiledChannel::IsTileEmpty(void *buffer) const
475 : {
476 : assert(sizeof(int32) == 4); // just to be on the safe side...
477 :
478 : unsigned int num_dword =
479 4 : (block_width * block_height * DataTypeSize(pixel_type)) / 4;
480 : unsigned int rem =
481 4 : (block_width * block_height * DataTypeSize(pixel_type)) % 4;
482 :
483 4 : int32* int_buf = reinterpret_cast<int32*>(buffer);
484 :
485 4 : if (num_dword > 0) {
486 182 : for (unsigned int n = 0; n < num_dword; n++) {
487 182 : if (int_buf[n]) return false;
488 : }
489 : }
490 :
491 0 : char* char_buf = reinterpret_cast<char*>(int_buf + num_dword);
492 0 : if (rem > 0) {
493 0 : for (unsigned int n = 0; n < rem; n++) {
494 0 : if (char_buf[n]) return false;
495 : }
496 : }
497 :
498 0 : return true;
499 : }
500 :
501 : /************************************************************************/
502 : /* WriteBlock() */
503 : /************************************************************************/
504 :
505 4 : int CTiledChannel::WriteBlock( int block_index, void *buffer )
506 :
507 : {
508 4 : if( !file->GetUpdatable() )
509 0 : throw PCIDSKException( "File not open for update in WriteBlock()" );
510 :
511 4 : InvalidateOverviews();
512 :
513 4 : int pixel_size = DataTypeSize(GetType());
514 4 : int pixel_count = GetBlockWidth() * GetBlockHeight();
515 :
516 4 : if( block_index < 0 || block_index >= tile_count )
517 : {
518 : ThrowPCIDSKException( "Requested non-existant block (%d)",
519 0 : block_index );
520 : }
521 :
522 : /* -------------------------------------------------------------------- */
523 : /* Fetch existing tile offset and size. */
524 : /* -------------------------------------------------------------------- */
525 : uint64 tile_offset;
526 : int tile_size;
527 :
528 4 : GetTileInfo( block_index, tile_offset, tile_size );
529 :
530 : /* -------------------------------------------------------------------- */
531 : /* The simpliest case it an uncompressed direct and complete */
532 : /* tile read into the destination buffer. */
533 : /* -------------------------------------------------------------------- */
534 4 : if( compression == "NONE"
535 : && tile_size == pixel_count * pixel_size )
536 : {
537 : // Do byte swapping if needed.
538 0 : if( needs_swap )
539 0 : SwapPixels( buffer, pixel_type, pixel_count );
540 :
541 0 : vfile->WriteToFile( buffer, tile_offset, tile_size );
542 :
543 0 : if( needs_swap )
544 0 : SwapPixels( buffer, pixel_type, pixel_count );
545 :
546 0 : return 1;
547 : }
548 :
549 4 : if ((int64)tile_offset == -1)
550 : {
551 : // Check if the tile is empty. If it is, we can skip writing it,
552 : // unless the tile is already dirty.
553 4 : bool is_empty = IsTileEmpty(buffer);
554 :
555 4 : if (is_empty) return 1; // we don't need to do anything else
556 : }
557 :
558 : /* -------------------------------------------------------------------- */
559 : /* Copy the uncompressed data into a PCIDSKBuffer, and byte */
560 : /* swap if needed. */
561 : /* -------------------------------------------------------------------- */
562 4 : PCIDSKBuffer oUncompressedData( pixel_size * block_width * block_height );
563 :
564 : memcpy( oUncompressedData.buffer, buffer,
565 4 : oUncompressedData.buffer_size );
566 :
567 4 : if( needs_swap )
568 2 : SwapPixels( oUncompressedData.buffer, pixel_type, pixel_count );
569 :
570 : /* -------------------------------------------------------------------- */
571 : /* Compress the imagery. */
572 : /* -------------------------------------------------------------------- */
573 4 : PCIDSKBuffer oCompressedData;
574 :
575 4 : if( compression == "NONE" )
576 : {
577 2 : oCompressedData = oUncompressedData;
578 : }
579 2 : else if( compression == "RLE" )
580 : {
581 1 : RLECompressBlock( oUncompressedData, oCompressedData );
582 : }
583 1 : else if( strncmp(compression.c_str(),"JPEG",4) == 0 )
584 : {
585 1 : JPEGCompressBlock( oUncompressedData, oCompressedData );
586 : }
587 : else
588 : {
589 : ThrowPCIDSKException(
590 : "Unable to write tile of unsupported compression type: %s",
591 0 : compression.c_str() );
592 : }
593 :
594 : /* -------------------------------------------------------------------- */
595 : /* If this fits in the existing space, just write it directly. */
596 : /* -------------------------------------------------------------------- */
597 4 : if( oCompressedData.buffer_size <= tile_size )
598 : {
599 0 : vfile->WriteToFile( oCompressedData.buffer, tile_offset, tile_size );
600 :
601 0 : tile_size = oCompressedData.buffer_size;
602 0 : SetTileInfo( block_index, tile_offset, tile_size );
603 : }
604 :
605 : /* -------------------------------------------------------------------- */
606 : /* Otherwise we try and write it at the end of the virtual file. */
607 : /* -------------------------------------------------------------------- */
608 : else
609 : {
610 4 : uint64 new_offset = vfile->GetLength();
611 :
612 : vfile->WriteToFile( oCompressedData.buffer,
613 4 : new_offset, oCompressedData.buffer_size );
614 :
615 4 : SetTileInfo( block_index, new_offset, oCompressedData.buffer_size );
616 : }
617 :
618 4 : return 1;
619 : }
620 :
621 : /************************************************************************/
622 : /* GetBlockWidth() */
623 : /************************************************************************/
624 :
625 57 : int CTiledChannel::GetBlockWidth() const
626 :
627 : {
628 57 : EstablishAccess();
629 57 : return CPCIDSKChannel::GetBlockWidth();
630 : }
631 :
632 : /************************************************************************/
633 : /* GetBlockHeight() */
634 : /************************************************************************/
635 :
636 57 : int CTiledChannel::GetBlockHeight() const
637 :
638 : {
639 57 : EstablishAccess();
640 57 : return CPCIDSKChannel::GetBlockHeight();
641 : }
642 :
643 : /************************************************************************/
644 : /* GetWidth() */
645 : /************************************************************************/
646 :
647 5 : int CTiledChannel::GetWidth() const
648 :
649 : {
650 5 : if( width == -1 )
651 0 : EstablishAccess();
652 :
653 5 : return CPCIDSKChannel::GetWidth();
654 : }
655 :
656 : /************************************************************************/
657 : /* GetHeight() */
658 : /************************************************************************/
659 :
660 5 : int CTiledChannel::GetHeight() const
661 :
662 : {
663 5 : if( height == -1 )
664 0 : EstablishAccess();
665 :
666 5 : return CPCIDSKChannel::GetHeight();
667 : }
668 :
669 : /************************************************************************/
670 : /* GetType() */
671 : /************************************************************************/
672 :
673 45 : eChanType CTiledChannel::GetType() const
674 :
675 : {
676 45 : if( pixel_type == CHN_UNKNOWN )
677 0 : EstablishAccess();
678 :
679 45 : return CPCIDSKChannel::GetType();
680 : }
681 :
682 : /************************************************************************/
683 : /* RLEDecompressBlock() */
684 : /************************************************************************/
685 :
686 1 : void CTiledChannel::RLEDecompressBlock( PCIDSKBuffer &oCompressedData,
687 : PCIDSKBuffer &oDecompressedData )
688 :
689 :
690 : {
691 1 : int src_offset=0, dst_offset=0;
692 1 : uint8 *src = (uint8 *) oCompressedData.buffer;
693 1 : uint8 *dst = (uint8 *) oDecompressedData.buffer;
694 1 : int pixel_size = DataTypeSize(GetType());
695 :
696 : /* -------------------------------------------------------------------- */
697 : /* Process till we are out of source data, or our destination */
698 : /* buffer is full. These conditions should be satisified at */
699 : /* the same time! */
700 : /* -------------------------------------------------------------------- */
701 11 : while( src_offset + 1 + pixel_size <= oCompressedData.buffer_size
702 : && dst_offset < oDecompressedData.buffer_size )
703 : {
704 : /* -------------------------------------------------------------------- */
705 : /* Extract a repeat run */
706 : /* -------------------------------------------------------------------- */
707 9 : if( src[src_offset] > 127 )
708 : {
709 5 : int count = src[src_offset++] - 128;
710 : int i;
711 :
712 5 : if( dst_offset + count * pixel_size > oDecompressedData.buffer_size)
713 : {
714 0 : ThrowPCIDSKException( "RLE compressed tile corrupt, overrun avoided." );
715 : }
716 :
717 526 : while( count-- > 0 )
718 : {
719 1548 : for( i = 0; i < pixel_size; i++ )
720 1032 : dst[dst_offset++] = src[src_offset+i];
721 : }
722 5 : src_offset += pixel_size;
723 : }
724 :
725 : /* -------------------------------------------------------------------- */
726 : /* Extract a literal run. */
727 : /* -------------------------------------------------------------------- */
728 : else
729 : {
730 4 : int count = src[src_offset++];
731 :
732 4 : if( dst_offset + count*pixel_size > oDecompressedData.buffer_size
733 : || src_offset + count*pixel_size > oCompressedData.buffer_size)
734 : {
735 0 : ThrowPCIDSKException( "RLE compressed tile corrupt, overrun avoided." );
736 : }
737 :
738 : memcpy( dst + dst_offset, src + src_offset,
739 4 : pixel_size * count );
740 4 : src_offset += pixel_size * count;
741 4 : dst_offset += pixel_size * count;
742 : }
743 :
744 : }
745 :
746 : /* -------------------------------------------------------------------- */
747 : /* Final validation. */
748 : /* -------------------------------------------------------------------- */
749 1 : if( src_offset != oCompressedData.buffer_size
750 : || dst_offset != oDecompressedData.buffer_size )
751 : {
752 0 : ThrowPCIDSKException( "RLE compressed tile corrupt, result incomplete." );
753 : }
754 1 : }
755 :
756 : /************************************************************************/
757 : /* RLECompressBlock() */
758 : /* */
759 : /* TODO: There does not seem to be any byte order logic in here! */
760 : /************************************************************************/
761 :
762 1 : void CTiledChannel::RLECompressBlock( PCIDSKBuffer &oUncompressedData,
763 : PCIDSKBuffer &oCompressedData )
764 :
765 :
766 : {
767 1 : int src_bytes = oUncompressedData.buffer_size;
768 1 : int pixel_size = DataTypeSize(GetType());
769 1 : int src_offset = 0, dst_offset = 0;
770 : int i;
771 1 : uint8 *src = (uint8 *) oUncompressedData.buffer;
772 :
773 : /* -------------------------------------------------------------------- */
774 : /* Loop till input exausted. */
775 : /* -------------------------------------------------------------------- */
776 11 : while( src_offset < src_bytes )
777 : {
778 9 : bool bGotARun = false;
779 :
780 : /* -------------------------------------------------------------------- */
781 : /* Establish the run length, and emit if greater than 3. */
782 : /* -------------------------------------------------------------------- */
783 9 : if( src_offset + 3*pixel_size < src_bytes )
784 : {
785 9 : int count = 1;
786 :
787 529 : while( count < 127
788 : && src_offset + count*pixel_size < src_bytes )
789 : {
790 516 : bool bWordMatch = true;
791 :
792 1548 : for( i = 0; i < pixel_size; i++ )
793 : {
794 2064 : if( src[src_offset+i]
795 1032 : != src[src_offset+i+count*pixel_size] )
796 8 : bWordMatch = false;
797 : }
798 :
799 516 : if( !bWordMatch )
800 5 : break;
801 :
802 511 : count++;
803 : }
804 :
805 9 : if( count >= 3 )
806 : {
807 5 : if( oCompressedData.buffer_size < dst_offset + pixel_size+1 )
808 1 : oCompressedData.SetSize( oCompressedData.buffer_size*2+100);
809 :
810 5 : oCompressedData.buffer[dst_offset++] = (char) (count+128);
811 :
812 15 : for( i = 0; i < pixel_size; i++ )
813 10 : oCompressedData.buffer[dst_offset++] = src[src_offset+i];
814 :
815 5 : src_offset += count * pixel_size;
816 :
817 5 : bGotARun = true;
818 : }
819 : else
820 4 : bGotARun = false;
821 : }
822 :
823 : /* -------------------------------------------------------------------- */
824 : /* Otherwise emit a literal till we encounter at least a three */
825 : /* word series. */
826 : /* -------------------------------------------------------------------- */
827 9 : if( !bGotARun )
828 : {
829 4 : int count = 1;
830 4 : int match_count = 0;
831 :
832 512 : while( count < 127
833 : && src_offset + count*pixel_size < src_bytes )
834 : {
835 504 : bool bWordMatch = true;
836 :
837 1512 : for( i = 0; i < pixel_size; i++ )
838 : {
839 2016 : if( src[src_offset+i]
840 1008 : != src[src_offset+i+count*pixel_size] )
841 724 : bWordMatch = false;
842 : }
843 :
844 504 : if( bWordMatch )
845 0 : match_count++;
846 : else
847 504 : match_count = 0;
848 :
849 504 : if( match_count > 2 )
850 0 : break;
851 :
852 504 : count++;
853 : }
854 :
855 4 : assert( src_offset + count*pixel_size <= src_bytes );
856 :
857 11 : while( oCompressedData.buffer_size
858 : < dst_offset + count*pixel_size+1 )
859 3 : oCompressedData.SetSize( oCompressedData.buffer_size*2+100 );
860 :
861 4 : oCompressedData.buffer[dst_offset++] = (char) count;
862 : memcpy( oCompressedData.buffer + dst_offset,
863 : src + src_offset,
864 4 : count * pixel_size );
865 4 : src_offset += count * pixel_size;
866 4 : dst_offset += count * pixel_size;
867 : }
868 : }
869 :
870 1 : oCompressedData.buffer_size = dst_offset;
871 1 : }
872 :
873 : /************************************************************************/
874 : /* JPEGDecompressBlock() */
875 : /************************************************************************/
876 :
877 1 : void CTiledChannel::JPEGDecompressBlock( PCIDSKBuffer &oCompressedData,
878 : PCIDSKBuffer &oDecompressedData )
879 :
880 :
881 : {
882 1 : if( file->GetInterfaces()->JPEGDecompressBlock == NULL )
883 0 : ThrowPCIDSKException( "JPEG decompression not enabled in the PCIDSKInterfaces of this build." );
884 :
885 1 : file->GetInterfaces()->JPEGDecompressBlock(
886 : (uint8 *) oCompressedData.buffer, oCompressedData.buffer_size,
887 : (uint8 *) oDecompressedData.buffer, oDecompressedData.buffer_size,
888 2 : GetBlockWidth(), GetBlockHeight(), GetType() );
889 1 : }
890 :
891 : /************************************************************************/
892 : /* JPEGCompressBlock() */
893 : /************************************************************************/
894 :
895 1 : void CTiledChannel::JPEGCompressBlock( PCIDSKBuffer &oDecompressedData,
896 : PCIDSKBuffer &oCompressedData )
897 :
898 :
899 :
900 : {
901 1 : if( file->GetInterfaces()->JPEGCompressBlock == NULL )
902 0 : ThrowPCIDSKException( "JPEG compression not enabled in the PCIDSKInterfaces of this build." );
903 :
904 : /* -------------------------------------------------------------------- */
905 : /* What quality should we be using? */
906 : /* -------------------------------------------------------------------- */
907 1 : int quality = 75;
908 :
909 1 : if( compression.c_str()[4] >= '1'
910 0 : && compression.c_str()[4] <= '0' )
911 0 : quality = atoi(compression.c_str() + 4);
912 :
913 : /* -------------------------------------------------------------------- */
914 : /* Make the output buffer plent big to hold any conceivable */
915 : /* result. */
916 : /* -------------------------------------------------------------------- */
917 1 : oCompressedData.SetSize( oDecompressedData.buffer_size * 2 + 1000 );
918 :
919 : /* -------------------------------------------------------------------- */
920 : /* invoke. */
921 : /* -------------------------------------------------------------------- */
922 1 : file->GetInterfaces()->JPEGCompressBlock(
923 : (uint8 *) oDecompressedData.buffer, oDecompressedData.buffer_size,
924 : (uint8 *) oCompressedData.buffer, oCompressedData.buffer_size,
925 2 : GetBlockWidth(), GetBlockHeight(), GetType(), 75 );
926 1 : }
927 :
|