home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DOS/V Power Report 2001 January
/
VPR0101A.BIN
/
OLS
/
LZR100
/
lzr100.lzh
/
Source
/
GIFDecode.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2000-10-07
|
9KB
|
249 lines
/*--------------------------------------------------------------------------------------*/
/* 非LZW理論GIFデコーダー本体 実装ファイル Ver 1.00 */
/* for VisualStudio6.0 only */
/* ※実際の展開処理を行い、DIBとして返却します。 */
/* Copyright (C) 2000 DJ☆Uchi [H.Uchida] */
/*--------------------------------------------------------------------------------------*/
#include "GIFDecode.h"
/*--------------------------------------------------------------------------------------*/
/* GifDecode() */
/* GIF画像(最初の一枚のみ)を展開する。 */
/* ※入力はGIFデータ、出力はDIB。 */
/* ※ここでは主にデコードの前処理を行います。 */
/*--------------------------------------------------------------------------------------*/
void GifDecode(BYTE *rgb, BYTE *gif, DWORD width, DWORD height,
DWORD align, DWORD inter ,WORD color, DWORD size)
{
WORD lzw[8192]; //圧縮データ格納用配列(辞書テーブルではない!)
DWORD code = 0; //符号化コード初期化
//GIFデータ解析用構造体初期化
GIFDECODESTATUS gs;
gs.code_size = gif[0]; //コードサイズ取得
gs.bit_size = gs.code_size + 1; //CBL初期化(コードサイズ+1)
gs.clear_code = 1 << gs.code_size; //クリアコード設定(2^コードサイズ)
gs.end_code = gs.clear_code + 1; //エンドコード設定(2^コードサイズ+1)
gs.entry = gs.end_code + 1; //エントリ数初期化(2^コードサイズ+2)
gs.bit_pt = 8; //ビットポインタ初期化(8bit=1byte)
gs.next_block = 1; //次ブロック位置初期化(先頭ブロック)
gs.data_size = size; //GIFデータサイズ取得
//DIBデータ書き込み用構造体初期化
RGBDECODESTATUS rs;
rs.gs = &gs; //GIFDECODESTATUS構造体参照用ポインタ取得
rs.rgb_pt = 0; //展開データ書き込み位置初期化
rs.rgb_offset = (height - 1) * align; //書き込み位置オフセット
rs.rgb_width = width; //DIB横pixel数取得
rs.rgb_height = height; //DIB縦pixel数取得
rs.rgb_align = align; //DIBアラインメント値取得
rs.rgb_color = color; //DIBカラービット値取得
rs.rgb_line = 0; //展開データ書き込みライン初期化
rs.inter_offset = 8; //インタレースオフセット初期化
rs.inter_flag = inter; //インタレースフラグ取得
int x = 0; //展開回数初期化
int y = 0; //展開開始位置初期化
//エンドコードが現れるまでひたすらループ
while ((code = GetCode(gif,&gs)) != gs.end_code) {
//クリアコードが現れた場合
if (code == gs.clear_code) {
//エントリ数&CBL初期化
gs.entry = gs.end_code + 1;
gs.bit_size = gs.code_size + 1;
//展開関数をコール。
lzw_decode(rgb, lzw, &rs, x, y);
x = 0;
y = 0;
} else {
//WORDサイズ配列に取得した符号化コードを溜め込む。
//これは展開作業をより円滑に行う為であり、辞書テーブルとは異なります。
//配列に溜め込まずに取得した符号化コードを直に展開することも出来ますが、
//速度的に問題がある為、この方法を採用しています。
lzw[x++] = (WORD)code;
//いつまでもクリアコードが現れないと配列が溢れるため、
//符号化コードが8192個貯まった時点で展開作業を行う。
//非圧縮GIF対策か?
if (x == 8192) {
lzw_decode(rgb, lzw, &rs, x, y);
x = 4096;
y = 4096;
}
if (gs.entry < 4095) gs.entry++;
}
}
//配列に残っている符号化コードを展開する。
lzw_decode(rgb, lzw, &rs, x, y);
return;
}
/*--------------------------------------------------------------------------------------*/
/* GetCode() */
/* 可変ビット長入力関数。 */
/* ※符号化コードを一つ取り出して、ビット位置をインクリメントする。 */
/*--------------------------------------------------------------------------------------*/
DWORD GetCode(BYTE *gif, GIFDECODESTATUS *gs)
{
DWORD code = 0; //符号化コード初期化
DWORD pt = gs->bit_pt >> 3; //読み込み位置(バイト単位)取得
//サイズオーバーフローの場合、強制的にエンドコードを返す(破損ファイル対策)
if (pt + 2 > gs->data_size) return gs->end_code;
//符号化コード取得
int i = 0; //読み込みバイト数初期化
while ((gs->bit_pt + gs->bit_size - 1) >> 3 >= pt) {
//読み込み中にブロックが終了した場合
if (pt == gs->next_block) {
gs->next_block += gif[pt++] + 1; //次ブロック位置更新
gs->bit_pt += 8; //ビットポインタを1バイト分加算
}
code += gif[pt++] << i; //コード取得
i += 8;
}
//得られたコードの余分なビットを切りとばす。(マスキング処理)
code = (code >> (gs->bit_pt & 0x07)) & ((1 << gs->bit_size) - 1);
//ビットポインタ更新
gs->bit_pt += gs->bit_size;
//CBLをインクリメントする必要があるかどうか確認する。
if (gs->entry > (DWORD)((1 << gs->bit_size) - 1)) gs->bit_size++;
return code;
}
/*--------------------------------------------------------------------------------------*/
/* lzw_decode() */
/* 非LZW理論展開関数(メインループ) */
/* ※WORDサイズの配列に格納された符号化コードをデコードします。 */
/*--------------------------------------------------------------------------------------*/
void lzw_decode(BYTE *rgb, WORD *lzw, RGBDECODESTATUS *rs, int x, int y)
{
//単にループを回して展開関数を呼んでるだけ。
for (int i=y; i<x; i++) {
lzw_string(rgb, lzw, rs, i);
}
}
/*--------------------------------------------------------------------------------------*/
/* lzw_string() */
/* 非LZW理論展開関数(コア) */
/* ※非LZW理論の核。 */
/* ※指定された符号化コードに対する展開データを返します。 */
/*--------------------------------------------------------------------------------------*/
void lzw_string(BYTE *rgb, WORD *lzw, RGBDECODESTATUS *rs, int x)
{
//配列から符号化コードを一つ取り出します。
WORD code = lzw[x];
//符号化コードが”色数”より小さい場合
if (code < rs->gs->clear_code) {
//符号化コードをそのままDIBに書き込む。
rgb_write(rgb, rs, code);
//符号化コードが未知のものである場合
} else if (code > rs->gs->end_code + x) {
//一つ前の展開データ
lzw_string(rgb, lzw, rs, --x);
//一つ前の展開データの先頭一個
lzw_char(rgb, lzw, rs, --x);
//符号化コードが”色数+1”より大きい場合
//ちなみに、この関数に入ってくるコードにエンドコードやクリアコードは
//絶対に現れませんので、その場合の処理は考慮されていません。
} else {
//(符号化コード-色数+1)の展開データ
lzw_string(rgb, lzw, rs, code - rs->gs->end_code - 1);
//(符号化コード-色数+2)の展開データの先頭一個
lzw_char(rgb, lzw, rs, code - rs->gs->end_code);
}
}
/*--------------------------------------------------------------------------------------*/
/* lzw_char() */
/* 非LZW理論展開関数(サブ) */
/* ※非LZW理論の核。 */
/* ※指定された符号化コードに対する展開データの先頭1つを返します。 */
/*--------------------------------------------------------------------------------------*/
void lzw_char(BYTE *rgb, WORD *lzw, RGBDECODESTATUS *rs, int x)
{
//配列から符号化コードを一つ取り出します。
WORD code = lzw[x];
//符号化コードが”色数”より小さい場合
if (code < rs->gs->clear_code) {
//符号化コードをそのままDIBに書き込む。
rgb_write(rgb, rs, code);
//符号化コードが未知のものである場合
} else if (code > rs->gs->end_code + x) {
//一つ前の展開データの先頭一個
lzw_char(rgb, lzw, rs, --x);
//符号化コードが”色数+1”より大きい場合
} else {
//(符号化コード-色数+1)の展開データの先頭一個
lzw_char(rgb, lzw, rs, code - rs->gs->end_code - 1);
}
}
/*--------------------------------------------------------------------------------------*/
/* rgb_write() */
/* DIB画像データ書き込み関数。 */
/* ※展開された画像データをDIBとして書き込みます。 */
/*--------------------------------------------------------------------------------------*/
void rgb_write(BYTE *rgb, RGBDECODESTATUS *rs, WORD code)
{
//DIBに画像データを書き込みます。
//モノクロや16色の場合はビット単位での書き込みになる為、マスキング処理を行います。
DWORD i, j;
if (rs->rgb_color == 1) { //モノクロ画像の場合
i = rs->rgb_offset + (rs->rgb_pt >> 3);
j = 7 - (rs->rgb_pt & 0x07);
rgb[i] = (rgb[i] & ~(1 << j)) | (code << j);
} else if (rs->rgb_color == 4) { //16色画像の場合
i = rs->rgb_offset + (rs->rgb_pt >> 1);
j = (rs->rgb_pt & 0x01) << 2;
rgb[i] = (rgb[i] & (0x0F << j)) | (code << (4 - j));
} else { //256色の場合
rgb[rs->rgb_offset + rs->rgb_pt] = (BYTE)code;
}
//書き込み位置をインクリメント
rs->rgb_pt++;
//書き込み位置がラインの終端に達した場合
if (rs->rgb_pt == rs->rgb_width) {
if (rs->inter_flag) { //インタレースGIFの場合
//インタレースラインが画面下端に達した場合
if (rs->rgb_line + rs->inter_offset >= rs->rgb_height){
if ((rs->rgb_line & 0x07) == 0) {
rs->rgb_offset = (rs->rgb_height - 5) * rs->rgb_align;
rs->rgb_line = 4;
} else if (rs->inter_offset == 8) {
rs->rgb_offset = (rs->rgb_height - 3) * rs->rgb_align;
rs->rgb_line = 2;
rs->inter_offset = 4;
} else if (rs->inter_offset == 4) {
rs->rgb_offset = (rs->rgb_height - 2) * rs->rgb_align;
rs->rgb_line = 1;
rs->inter_offset = 2;
}
} else {
rs->rgb_offset -= rs->rgb_align * rs->inter_offset;
rs->rgb_line += rs->inter_offset;
}
} else { //リニアGIFの場合
rs->rgb_offset -= rs->rgb_align;
}
rs->rgb_pt = 0;
}
}