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