ultimatepp/bazaar/plugin/gdal/frmts/rmf/rmflzw.cpp
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

241 lines
8.2 KiB
C++

/******************************************************************************
* $Id: rmflzw.cpp 11865 2007-08-09 11:53:57Z warmerdam $
*
* Project: Raster Matrix Format
* Purpose: Implementation of the LZW compression algorithm as used in
* GIS "Panorama"/"Integratsia" raster files. Based on implementation
* of Kent Williams, but heavily modified over it. The key point
* in the initial implementation is a hashing algorithm.
* Author: Andrey Kiselev, dron@ak4719.spb.edu
*
******************************************************************************
* Copyright (c) 2007, Andrey Kiselev <dron@ak4719.spb.edu>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
******************************************************************************
* COPYRIGHT NOTICE FROM THE INITIAL IMPLEMENTATION:
*
* The programs LZWCOM and LZWUNC, both in binary executable and source forms,
* are in the public domain. No warranty is given or implied, and no
* liability will be assumed by the author.
*
* Everyone on earth is hereby given permission to use, copy, distribute,
* change, mangle, destroy or otherwise employ these programs, provided they
* hurt no one but themselves in the process.
*
* Kent Williams
* Norand Inc.
* 550 2nd St S.E.
* Cedar Rapids, Iowa 52401
* (319) 369-3131
****************************************************************************/
#include "cpl_conv.h"
#include "rmfdataset.h"
// Code marks that there is no predecessor in the string
#define NO_PRED 0xFFFF
// We are using 12-bit codes in this particular implementation
#define TABSIZE 4096U
#define STACKSIZE TABSIZE
/************************************************************************/
/* LZWStringTab */
/************************************************************************/
typedef struct
{
int bUsed;
GUInt32 iNext; // hi bit is 'used' flag
GUInt32 iPredecessor; // 12 bit code
GByte iFollower;
} LZWStringTab;
/************************************************************************/
/* LZWUpdateTab() */
/************************************************************************/
static void LZWUpdateTab(LZWStringTab *poCodeTab, GUInt32 iPred, char bFoll)
{
GUInt32 nNext;
/* -------------------------------------------------------------------- */
/* Hash uses the 'mid-square' algorithm. I.E. for a hash val of n bits */
/* hash = middle binary digits of (key * key). Upon collision, hash */
/* searches down linked list of keys that hashed to that key already. */
/* It will NOT notice if the table is full. This must be handled */
/* elsewhere */
/* -------------------------------------------------------------------- */
GUInt32 nLocal = (iPred + bFoll) | 0x0800;
nLocal = (nLocal*nLocal >> 6) & 0x0FFF; // middle 12 bits of result
// If string is not used
if (poCodeTab[nLocal].bUsed == FALSE)
nNext = nLocal;
else
{
// If collision has occured
while ( (nNext = poCodeTab[nLocal].iNext) != 0 )
nLocal = nNext;
// Search for free entry from nLocal + 101
nNext = (nLocal + 101) & 0x0FFF;
while ( poCodeTab[nNext].bUsed )
{
if ( ++nNext >= TABSIZE )
nNext = 0;
}
// Put new tempnext into last element in collision list
poCodeTab[nLocal].iNext = nNext;
}
poCodeTab[nNext].bUsed = TRUE;
poCodeTab[nNext].iNext = 0;
poCodeTab[nNext].iPredecessor = iPred;
poCodeTab[nNext].iFollower = bFoll;
}
/************************************************************************/
/* LZWDecompress() */
/************************************************************************/
int RMFDataset::LZWDecompress( const GByte* pabyIn, GUInt32 nSizeIn,
GByte* pabyOut, GUInt32 nSizeOut )
{
GUInt32 nCount = TABSIZE - 256;
GUInt32 iCode, iOldCode, iInCode;
GByte iFinChar, bLastChar=FALSE;
LZWStringTab *poCodeTab;
int bBitsleft;
if ( pabyIn == 0 ||
pabyOut == 0 ||
nSizeOut < nSizeIn ||
nSizeIn < 2 )
return 0;
// Allocate space for the new table and pre-fill it
poCodeTab = (LZWStringTab *)CPLMalloc( TABSIZE * sizeof(LZWStringTab) );
if ( !poCodeTab )
return 0;
memset( poCodeTab, 0, TABSIZE * sizeof(LZWStringTab) );
for ( iCode = 0; iCode < 256; iCode++ )
LZWUpdateTab( poCodeTab, NO_PRED, (char)iCode );
// The first code is always known
iCode = (*pabyIn++ << 4) & 0xFF0; nSizeIn--;
iCode += (*pabyIn >> 4) & 0x00F;
iOldCode = iCode;
bBitsleft = TRUE;
*pabyOut++ = iFinChar = poCodeTab[iCode].iFollower; nSizeOut--;
// Decompress the input buffer
while ( nSizeIn > 0 )
{
int bNewCode;
GUInt32 nStackCount = 0;
GByte abyStack[STACKSIZE];
GByte *pabyTail = abyStack + STACKSIZE;
// Fetch 12-bit code from input stream
if ( bBitsleft )
{
iCode = ((*pabyIn++ & 0x0F) << 8) & 0xF00; nSizeIn--;
if ( nSizeIn <= 0 )
break;
iCode += *pabyIn++; nSizeIn--;
bBitsleft = FALSE;
}
else
{
iCode = (*pabyIn++ << 4) & 0xFF0; nSizeIn--;
if ( nSizeIn <= 0 )
break;
iCode += (*pabyIn >> 4) & 0x00F;
bBitsleft = TRUE;
}
iInCode = iCode;
// Do we have unknown code?
if ( poCodeTab[iCode].bUsed )
bNewCode = FALSE;
else
{
iCode = iOldCode;
bLastChar = iFinChar;
bNewCode = TRUE;
}
while ( poCodeTab[iCode].iPredecessor != NO_PRED )
{
// Stack overrun
if ( nStackCount >= STACKSIZE )
goto bad;
// Put the decoded character into stack
*(--pabyTail) = poCodeTab[iCode].iFollower; nStackCount++;
iCode = poCodeTab[iCode].iPredecessor;
}
if ( !nSizeOut )
goto bad;
// The first character
*pabyOut++ = iFinChar = poCodeTab[iCode].iFollower; nSizeOut--;
// Output buffer overrun
if ( nStackCount > nSizeOut )
goto bad;
// Now copy the stack contents into output buffer. Our stack was
// filled in reverse order, so no need in character reordering
memcpy( pabyOut, pabyTail, nStackCount );
nSizeOut -= nStackCount;
pabyOut += nStackCount;
// If code isn't known
if ( bNewCode )
{
// Output buffer overrun
if ( !nSizeOut )
goto bad;
*pabyOut++ = iFinChar = bLastChar; // the follower char of last
nSizeOut--;
}
if ( nCount > 0 )
{
nCount--;
// Add code to the table
LZWUpdateTab( poCodeTab, iOldCode, iFinChar );
}
iOldCode = iInCode;
}
CPLFree( poCodeTab );
return 1;
bad:
CPLFree( poCodeTab );
return 0;
}