home *** CD-ROM | disk | FTP | other *** search
/ DOS/V Power Report 2001 January / VPR0101A.BIN / OLS / LZR100 / lzr100.lzh / Source / GIFDecode.cpp < prev    next >
C/C++ Source or Header  |  2000-10-07  |  9KB  |  249 lines

  1. /*--------------------------------------------------------------------------------------*/
  2. /*    非LZW理論GIFデコーダー本体 実装ファイル    Ver 1.00                                    */
  3. /*    for VisualStudio6.0 only                                                            */
  4. /*    ※実際の展開処理を行い、DIBとして返却します。                                        */
  5. /*    Copyright (C) 2000 DJ☆Uchi [H.Uchida]                                                */
  6. /*--------------------------------------------------------------------------------------*/
  7. #include "GIFDecode.h"
  8.  
  9.  
  10. /*--------------------------------------------------------------------------------------*/
  11. /*    GifDecode()                                                                            */
  12. /*    GIF画像(最初の一枚のみ)を展開する。                                                */
  13. /*    ※入力はGIFデータ、出力はDIB。                                                        */
  14. /*    ※ここでは主にデコードの前処理を行います。                                            */
  15. /*--------------------------------------------------------------------------------------*/
  16. void GifDecode(BYTE *rgb, BYTE *gif, DWORD width, DWORD height,
  17.                DWORD align, DWORD inter ,WORD color, DWORD size)
  18. {
  19.     WORD lzw[8192];                            //圧縮データ格納用配列(辞書テーブルではない!)
  20.     DWORD code = 0;                            //符号化コード初期化
  21.  
  22.     //GIFデータ解析用構造体初期化
  23.     GIFDECODESTATUS gs;
  24.     gs.code_size    = gif[0];                //コードサイズ取得
  25.     gs.bit_size        = gs.code_size + 1;        //CBL初期化(コードサイズ+1)
  26.     gs.clear_code    = 1 << gs.code_size;    //クリアコード設定(2^コードサイズ)
  27.     gs.end_code        = gs.clear_code + 1;    //エンドコード設定(2^コードサイズ+1)
  28.     gs.entry        = gs.end_code + 1;        //エントリ数初期化(2^コードサイズ+2)
  29.     gs.bit_pt        = 8;                    //ビットポインタ初期化(8bit=1byte)
  30.     gs.next_block    = 1;                    //次ブロック位置初期化(先頭ブロック)
  31.     gs.data_size    = size;                    //GIFデータサイズ取得
  32.  
  33.     //DIBデータ書き込み用構造体初期化
  34.     RGBDECODESTATUS rs;
  35.     rs.gs            = &gs;                    //GIFDECODESTATUS構造体参照用ポインタ取得
  36.     rs.rgb_pt        = 0;                    //展開データ書き込み位置初期化
  37.     rs.rgb_offset    = (height - 1) * align;    //書き込み位置オフセット
  38.     rs.rgb_width    = width;                //DIB横pixel数取得
  39.     rs.rgb_height    = height;                //DIB縦pixel数取得
  40.     rs.rgb_align    = align;                //DIBアラインメント値取得
  41.     rs.rgb_color    = color;                //DIBカラービット値取得
  42.     rs.rgb_line        = 0;                    //展開データ書き込みライン初期化
  43.     rs.inter_offset    = 8;                    //インタレースオフセット初期化
  44.     rs.inter_flag    = inter;                //インタレースフラグ取得
  45.  
  46.     int x = 0;                                //展開回数初期化
  47.     int y = 0;                                //展開開始位置初期化
  48.  
  49.     //エンドコードが現れるまでひたすらループ
  50.     while ((code = GetCode(gif,&gs)) != gs.end_code) {
  51.         //クリアコードが現れた場合
  52.         if (code == gs.clear_code) {
  53.             //エントリ数&CBL初期化
  54.             gs.entry = gs.end_code + 1;
  55.             gs.bit_size = gs.code_size + 1;
  56.             //展開関数をコール。
  57.             lzw_decode(rgb, lzw, &rs, x, y);
  58.             x = 0;
  59.             y = 0;
  60.         } else {
  61.             //WORDサイズ配列に取得した符号化コードを溜め込む。
  62.             //これは展開作業をより円滑に行う為であり、辞書テーブルとは異なります。
  63.             //配列に溜め込まずに取得した符号化コードを直に展開することも出来ますが、
  64.             //速度的に問題がある為、この方法を採用しています。
  65.             lzw[x++] = (WORD)code;
  66.             //いつまでもクリアコードが現れないと配列が溢れるため、
  67.             //符号化コードが8192個貯まった時点で展開作業を行う。
  68.             //非圧縮GIF対策か?
  69.             if (x == 8192) {
  70.                 lzw_decode(rgb, lzw, &rs, x, y);
  71.                 x = 4096;
  72.                 y = 4096;
  73.             }
  74.             if (gs.entry < 4095) gs.entry++;
  75.         }
  76.     }
  77.     //配列に残っている符号化コードを展開する。
  78.     lzw_decode(rgb, lzw, &rs, x, y);
  79.  
  80.     return;
  81. }
  82.  
  83.  
  84. /*--------------------------------------------------------------------------------------*/
  85. /*    GetCode()                                                                            */
  86. /*    可変ビット長入力関数。                                                                */
  87. /*    ※符号化コードを一つ取り出して、ビット位置をインクリメントする。                    */
  88. /*--------------------------------------------------------------------------------------*/
  89. DWORD GetCode(BYTE *gif, GIFDECODESTATUS *gs)
  90. {
  91.     DWORD code = 0;                    //符号化コード初期化
  92.     DWORD pt = gs->bit_pt >> 3;        //読み込み位置(バイト単位)取得
  93.  
  94.     //サイズオーバーフローの場合、強制的にエンドコードを返す(破損ファイル対策)
  95.     if (pt + 2 > gs->data_size) return gs->end_code;
  96.  
  97.     //符号化コード取得
  98.     int i = 0;    //読み込みバイト数初期化
  99.     while ((gs->bit_pt + gs->bit_size - 1) >> 3 >= pt) {
  100.         //読み込み中にブロックが終了した場合
  101.         if (pt == gs->next_block) {
  102.             gs->next_block += gif[pt++] + 1;    //次ブロック位置更新
  103.             gs->bit_pt += 8;                    //ビットポインタを1バイト分加算
  104.         }
  105.         code += gif[pt++] << i;                    //コード取得
  106.         i += 8;
  107.     }
  108.  
  109.     //得られたコードの余分なビットを切りとばす。(マスキング処理)
  110.     code = (code >> (gs->bit_pt & 0x07)) & ((1 << gs->bit_size) - 1);
  111.     //ビットポインタ更新
  112.     gs->bit_pt += gs->bit_size;
  113.     //CBLをインクリメントする必要があるかどうか確認する。
  114.     if (gs->entry > (DWORD)((1 << gs->bit_size) - 1)) gs->bit_size++;
  115.  
  116.     return code;
  117. }
  118.  
  119.  
  120. /*--------------------------------------------------------------------------------------*/
  121. /*    lzw_decode()                                                                        */
  122. /*    非LZW理論展開関数(メインループ)                                                    */
  123. /*    ※WORDサイズの配列に格納された符号化コードをデコードします。                        */
  124. /*--------------------------------------------------------------------------------------*/
  125. void lzw_decode(BYTE *rgb, WORD *lzw, RGBDECODESTATUS *rs, int x, int y)
  126. {
  127.     //単にループを回して展開関数を呼んでるだけ。
  128.     for (int i=y; i<x; i++) {
  129.         lzw_string(rgb, lzw, rs, i);
  130.     }
  131. }
  132.  
  133.  
  134. /*--------------------------------------------------------------------------------------*/
  135. /*    lzw_string()                                                                        */
  136. /*    非LZW理論展開関数(コア)                                                            */
  137. /*    ※非LZW理論の核。                                                                    */
  138. /*    ※指定された符号化コードに対する展開データを返します。                                */
  139. /*--------------------------------------------------------------------------------------*/
  140. void lzw_string(BYTE *rgb, WORD *lzw, RGBDECODESTATUS *rs, int x)
  141. {
  142.     //配列から符号化コードを一つ取り出します。
  143.     WORD code = lzw[x];
  144.  
  145.     //符号化コードが”色数”より小さい場合
  146.     if (code < rs->gs->clear_code) {
  147.         //符号化コードをそのままDIBに書き込む。
  148.         rgb_write(rgb, rs, code);
  149.  
  150.     //符号化コードが未知のものである場合
  151.     } else if (code > rs->gs->end_code + x) {
  152.         //一つ前の展開データ
  153.         lzw_string(rgb, lzw, rs, --x);
  154.         //一つ前の展開データの先頭一個
  155.         lzw_char(rgb, lzw, rs, --x);
  156.  
  157.     //符号化コードが”色数+1”より大きい場合
  158.     //ちなみに、この関数に入ってくるコードにエンドコードやクリアコードは
  159.     //絶対に現れませんので、その場合の処理は考慮されていません。
  160.     } else {
  161.         //(符号化コード-色数+1)の展開データ
  162.         lzw_string(rgb, lzw, rs, code - rs->gs->end_code - 1);
  163.         //(符号化コード-色数+2)の展開データの先頭一個
  164.         lzw_char(rgb, lzw, rs, code - rs->gs->end_code);
  165.     }
  166. }
  167.  
  168.  
  169. /*--------------------------------------------------------------------------------------*/
  170. /*    lzw_char()                                                                            */
  171. /*    非LZW理論展開関数(サブ)                                                            */
  172. /*    ※非LZW理論の核。                                                                    */
  173. /*    ※指定された符号化コードに対する展開データの先頭1つを返します。                    */
  174. /*--------------------------------------------------------------------------------------*/
  175. void lzw_char(BYTE *rgb, WORD *lzw, RGBDECODESTATUS *rs, int x)
  176. {
  177.     //配列から符号化コードを一つ取り出します。
  178.     WORD code = lzw[x];
  179.  
  180.     //符号化コードが”色数”より小さい場合
  181.     if (code < rs->gs->clear_code) {
  182.         //符号化コードをそのままDIBに書き込む。
  183.         rgb_write(rgb, rs, code);
  184.  
  185.     //符号化コードが未知のものである場合
  186.     } else if (code > rs->gs->end_code + x) {
  187.         //一つ前の展開データの先頭一個
  188.         lzw_char(rgb, lzw, rs, --x);
  189.  
  190.     //符号化コードが”色数+1”より大きい場合
  191.     } else {
  192.         //(符号化コード-色数+1)の展開データの先頭一個
  193.         lzw_char(rgb, lzw, rs, code - rs->gs->end_code - 1);
  194.     }
  195. }
  196.  
  197.  
  198. /*--------------------------------------------------------------------------------------*/
  199. /*    rgb_write()                                                                            */
  200. /*    DIB画像データ書き込み関数。                                                            */
  201. /*    ※展開された画像データをDIBとして書き込みます。                                        */
  202. /*--------------------------------------------------------------------------------------*/
  203. void rgb_write(BYTE *rgb, RGBDECODESTATUS *rs, WORD code)
  204. {
  205.     //DIBに画像データを書き込みます。
  206.     //モノクロや16色の場合はビット単位での書き込みになる為、マスキング処理を行います。
  207.     DWORD i, j;
  208.     if (rs->rgb_color == 1) {            //モノクロ画像の場合
  209.         i = rs->rgb_offset + (rs->rgb_pt >> 3);
  210.         j = 7 - (rs->rgb_pt & 0x07);
  211.         rgb[i] = (rgb[i] & ~(1 << j)) | (code << j);
  212.     } else if (rs->rgb_color == 4) {    //16色画像の場合
  213.         i = rs->rgb_offset + (rs->rgb_pt >> 1);
  214.         j = (rs->rgb_pt & 0x01) << 2;
  215.         rgb[i] = (rgb[i] & (0x0F << j)) | (code << (4 - j));
  216.     } else {                            //256色の場合
  217.         rgb[rs->rgb_offset + rs->rgb_pt] = (BYTE)code;
  218.     }
  219.  
  220.     //書き込み位置をインクリメント
  221.     rs->rgb_pt++;
  222.     //書き込み位置がラインの終端に達した場合
  223.     if (rs->rgb_pt == rs->rgb_width) {
  224.         if (rs->inter_flag) {    //インタレースGIFの場合
  225.             //インタレースラインが画面下端に達した場合
  226.             if (rs->rgb_line + rs->inter_offset >= rs->rgb_height){
  227.                 if ((rs->rgb_line & 0x07) == 0) {
  228.                     rs->rgb_offset = (rs->rgb_height - 5) * rs->rgb_align;
  229.                     rs->rgb_line = 4;
  230.                 } else if (rs->inter_offset == 8) {
  231.                     rs->rgb_offset = (rs->rgb_height - 3) * rs->rgb_align;
  232.                     rs->rgb_line = 2;
  233.                     rs->inter_offset = 4;
  234.                 } else if (rs->inter_offset == 4) {
  235.                     rs->rgb_offset = (rs->rgb_height - 2) * rs->rgb_align;
  236.                     rs->rgb_line = 1;
  237.                     rs->inter_offset = 2;
  238.                 }
  239.             } else {
  240.                 rs->rgb_offset -= rs->rgb_align * rs->inter_offset;
  241.                 rs->rgb_line += rs->inter_offset;
  242.             }
  243.         } else {                //リニアGIFの場合
  244.             rs->rgb_offset -= rs->rgb_align;
  245.         }
  246.         rs->rgb_pt = 0;
  247.     }
  248. }
  249.