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