TwlIPL/build/tests/compressSharedFontLoad/compSharedFont/ntrcomp/src/rangeCoder.c
nishikawa_takeshi b4e285d208 フォント圧縮テスト:compBLZとntrcompを切り替えることができるように変更。
git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/TwlIPL/trunk@1816 b08762b0-b915-fc4b-9d8c-17b2551a87ff
2008-07-08 06:49:39 +00:00

540 lines
13 KiB
C

/*---------------------------------------------------------------------------*
Project: NinTendo Compress tool
File: rangeCoder.c
Copyright 2007 Nintendo. All rights reserved.
These coded instructions, statements, and computer programs contain
proprietary information of Nintendo of America Inc. and/or Nintendo
Company Ltd., and are protected by Federal copyright law. They may
not be disclosed to third parties or copied or duplicated in any form,
in whole or in part, without the prior written consent of Nintendo.
$Date:: $
$Rev$
$Author$
*---------------------------------------------------------------------------*/
#include "rangeCoder.h"
typedef struct
{
u32 freq[ 0x100 ];
u32 low_cnt[ 0x100 ];
u32 total;
}
RCCompressionInfo;
// レンジコーダ状態構造体
typedef struct
{
u32 low;
u32 range;
u32 code; // 展開時のみ使用
u8 carry; // 圧縮時のみ使用
u32 carry_cnt; // 圧縮時のみ使用
}
RCState;
#define MAX_RANGE 0x80000000
/*---------------------------------------------------------------------------*
Name: RCInitInfo_
Description:
Arguments: info
Returns: None.
*---------------------------------------------------------------------------*/
static void
RCInitInfo_( RCCompressionInfo* info, BOOL adaptive )
{
u32 i;
if ( adaptive )
// 適応型レンジコーダ
{
for ( i = 0; i < 0x100; i++ )
{
info->freq[ i ] = 1;
info->low_cnt[ i ] = i;
}
info->total = 0x100;
}
else
// 静的レンジコーダ
{
for ( i = 0; i < 0x100; i++ )
{
info->freq[ i ] = 0;
info->low_cnt[ i ] = 0;
}
info->total = 0;
}
}
/*---------------------------------------------------------------------------*
Name: RCCount_
Description:
Arguments: info
srcp
size
Returns: None.
*---------------------------------------------------------------------------*/
static void
RCCount_( RCCompressionInfo* info, const u8* srcp, u32 size )
{
u32 srcCnt = 0;
u32 i;
while ( srcCnt < size )
{
info->freq[ srcp[ srcCnt ] ]++;
info->total++;
srcCnt++;
}
// 0x10000へ正規化
#define NORMAL_FREQ 0x10000
{
f32 rate = (f32)NORMAL_FREQ / info->total;
u32 max_i = 0;
u32 max_freq = 0;
info->total = 0;
for ( i = 0; i < 0x100; i++ )
{
u32 orig = info->freq[ i ];
info->freq[ i ] = (u32)(rate * info->freq[ i ] + 0.5f);
if ( orig != 0 && info->freq[ i ] == 0 )
{
info->freq[ i ] = 1;
}
info->total += info->freq[ i ];
if ( info->freq[ i ] >= max_freq )
{
max_i = i;
max_freq = info->freq[ i ];
}
}
if ( info->total > NORMAL_FREQ )
{
info->freq[ max_i ] -= (info->total - NORMAL_FREQ);
}
else
{
info->freq[ max_i ] += (NORMAL_FREQ - info->total);
}
info->total = NORMAL_FREQ;
}
#undef NORMAL_FREQ
info->low_cnt[ 0 ] = 0;
for ( i = 1; i < 0x100; i++ )
{
info->low_cnt[ i ] = info->low_cnt[ i - 1 ] + info->freq[ i - 1 ];
}
}
/*---------------------------------------------------------------------------*
Name: RCAAddCount_
Description: 適応型レンジコーダの頻度テーブルを更新します。
Arguments: info
val
Returns: None.
*---------------------------------------------------------------------------*/
static void
RCAAddCount_( RCCompressionInfo* info, u8 val )
{
u32 i;
info->freq[ val ]++;
info->total++;
for ( i = val + 1; i < 0x100; i++ )
{
info->low_cnt[ i ]++;
}
// トータルが最大値を越えた場合には、再構成する。
if ( info->total >= 0x00010000 )
{
if ( info->freq[ 0 ] > 1 )
{
info->freq[ 0 ] = info->freq[ 0 ] / 2;
}
info->low_cnt[ 0 ] = 0;
info->total = info->freq[ 0 ];
for ( i = 1; i < 0x100; i++ )
{
if ( info->freq[ i ] > 1 )
{
info->freq[ i ] >>= 1;
}
info->low_cnt[ i ] = info->low_cnt[ i - 1 ] + info->freq[ i - 1 ];
info->total += info->freq[ i ];
}
}
}
/*---------------------------------------------------------------------------*
Name: RCCompWrite
Description: レンジコーダの圧縮(テーブル型)
Arguments: srcp
size
dstp
Returns:
*---------------------------------------------------------------------------*/
s32
RCCompWrite_( const u8* srcp, u32 size, u8* dstp, BOOL adaptive )
{
#define MIN_RANGE 0x01000000
static RCCompressionInfo sInfo;
u32 srcCnt = 0;
u32 dstCnt = 0;
u32 low, range;
u8 carry;
u32 carry_cnt;
u32 i;
// ワークの初期化
RCInitInfo_( &sInfo, adaptive );
if ( ! adaptive )
{
// 出現頻度を計算
RCCount_( &sInfo, srcp, size );
}
// 開始Rangeが0x80000000なので、初回いきなり桁上げが発生することはない
low = 0;
range = MAX_RANGE;
carry = 0;
carry_cnt = 0;
// ヘッダ出力
dstp[ dstCnt++ ] = LRC_CODE_HEADER;
if ( size > 0x1000000 )
{
dstp[ dstCnt++ ] = 0;
dstp[ dstCnt++ ] = 0;
dstp[ dstCnt++ ] = 0;
}
dstp[ dstCnt++ ] = (u8)( size );
dstp[ dstCnt++ ] = (u8)( size >> 8 );
dstp[ dstCnt++ ] = (u8)( size >> 16 );
if ( size > 0x1000000 )
{
dstp[ dstCnt++ ] = (u8)( size >> 24 );
}
if ( ! adaptive )
{
// 頻度テーブルの出力(16bitリトルエンディアン)
for ( i = 0; i < 0x100; i++ )
{
dstp[ dstCnt++ ] = (u8)( sInfo.freq[ i ] );
dstp[ dstCnt++ ] = (u8)( sInfo.freq[ i ] >> 8 );
}
}
// コードの出力
while ( srcCnt < size )
{
u8 val = srcp[ srcCnt++ ];
u32 temp = range / sInfo.total;
u32 prevLow = low;
low = low + sInfo.low_cnt[ val ] * temp;
range = sInfo.freq[ val ] * temp;
if ( adaptive )
{
// 出現頻度テーブルを更新
RCAAddCount_( &sInfo, val );
}
// 桁上がりが発生する場合の処理
if ( prevLow > low )
{
// キャリーを1繰上げ
++carry;
// キャリーと(キャリーカウンタ - 1)分の0x00を出力します。
if ( carry_cnt > 1 )
{
dstp[ dstCnt++ ] = carry;
--carry_cnt;
carry = 0x00;
}
while ( carry_cnt > 1 )
{
dstp[ dstCnt++ ] = 0x00;
--carry_cnt;
}
}
// Rangeの上位1バイトが空になったら桁上げ
while ( range < MIN_RANGE )
{
u8 candidate = (u8)( low >> 24 );
// 次のキャリーが0xFFの場合は更に桁上げがあり得るのでcarryを出力せずにcarry_cntだけ増やす
if ( candidate == 0xFF )
{
++carry_cnt;
}
else
// 次のキャリーが0xFFではない場合はcarryを出力する
{
// carryと(carry_cnt - 1)分の0xFFを出力する
if ( carry_cnt > 0 )
{
dstp[ dstCnt++ ] = carry;
--carry_cnt;
}
while ( carry_cnt > 0 )
{
dstp[ dstCnt++ ] = 0xFF;
--carry_cnt;
}
// 新しいcarryに置き換え
carry = candidate;
carry_cnt = 1;
}
low <<= 8;
range <<= 8;
}
}
if ( carry_cnt > 0 )
{
dstp[ dstCnt++ ] = carry;
--carry_cnt;
}
while ( carry_cnt > 0 )
{
dstp[ dstCnt++ ] = 0xFF;
--carry_cnt;
}
dstp[ dstCnt++ ] = (u8)( (low >> 24) & 0xFF );
dstp[ dstCnt++ ] = (u8)( (low >> 16) & 0xFF );
dstp[ dstCnt++ ] = (u8)( (low >> 8 ) & 0xFF );
dstp[ dstCnt++ ] = (u8)( (low >> 0 ) & 0xFF );
return dstCnt;
#undef MIN_RANGE
}
/*---------------------------------------------------------------------------*
Name: RCACompWrite
Description: レンジコーダの圧縮(適応型)
Arguments: srcp
size
dstp
Returns:
*---------------------------------------------------------------------------*/
s32
RCCompWrite( const u8* srcp, u32 size, u8* dstp )
{
return RCCompWrite_( srcp, size, dstp, FALSE );
}
s32
RCACompWrite( const u8* srcp, u32 size, u8* dstp )
{
return RCCompWrite_( srcp, size, dstp, TRUE );
}
static u8
SearchRC_( RCCompressionInfo* info, u32 code, u32 range, u32 low )
{
#define TABLE_SIZE 0x100
u32 codeVal = code - low;
u32 i;
u32 temp = range / info->total;
u32 tempVal = codeVal / temp;
#if 0
// TODO: とりあえず線形探索、二分探索にするべき
for ( i = 0; i < TABLE_SIZE - 1; i++ )
{
if ( info->low_cnt[ i + 1 ] > tempVal )
{
while ( info->freq[ i ] == 0 )
{
--i;
}
return (u8)i;
}
}
return TABLE_SIZE - 1;
#else
// 二分探索
u32 left = 0;
u32 right = TABLE_SIZE - 1;
while ( left < right )
{
i = (left + right) / 2;
if ( info->low_cnt[ i ] > tempVal )
{
right = i;
}
else
{
left = i + 1;
}
}
i = left;
while ( info->low_cnt[ i ] > tempVal )
{
--i;
}
return (u8)i;
#endif
}
/*---------------------------------------------------------------------------*
Name: RCCompRead
Description: レンジコーダ展開(テーブル型)
Arguments: srcp
size
dstp
Returns:
*---------------------------------------------------------------------------*/
s32
RCCompRead_( const u8* srcp, u32 srcSize, u8* dstp, BOOL adaptive )
{
#define MIN_RANGE 0x01000000
static RCCompressionInfo sInfo;
u32 dstSize = *(u32*)srcp >> 8;
u32 dstCnt = 0;
u32 srcCnt = 0;
u32 i;
u32 code, range, low;
if ( srcSize < 4 )
{
return -1;
}
srcCnt = 4;
if ( dstSize == 0 )
{
dstSize = *(u32*)&srcp[ srcCnt ];
srcCnt += 4;
if ( srcSize < 8 )
{
return -1;
}
}
if ( srcSize < sizeof(u16) * 0x100 + srcCnt )
{
return -1;
}
// ワークの初期化
if ( adaptive )
{
RCInitInfo_( &sInfo, TRUE );
}
else
{
sInfo.total = 0;
for ( i = 0; i < 0x100; i++ )
{
sInfo.freq[ i ] = *(u16*)&srcp[ srcCnt ];
srcCnt += 2;
sInfo.total += sInfo.freq[ i ];
}
sInfo.low_cnt[ 0 ] = 0;
for ( i = 1; i < 0x100; i++ )
{
sInfo.low_cnt[ i ] = sInfo.low_cnt[ i - 1 ] + sInfo.freq[ i - 1 ];
}
}
code = (u32)((srcp[ srcCnt ] << 24) | (srcp[ srcCnt + 1 ] << 16) |
(srcp[ srcCnt + 2 ] << 8) | (srcp[ srcCnt + 3 ]));
range = MAX_RANGE;
low = 0;
srcCnt += 4;
while ( dstCnt < dstSize )
{
u8 val = SearchRC_( &sInfo, code, range, low );
dstp[ dstCnt++ ] = val;
{
u32 tmp;
tmp = range / sInfo.total;
low = low + sInfo.low_cnt[ val ] * tmp;
range = sInfo.freq[ val ] * tmp;
}
if ( adaptive )
{
// 出現頻度テーブルを更新
RCAAddCount_( &sInfo, val );
}
while ( range < MIN_RANGE )
{
code <<= 8;
code += srcp[ srcCnt++ ];
range <<= 8;
low <<= 8;
}
}
return dstSize;
#undef MIN_RANGE
}
/*---------------------------------------------------------------------------*
Name: RCACompRead
Description: レンジコーダ展開(適応型)
Arguments: srcp
size
dstp
Returns:
*---------------------------------------------------------------------------*/
s32
RCCompRead( const u8* srcp, u32 srcSize, u8* dstp )
{
return RCCompRead_( srcp, srcSize, dstp, FALSE );
}
s32
RCACompRead( const u8* srcp, u32 srcSize, u8* dstp )
{
return RCCompRead_( srcp, srcSize, dstp, TRUE );
}