強火で進め

このブログではプログラム関連の記事を中心に書いてます。

RLE圧縮されたTGAファイルの解凍方法

今回、TGAファイルをテクスチャに読み込むプログラムをテストしようとMacのプレビューでTGAファイルを変換しました。

しかし、プレビューで変換するとRLE圧縮のかかったTGAファイルが作成される様で正しく読み込めませんでした。そこでRLE圧縮されたものにも対応するためにちょっとRLE圧縮について調査しました。

なかなかハマりポイントが多かったのでメモっておきます。

RLE圧縮

まず、RLE(Run Length Encoding、ランレングス)圧縮について解説しておきます。

RLE圧縮とは簡単に説明すると

AAAAABBBB

の様なデータが有ったとき「繰り返す回数」+「データ」の組み合わせに変換し、データを以下の様に圧縮する手法です。

5A4B

5が「繰り返す回数」となり、Aが「データ」となります。この様に圧縮されたデータはプログラム上で「Aを5回繰り返す」というプログラムで解凍が可能です。

この様なルールで圧縮しているため同じデータが連続する場合は圧縮率が高くなるのですが連続しない場合は返ってデータ量が増加するという厄介な特徴があります。
例えば

ABC

この様なデータを圧縮すると

1A1B1C

となってしまい、元のデータ量より増加してしまいます。

この問題に対処するためにTGAファイルでは「連続するデータ」「連続しないデータ」の2種類のデータが混在している様です。

「連続するデータ」については先ほど説明した 5A4B などと記述する手法です。
もうひとつの「連続しないデータ」ですがこちらは「繰り返す回数」+「データ」の逆で「繰り返さない回数」+「データ」の組み合わせになっています。

例えば

ABCDDDDD

このようなデータを圧縮する場合、先頭部分 ABC については「連続しないデータ」として圧縮します。3つ、連続しないデータが有るため圧縮後のデータは 3ABC となります。続く DDDDD については繰り返しているため「連続するデータ」として処理し、圧縮後は 5D と記述します。

ABCDDDDD

↓圧縮

3ABC5D

もちろんこの方法でもデータが2つずつ連続する以下の様なデータでは返ってデータが増加します。しかし、そのようなデータばかりになる可能性は少ないですし、「連続しないデータ」を導入する前に比べるとかなり改善されていることが分かります。

ABCC

↓圧縮

2AB2C

実例 - RLE圧縮されたTGAファイルの画像データを解凍する

これから実際にTGAファイルの画像データを解凍する方法を解説します。
※プログラムの記述が必要なところは原則、C言語で解説しますが他の言語でもほぼ同様の記述で対応できると思います。
※ヘッダ部分の解説は省略します。ヘッダ部分については以下のサイトなどを参照下さい。

☆PROJECT ASURA☆ [OpenGL] 『テクスチャを読み込む!!(2) 〜TGAファイル〜』
http://asura.iaigiri.com/OpenGL/gl5.html

あるTGAファイル(24bit画像)から画像データを先頭から10byte抽出したところ以下になっていました(データは10進数で表示しています)。

画像データの先頭8byte

131 29 36 231 7 45 50 232

まず、先頭の1Byteを取り出します。このデータは「連続するデータの数」か連続しないデータの数」のどちらかとその回数を示しており、

  • 最上位ビットがセット(1のとき)の場合→「連続するデータの数」
  • 最上位ビットがセット(0のとき)の場合→「連続しないデータの数」

となっています。

実際のプログラムでは1Byte=8Bitの最上位ビットのデータの取得なので 131 >> 7 を行うことで取得できます。結果は 1 となるので「連続するデータ」として処理することになります。「回数」については最上位ビット以外の残り7bitの部分で示される値となります。

131をビットで表現すると以下の様になり。

1000:0011

最上位ビットがセットされ、残りの7bitの部分を確認すると3だと確認できます。

しかし、ここにもう一つ工夫がされています。この値は「繰り返す回数」を示すために使われるため値が0ということはあり得ません。そのためここで示される値は0の場合1、1の場合2と実際の値より1小さい(実際に「繰り返す回数」を使うときは+1して使う)というルールになっています。

今回の場合、「繰り返す回数」を取得するプログラムは以下となります。

(131 & 127) + 1

131 をそのまま使うと最上位ビットも含まれたままになってしまうので 127(0111:1111) でマスクをし、先ほど説明した「実際の値より1小さい」のルールが有るので実際の値に戻すために + 1 します。

画像データの先頭8byte

131 29 36 231 7 45 50 232

そして、続く3byteの 29 36 231 がピクセルの値です。データはBGRの順で並んでいる事に注意して下さい。

以上の解析結果をまとめると以下の様になります。

  • 連続するデータ
  • 回数は4
  • R=231、G=36、B=131

続いてデータを読み進めると確認するデータは 7 と 45 50 232 になります。
7は 0000:0110 なので最上位ビットはクリア(ビットが立ってない)ため解析結果は以下となります。

  • 連続しないデータ
  • 回数は8

確認のため今回の使ったTGAファイルのピクセルデータをデバックのためにRGBの順番でログに出力したところ以下の様になりました。

231 36 29
231 36 29
231 36 29
231 36 29
232 50 45
233 64 60
233 70 66
233 81 78
236 108 106
242 162 161
250 223 223
254 251 251
255 255 255
255 255 255
255 255 255

「231 36 29」が4回繰り返し、その後に8回連続しない値が繰り返していることが確認できます。

RLE圧縮の解説は以上です。今回説明した処理を全ての画像データについて行うことにより、圧縮されたデータを解凍できます。

実際の解凍プログラムについては以前紹介したこちらのライブラリの tga_load() 関数を参照下さい。

ちなみにBMPファイルもRLE圧縮に対応しています。BMPについてはこちらのサイトが丁寧な解説が有り。おすすめです。

プログラミング/BMPファイル仕様/RLEによる圧縮 - ルーチェ's Homepage
http://www.ruche-home.net/?%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0%2FBMP%A5%D5%A5%A1%A5%A4%A5%EB%BB%C5%CD%CD%2FRLE%A4%CB%A4%E8%A4%EB%B0%B5%BD%CC#about