チラシの裏からうっすら見える外枠の外のメモ書き

新聞に挟まってる硬い紙のチラシの裏からうっすら見える外枠の外に走り書きされたようなものです。思いついたときにふらふらと。

Linuxやmacでbmp.hを利用するC言語プログラムを実行すると発生する問題について

学校でビットマップ画像を加工する実験がありました。

そこでは、静岡大学安藤和敏先生が作成されたライブラリbmp.h及びbmp.cを用いて画像を加工するのですが、Linuxmacでは画像を読み込む際(ReadBmp()関数)に次のようなエラーが発生したので記事にしておきます。

Error: Bmp_width = 0 > 1000 = MAXWIDTH!

発症環境

このエラーが発生するのは次の環境のユーザーです。

  • 64bit Linux系OSを搭載したPC
  • 64bit mac OSを搭載したPC
  • 64bit Linux系OSの機能をもつソフトウェアがインストールされたPC(WSLやVirtualBoxなど)

原因

この問題が発生する原因はLLP64のライブラリをLP64のコンピュータ上で実行しているという点です。

LLP64とLP64とは

LLP64、LP64とはコンピュータが採用している各型のサイズを取り決めたデータ型モデルです。

LLP64とLP64では一部の型サイズが異なります。

LLP64とLP64の違い
char short int long long long ポインター
LLP64
8bit
16bit
32bit
32bit
64bit
64bit
LP64
8bit
16bit
32bit
64bit
64bit
64bit

表にあるとおり、実はLLP64ではlong型が32bit=4byteであるのに対し、LP64ではlong型は64bit=8byteもあるんです。

そのため、long型を32bitだと思いこんで使用していると今回のように困ったことになります。

project-flora.net

今回の場合、ReadBmp()関数内ではファイルヘッダーに書き込まれている画像の高さ/幅 情報を取得する際にlong型のバイト数分、つまり8byteも読み出してしまうことが原因で必要以上のサイズとなってしまっています。

ビットマップ画像のフォーマットを確認してみると、ファイルヘッダーに記載されている高さ/幅情報はそれぞれ4byteです。

www.umekkii.jp

解決策

解決方法は単純で、long型で定義されているBmp_height及びBmp_widthをint型にするだけでです。

ただし、この解決方法は暫定的なものなので、C99/C++11に対応しているコンパイラコンパイルできる場合であればint型でなくint32_t型を使用します。

この型はLLP64/LP64問題が発生したことから生まれた型で、この型を使用すれば32bitのint型であることが保証されます。

以下が差分情報です。

bmp画像ライブラリの修正

この2つのpatchfileを適用してあげれば動作すると思います。

patchfileの適用方法は調べてください。

www.atmarkit.co.jp

あとがき

私がこの問題に遭遇したのはmacで、解決するのに1時間ぐらいかかりました。

最初はビットマップ画像を正しく読み込めていないのだろうとコンパイル引数を確かめたりしていましたがうまくいかず、macはビッグエンディアンなのかもしれないと思ってちょっと確認するもリトルエンディアンで問題なし。

ビットマップ画像のファイルヘッダー情報と読み込んできた情報が違うのかもしれないと思ってhexdumpを使ってビットマップ画像と変数の中身を比較していました。

結果4byteの範囲を8byte分も取得していたことがわかり、満足感を得られました。

 

ちなみに実験は終わりませんでした。