ゲームプログラミング/画像ファイルの作成プログラム
概要

もし、画像ファイルそのものをゲームから出力したい場合、
概要

そういう出力機能がOSに用意されていればそれを使えばよいが、実は専用の出力機能がなくてもC言語でバイナリー・データーファイルを作成・編集することによって、画像ファイルを作ることが原理的には可能である。
概要

画像ファイルだけでなく音声ファイルなども同様にバイナリー・データーの編集によって作成可能であるが、本ページでは画像ファイルを例に説明する。
概要

ただし、それら画像フォーマットの、バイナリー・データーでの記述の仕様を知る必要がある。かなり専門的な専門書や、もし出版されていなければネット検索などで、調べることになるだろう。
概要

一応、バイナリー・データーの直接編集をしなくても、windows にある MFC という機能にある cimage クラスのsaveメソッドなどの機能によって、画像ファイルの出力作成が可能ではある。 しかし、それをどうDirectX形式またはDXライブラリ形式のC++のプログラムに組み込めばいいのか、現在では書籍が乏しく不明である(MFCの書式は、一般のC++プログラミングとは微妙に異なる)。だからMFCによる方法は、ゲームとしては2020年以降の現在では期待できないだろう。
バイナリー・データーの読み書き

C言語のプログラムで、バイナリー・データーの読み書きをしたい場合、
バイナリー・データーの読み書き

fopen の引数で「r」(読み込み)とか「w」(書き込み)とか編集モードの指定がありますが、それに「b」をつけます。つまり「rb」や「wb」などの引数になります。
バイナリー・データーの読み書き

また、書き込む関数には fwrite を使う必要があります。 いっぽう、 fprintf では、テキストファイルに自動的に変換してしまいます。
バイナリー・データーの読み書き

fopenやfwriteの機能については、一般的な市販のC言語入門書に解説があるので、本ページではこれ以上は説明しない。
画像ファイルのバイナリー・データー

たとえばビットマップ画像を作りたい場合、
画像ファイルのバイナリー・データー

いわゆる「ビットマップ構造体」の仕様で決められた様式で、バイナリー・データーをファイルに書き込む。
画像ファイルのバイナリー・データー

すると、たいていのデスクトップ用OS(ウィンドウズも含む)では、ビットマップ形式(BMP)をサポートしているので、そのバイナリー・データーを画像ファイルとして認識してくれるので、クリックなどをするとOSが画像表示などをしてくれる。
画像ファイルのバイナリー・データー

なお、ネットや専門書の解説では画像フォーマットの仕様を説明するためによくC言語の「構造体」の書式が説明されるが、しかし、その構造体を入力しても、画像は生成されない。
画像ファイルのバイナリー・データー

さて、たとえば何らかのビットマップ画像をバイナリエディタで読み込むと、ファイルの先頭は必ず「42 4D」という16進数である。この数字は、アスキーコードで翻訳すると「BM」になる。
画像ファイルのバイナリー・データー

この「42 4D」という冒頭の数字を識別子とすることで、ファイルの種類を認識している。
画像ファイルのバイナリー・データー

誤解しないでほしいが、けっして「BM」とバイナリー・データーで書かれているのでなく(そもそも十六進数に「M」は無い)、書かれているのは あくまで「42 4D」である。
画像ファイルのバイナリー・データー

ネット検索で調べる場合は「42 4D」というキーワードを付け加えて「ビットマップ 42 4D」などで調べれば、ビットマップ画像のバイナリでの扱いに関する有益な情報が入手しやすいだろう。
画像ファイルのバイナリー・データー

たとえば、Windows用のフリーのバイナリエディタ stiring で3×5ピクセルの白色だけのビットマップファイルを見ると、下記のようになっている。
画像ファイルのバイナリー・データー

stiring に限らず一般にバイナリエディタの画面の構成は、上記のようになっている。(なお、「ファイル」→「ダンプイメージの保存」などの操作で、上記のようなバイナリー・データーのイメージをテキストエディタ(Windowsの場合は『ワードパッド』など)で見られる形式に変換して出力できる。)
画像ファイルのバイナリー・データー

バイナリー・データーはそのままでは、テキスト表示できない。なので、それをテキスト化したり、見やすくするために書式を整えたりして、バイナリー・データーの内容を出力したものをダンプ イメージという。
画像ファイルのバイナリー・データー

また、そのようなダンプイメージを出力することを、俗に(ぞくに)「ダンプする」などとIT業界では言う。
画像ファイルのバイナリー・データー

さて、このダンプイメージのうち、バイナリー・データーの本体は、真ん中のブロックである、下記の
画像ファイルのバイナリー・データー

の部分である。
画像ファイルのバイナリー・データー

一番左のブロック、
画像ファイルのバイナリー・データー

は、ファイル内容のバイナリー・データーをアスキーコードに変換して表示している。
画像ファイルのバイナリー・データー

このアスキーは、バイナリー・データーのファイル内容ではなく、バイナリエディタ側がユーザーが内容を探しやすくする目的などのために表示している。
画像ファイルのバイナリー・データー

一番上の行の
画像ファイルのバイナリー・データー

は、単に何バイト目かをあらわしているだけである。これは、バイナリー・データーのファイル内容ではなく、バイナリエディタ側がユーザーが見やすくするために表示している。
画像ファイルのバイナリー・データー

また、一番左の列の
画像ファイルのバイナリー・データー

は、アドレスをあらわしている。
画像ファイルのバイナリー・データー

赤い背景色のブロックで表した部分を「BITMAP FILE HEADER」といい、これの長さは14バイトで固定である。「42 4D」で2バイト、同じように数えて12バイトの長さ。「BITMAP FILE HEADER」とは、つまり、「42 42」「画像のファイルサイズ」「予約領域」「予約領域」「画像の場所の指定」の部分である。
画像ファイルのバイナリー・データー

日本語では、「BITMAP FILE HEADER」のことを、ビットマップの「ファイルヘッダ」と言う場合もある。 つまり、ビットマップのファイルヘッダの長さは14バイトで固定である。
画像ファイルのバイナリー・データー

さて、青い背景色のブロックで表した部分を「BITMAP INFO HEADER」という。
画像ファイルのバイナリー・データー

つまり、「BITMAP INFO HEADER」とは、
画像ファイルのバイナリー・データー

の部分である。日本語では、「BITMAP INFO HEADER」のことを、ビットマップの「情報ヘッダ」と言う場合もある。
画像ファイルのバイナリー・データー

ビットマップ情報ヘッダの長さは40バイトで固定である。 バイナリー・データー本体の右上にあるビットマップ情報ヘッダの「28 00」と次の段の「00 00」は、ヘッダサイズに相当するが、28は十進数に直すと40である(16×2+8=40)。 リトルエンディアンなので、あとの00の3回くりかえしは無視していい。
画像ファイルのバイナリー・データー

さて、ビットマップ構造体の図を、ダンプイメージと照らし合わせてみよう。
画像ファイルのバイナリー・データー

「42 4D」のあとに 「82 00 00 00」と4バイトぶんの数字が続くが、これはファイルサイズを表している。
画像ファイルのバイナリー・データー

なお、このファイルのサイズは130バイトである。16進数の「82」は 10進数で「130」である。算数で10進数の計算で 8×16+2=130 である。
画像ファイルのバイナリー・データー

読者のWindowsパソコンでもバージョンによるかもしれないが、『ペイント』などで3×5ピクセルの16色ビットマップ画像を作成すれば、おおむね、このあたりのファイルサイズになるだろう。
画像ファイルのバイナリー・データー

なお、ペイントのキャンバスサイズの変更は、左上のメニューアイコンから「プロパティ」を選ぶと「イメージのプロパティ」設定ウィンドウが開き、下のほうにキャンバス幅とキャンバス高さの指定欄があるので、そこで数値を入力してサイズを変形できる。
画像ファイルのバイナリー・データー

この「82 00 00 00」の意味は、10進数にすれば
画像ファイルのバイナリー・データー

という意味である。こういう数え方を「リトル エンディアン」という。
画像ファイルのバイナリー・データー

なお「256」の由来は 16×16=256 である。
画像ファイルのバイナリー・データー

けっして、8200万バイトでないし、130万バイトでもないので、勘違いしないように。
画像ファイルのバイナリー・データー

次の、2つの 予約領域 は、ゼロで埋める決まりになっており、なので「00 00 」「00 00」である。
画像ファイルのバイナリー・データー

1段目アドレスのABCDの部分にある「 76 00 00 00」は、まず意味は10進数で118である。7×16+6=118 より。
画像ファイルのバイナリー・データー

で、「バイナリー・データーの118バイト目から、画像データが始まります」と宣言している(ファイル先頭から画像デーマまでの「オフセット」という)。1バイトは256で、16×16=256なので16進数では2ケタぶんです。画像データの先頭のバイトが118バイト目であると、を指し示している。
画像ファイルのバイナリー・データー

なお、数え方は左上の「42 4D」ですでに2バイトである。 この調子で118バイト目を探すと
画像ファイルのバイナリー・データー

なので、8行目(商7にプラス1)の7列目(あまり「6」+1)のバイト(6)目が、開始位置である。下図の太字にした部分のバイトである。
画像ファイルのバイナリー・データー

よくみると、この太字の部分からFが5個続き、「000」というのが3回数くりかえすので、合計でFが15回繰り返される。これは、この画像が横5×3であることに対応している。
画像ファイルのバイナリー・データー

さて、「画像の横幅」「画像の縦幅」に対応するのは「05 00 00 00」「 03 00 00 00」である。これは、横5ピクセル×縦3ピクセルという意味である。数値の数え方はリトルエンディアンである。
画像ファイルのバイナリー・データー

ビットマップ情報ヘッダの終わりから画像の開始までは、カラーパレットが16個、続いています。
画像ファイルのバイナリー・データー

上ダンプの太字の直前の「FF FF FF 00」とは、16番目のビットマップのカラーパレットです。カラーパレットは合計で16個あります。また、この16番目パレットを呼び出すのに、画像データ部分のF5回の繰り返しの部分では「F」でパレットを呼び出しています。
画像ファイルのバイナリー・データー

その直前のパレットの「FF FF 00 00」が15番目のパレットです。このパレットは「E」で呼び出されますが、今回の画像データでは使われてないです。
画像ファイルのバイナリー・データー

「プレーン数」と「色ビット数」の「01 00 」「04 00」は。プレーン数が十進数で1で、色ビット数が十進数で4という意味である。
画像ファイルのバイナリー・データー

「圧縮形式」とあるが、無圧縮にする場合はゼロにする。つまり「00 00 00 00」で無圧縮である。ビットマップは、仕様上では、実は圧縮も可能な仕様である。
画像ファイルのバイナリー・データー

解像度「C4 0E 00 00」は、印刷などをする際の1メートルあたりのピクセル数ですが、今回は印刷をしないので、気にしないことにします。
画像ファイルのバイナリー・データー

「パレット数」は、仕様上は策定されていますが、実際には利用されていません。マイクロソフトの『ペイント』アプリで作ったファイルですらも、ここはゼロのままの「00 00 00 00」です。
画像ファイルのバイナリー・データー

「重要な色数」も、仕様上は策定されていますが、やはり実際には利用されていません。これまたマイクロソフトの『ペイント』アプリで作ったファイルですらも、ここはゼロのままの「00 00 00 00」です。
画像ファイルのバイナリー・データー

なお、ビットマップの仕様を策定した起業は、昔のIBMとマイクロソフトです。
画像ファイルのバイナリー・データー

なお、マイクロソフトのサイトでは
画像ファイルのバイナリー・データー

のようにC言語っぽいコードのようなモノが書いてあるが、これは単に構造を説明しているだけであり、このコードをコンパイラなどに入力しても何も起きない。
画像ファイルのバイナリー・データー

背景が白色だと分かりづらいので、黄色にしてみましょう。
画像ファイルのバイナリー・データー

それを横5×縦3ピクセルにしたファイルのバイナリー・データーダンプは、次のようになります。
画像ファイルのバイナリー・データー

「BB BB B0 00 」と3回繰り返すことで、パレットBでピクセルを塗るのを横5マスを3行続けていることが分かります。
画像ファイルのバイナリー・データー

末尾の「0」や「00」はおそらく改行のようなものでしょう。
画像ファイルのバイナリー・データー

また、キャンバスサイズを拡大し、横6×縦9ピクセルで黄色一色のビットマップ画像にすると、ダンプは下記のようになります。
画像ファイルのバイナリー・データー

よくみると後半「BB BB BB 00」が9回(※ この画像のタテのピクセル数が9である)、繰り返されています。
画像ファイルのバイナリー・データー

また、Bの個数も、6回ずつ(※ この画像の横幅は6である)です。
画像ファイルのバイナリー・データー

最後の「00」で、このことから、00バイトで改行を表すのだろう事が分かります。
画像ファイルのバイナリー・データー

とすると、パレット番号に「0」や「00」は使えないハズです。
画像ファイルのバイナリー・データー

なお、画像データの読み方は、バイナリー・データーの情報が、画像では左下から右下に対応します。
画像ファイルのバイナリー・データー

なので、2色以上を使った画像で、調べてみましょう。
画像ファイルのバイナリー・データー

下記の画像は、ややサイズが大きい画像ですが(サイズが小さいとビューワーの性能によっては色ボケしたりして不正確になるので)、
画像ファイルのバイナリー・データー

幅30×高さ10 の画像です。白地を基調としておりますが、左上に赤い帯があります。下側に青い帯があります。右上に短く青い帯があります。
画像ファイルのバイナリー・データー

ADDRESS 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0123456789ABCDEF
画像ファイルのバイナリー・データー

太字にしたところから、画像の始まりです。ほぼパレット「C」が30回繰り返されています。2行目の最初が「6C」になってるのは、ペイントが勝手にそういう色に変えただけです。2行目は6のあと「C」が28回繰り返され、最後にパレット「6」番の色です。
画像ファイルのバイナリー・データー

パレットCは、斜線にした 「FF 00 00 00」の部分です。「赤色(256段階), 緑色(256段階), 青色(256段階)」の順番になっています。
画像ファイルのバイナリー・データー

なので、
画像ファイルのバイナリー・データー

です。
画像ファイルのバイナリー・データー

さて、幅30×高さ10 の画像は、画像で見ると、青い帯は下側にあります。しかしバイナリー・データーでは、青い帯は、最初に書かれています。
画像ファイルのバイナリー・データー

このように、上下が反転しています。そういう仕様だからです。
画像ファイルのバイナリー・データー

Linux だと、このバイナリー・データーのテキスト形式のダンプ内容を、バイナリー・データーのビットマップ画像形式に変換できるコマンドがある。
画像ファイルのバイナリー・データー

xxd というコマンドを使えばいい。Fedora 31 では標準では、この xxd がついていない。なのでFedoraにこのコマンドをインストールするために vim-common をインストールする必要がある。Fedora ではvim-common に xxd が含まれている。
画像ファイルのバイナリー・データー

xxdはもともと、バイナリー・データーを16進ダンプするためのコマンドであるが、このソフトウェアには、さらに16進ダンプされた内容のテキストをバイナリー・データーに戻す機能もある。
画像ファイルのバイナリー・データー

引数に -r をつける事により、16進ダンプをバイナリー・データーに戻せる。
画像ファイルのバイナリー・データー

たとえば、なんらかのテキストエディタ(Fedora標準の gedit でもよい)に、下記のテキスト(これはstirringで出力したテキストから、上2行の見出しを除去しただけの物である)をそのままコピーペーストしてテキストエディタに貼り付けし、普通に「test」などの名前で保存しよう。この段階では、ファイル「test」は、まだ単なるテキストファイルである。
画像ファイルのバイナリー・データー

だが、コマンド端末で
画像ファイルのバイナリー・データー

と入力して実行すると、なんと『testout』というファイル名で、画像ファイルが新規作成されている。
コード例

言葉だけだと分かり辛いので、実際に下記コード例でC言語でバイナリー・データーを書き込むことによって画像ファイルを作成してみましょう。
コード例

とりあえず、上記コードを実行すると、黄色いビットマップが作成されます。
コード例

もちろん、こうやって一個ずつ作成するのは非効率だし、今さらパレット数が限られる形式は古いビットマップは非効率ですが、とりあえず上記コードで実際にビットマップが作成できるのです。
コード例

後半部
コード例

の英数字1文字が 5×3 のサイズの1ピクセルに対応していることが一目瞭然です。
コード例

実際、英数字のBを十六進で 0 から F の別の 英数字に変えると、コンパイル後のビットマップの対応する画素の色が変わります。
コード例

なお、画像は上下反転して表示されることに注意してください。
コード例

たとえば、元データが
コード例

の配置なら、表示画像は
コード例

のように上下逆に配置されます。
コード例

実際、色変えを試したところ、色と英数字の関係は
コード例

でした。カラーパレットどおりです。
コード例

となっていますが、なぜか16個のパレットを使用できます。
コード例

さて、上記の黄色5×3のビットマップに、右端に青色を1列くわえて横6マスにしたい場合、やればいいことは単に
コード例

のように横幅の数値を5から6に変えて、
コード例

のように青色に対応す「C」を一番右列の次に加えれば済みます。
コード例

なお、ビットマップにはいくつかの方式がありますが、 上述の例で示した方式は 4bitビットマップ
コード例

という方式です。
コード例

色ビット数の「04」に対応しています。
コード例

圧縮形式が「00」の場合、色ビット数の数値によって画像の形式が決まり、 色ビット数が「01」なら白黒2階調、「04」なら4bitカラー、「08」なら8bitカラーのように決まる仕様です
コード例

4bitビットマップでは、16色までしか使えません。
コード例

2010年以降の現在、一般のr=0~255,b=0~255,g=0~255の合計1677万色でよく使われているのは、これではなく24bitビットマップや32bitビットマップと呼ばれる方式です。
コード例

なお8bitビットマップは256色です。
コード例

重要色数から画素対応(イメージデータ)までの部分は、下記のように4個ずつ改行すれば16行になるので、
コード例

これはカラーパレットです。
コード例

ただし、各行の数値は前から青、緑、赤の順番に対応しています。「RGB」の逆ですので、注意してください。
コード例

実際、たとえば、0からFまでの数字を使った画素情報
コード例

に書き換えておいたあと、カラーパレットに相当する部分でたとえば 0xFF, 00, 00, 00, を 0x00, 00, 0xFF, 00, に書き換えたら、今まで青だったところが赤に変わります。
コード例

なお、ビットマップ形式は透明化をサポートしていません。
コード例

コードでは、たとえばパレットの6行目あたりに
コード例

RGB(コード青、緑、赤で並んでいますが)の色情報のあとにある 4列目の00 は、透明度でなければ不透明度でもなく、単なるゼロ・ゼロです。
コード例

実際、後述しますが24bitカラーになると、ゼロゼロはなくなり、RGB値だけを画素部分に直接記入しています。
ビットマップ構造体

ビットマップの2bit カラーのモノクロ2諧調で下記のように5×3でキャンバスに色を塗ると、バイナリー・データーは下記のようになります。
ビットマップ構造体

42 4D 4A 00 00 00 00 00 00 00 3E 00 00 00 28 00 00 00 05 00 00 00 03 00 00 00 01 00 01 00 00 00 00 00 0C 00 00 00 C4 0E 00 00 C4 0E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 78 00 00 00 E8 00 00 00 D8 00 00 00
ビットマップ構造体

末尾の FF FF FF 00 78 00 00 00 E8 00 00 00 D8 00 00 00
ビットマップ構造体

の FF FF FF 00 の後が画素の情報です。
ビットマップ構造体

この意味は、 まず「78」の最初の7を 2進法に変換すれば
ビットマップ構造体

0111 です。
ビットマップ構造体

0が黒色、1が白色に相当し、
ビットマップ構造体

これで一番下の行の
ビットマップ構造体

が確定します。
ビットマップ構造体

次の8は、2進数では「1000」です。1が白なので、
ビットマップ構造体

となりますが、横幅が5までなので、6個目以上はカットされて、
ビットマップ構造体

となり、こうして一番下の行が確定します。 下から2行目や3行目の色も、1行目と同様に決まります。
ビットマップ構造体

もしカットされた横方向の6列目以降を見たいなら、バイナリエディタ2行目辺りにある画像の横幅の 05 を 08 に変えてみれば、見ることができ、確かに黒色になっています。
ビットマップ構造体

また、カラーパレットに相当する部分は
ビットマップ構造体

のうちの
ビットマップ構造体

のあたりです。
ビットマップ構造体

のようにいじってみると、(白と黒ではなく)色が水色と青色のものに変わります。
ビットマップ構造体

次のFF FF FF が、青FF,緑FF,赤FFを意味しています。
ビットマップ構造体

だから例えば紫色を使いたいなら、FF 00 FF を入力すればいいわけですが、実際には色解像度などの都合でピンク色が表示されます。
ビットマップ構造体

赤色の濃さをきめるブロックが本当に赤に対応しているかを確認するには、青と緑の部分を 00 に変えてみれば、確かに残りのブロックが赤色に対応していることがわかります。
ビットマップ構造体

5×3の黄色画像を24bitカラーで作ると、バイナリー・データーは下記のようになります。
ビットマップ構造体

42 4D 66 00 00 00 00 00 00 00 36 00 00 00 28 00 00 00 05 00 00 00 03 00 00 00 01 00 18 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F2 FF 00 F2 FF 00 F2 FF 00 F2 FF 00 F2 FF 00 00 F2 FF 00 F2 FF 00 F2 FF 00 F2 FF 00 F2 FF 00 00 F2 FF 00 F2 FF 00 F2 FF 00 F2 FF 00 F2 FF 00
ビットマップ構造体

これは意味的には、
ビットマップ構造体

です。
ビットマップ構造体

24bitカラーでは、カラーパレットが無くなります。
ビットマップ構造体

色は、16進数の2ケタ×3原色ぶんで直接、指定する方式です。ただし、青,緑,赤の配置で並んでいます。
ビットマップ構造体

は
ビットマップ構造体

の意味です。
ビットマップ構造体

だから、バイナリエディタを使って画素の最初の
ビットマップ構造体

をたとえば FF 00 00 に変えれば、ビットマップ画像の左下が青く変わります。
PNGフォーマット

例によって 横 5× 縦 3 の黄色画像を作ってバイナリー・データーを調べてみましょう
PNGフォーマット

結果は
PNGフォーマット

です。
PNGフォーマット

これは意味は
PNGフォーマット

数値はビッグエンディアンで並んでいます。
PNGフォーマット

CRCは巡回冗長検査というものです。
PNGフォーマット

PNGは圧縮ライブラリにオープンソースの圧縮伸張ライブラリ ZLIB を採用しています。なので、ZLIB を調べなければなりません。
PNGフォーマット

PNGについて、企業 GREE の技術者ブログが詳しくわかりやすくまとまっています。 『PNG軽量化の減色と圧縮について | エンジニアブログ | GREE Engineering』
PNGフォーマット

詳しくは、そちらを読んでください。当wikiよりも遥かにレベルの高い技術者がわかりやすく解説してくれています。
PNGフォーマット

いちおう、当wiki側で念のためにある程度は技術解説しておくと(リンク先サイトが将来的に消失するような場合もあるので念のため)、 zlib自体は統一的に普及していますが、しかしPNGの圧縮はzlibだけによるものではなく、フィルタやパレットによる圧縮の効果もあります。
PNGフォーマット

このため、どういったアルゴリズムを採用するかによって圧縮率が変わるので、実はPNGには統一的な圧縮方法がなく、ソフトウェアやアルゴリズムによって変わります。だから代表的なPNG編集可能ソフトウェアでは、さまざまなパターンの圧縮方法を試して、一番圧縮率の高かったものを採用するとのことです。
PNGフォーマット

こういう複雑な仕様のため、個人でPNG編集ソフトウェアを作るのは、あまり現実的ではないでしょう。企業やベンチャーなどに任せましょう。
PNGフォーマット

カラータイプを見て、パレットを使う方式かどうかを判別しています。パレットを使う方式の場合、 PLTE というチャンクがある。
PNGフォーマット

カラータイプは、0,2,3,4,6 のいずれか。カラータイプ 3 ならパレットを使う方式。カラータイプが4または6のとき、透過情報をもつことができる。
PNGフォーマット

ビット深度は、青1の強さが何ビットかということです。
PNGフォーマット

8bitなら、0~255までの合計256段階ということです。
PNGフォーマット

RGBと合計で三原色あるので、24ビットになります。
PNGフォーマット

なので、バイナリー・データーを見ると8bitですが、しかしマウス操作の右クリックなどで画像のプロパティを見ると 「ビットの深さ」が24ビットになっています(8×3=24 なので)。
PNGフォーマット

なので、上記の黄色画像のファイル形式でなら フルカラー(約1670万色)を扱えます。PNG 24 は透過情報を持ちません。
PNGフォーマット

上記、黄色画像は、後述するPNG 24 というものです。
PNGフォーマット

PNG のビット深度には一般にrgbとアルファの合計で8bitと24bitと32bitのものがありますが、拡張子はどれも「.png」です。
PNGフォーマット

通称、ビット深度がrgb+アルファで合計24bitのものは、PNG 24 などと言われます。PNG 24 はフルカラー(約1670万色)を扱えます。PNG 24 は透過情報を持ちません。
PNGフォーマット

これに透過情報を持たせるには別途、外部の画像編集ソフトで透過情報を追加させてビット深度をrgb+アルファで合計32bitにさせた PNG 32 を作る必要があります。
PNGフォーマット

ビット深度がrgb+アルファで合計8bitのものは256色程度までしか扱えず、PNG 8 などといわれます。 png 8 も png 24 も png 32 も、どれも拡張子は「.png」です。
PNGフォーマット

なお、一原色あたりビット深度 16 のものは、つまり青一色でも 0 ~ 65535 の合計 65536 段階あるものです。
PNGフォーマット

ビット深度は通常は 1, 2, 4, 8, 16 のいずれかです。カラータイプ 2 や 6などのトゥルーカラーが対応しているのはビット深度が 8 以上のものです。
PNGフォーマット

なお実はPNGにはPNG 48 という色数が約280兆色 のものもあり、フォトショップなど一部の画像処理が対応しているようです。
PNGフォーマット

また、PNGには、画像の行ごとに「フィルタ」と言う処理があり・・・(調査中)
PNGフォーマット

PNGはハフマン符号と言うのを使っています。これは、出現頻度の多い文字にほど、短い文字を割り当てるものです。ただし、1バイト単位で文字と見なして、それを符号化するようです。
PNGフォーマット

ハフマン符号は、情報工学系の大学教科書などで一般的な『符号理論』のような科目の教科書で解説される、典型的な技術です。
PNGフォーマット

PNGは、このほか LZSS という処理を使っています。
PNGフォーマット

LZSS とハフマン符号を組み合わせたものが、PNGの採用している Deflate 圧縮です。
PNGフォーマット

そして、このDeflate圧縮に補助的に色々なものをつけてライブラリ化したものが ZLIB です。
PNGフォーマット

当ページは現状『ゲームプログラミング』をうたっています。
PNGフォーマット

同人ゲームプログラミングなどでよくDXライブラリが使われますが、幸運なことに、DXライブラリの作者さまは圧縮にも興味を持っており、リンク先 『データ圧縮プログラムについてざっくり学ぶ』 のように圧縮についての技術解説をしたwebページを作ってくれています。
PNGフォーマット

PNGはこのように複雑ですので、オープンソースのライブラリを使わずにPNGの読み書きソフトをゼロから作るのは非現実的です。zllbなどのオープンソースのライブラリを使って作ることになるでしょう。そのためには、windowsユーザーならwindowsでのライブラリの作り方やdllなどの作り方や使い方なども学ばなければならず、なかなか面倒そうです。
PNGフォーマット

画像編集ソフトの企業の技術者でもない限り、PNGのフォーマットについては教養にとどめておいて、個人での編集ソフト製作はしないのが現実的かもしれません。
PNGフォーマット

さて、PNGは解析には予備知識が膨大なのでプログラマーにはシンドイですが、しかし画像中での透過部分の保有に対応している画像フォーマットが、PNGやGIFなどの限られた画像フォーマットしかありません。(あとは、フォトショップなど一部の画像編集ソフトのフォーマットなので、そのソフトのユーザー以外には関係ない。)
PNGフォーマット

ビットマップとJPEGは、透過部分の保有には対応していません。このため、画像中に透明部分の保有をしたい場合には、PNGがよく使われます。
PNGフォーマット

なお、GIFはかつて特許問題があった等の事情があるためか、静止画にはGIFがあまり使われていないのが現状です。2004年にGIFの特許は切れましたが、しかしそれから10年以上経った2020年代の現在でも、静止画像にはGIFはあまり使われておらず、もっぱら動画において透過部分を保有したい場合にGIFが使われます。いわゆる「GIF動画」です。
そもそもの圧縮の理論について

よく圧縮の原理の説明で、「AAAAAAA」(Aが7個ある)を「A7」に置き換える、などと習います(なお「ランレングス圧縮」の原理)。高等学校でも、「ランレングス」という用語は習いませんが、こういう感じの原理を習います。
そもそもの圧縮の理論について

さて、上記「AAAAAAA」の説明では、都合よく続いている元の文字が、数字ではなく英字です。
そもそもの圧縮の理論について

ですが、もし「3333333」という数字の並びを圧縮するとき、どうするのかという問題があります。「37」とでも書くのでしょうか? しかしこれだと、もし三十七という内容の文字を表したい場合に困ります。
そもそもの圧縮の理論について

そう、つまり、実際には「A7」のような原理だけでは、圧縮できないのです。さらに必要な工夫として、圧縮後のどこからどこまでが文字の1単位であり、どこが反復回数なのかを、なんらかの方法で区別する必要性があるのです。
そもそもの圧縮の理論について

この問題を解決するための方法は単純であり、
そもそもの圧縮の理論について

という方法を使います。
そもそもの圧縮の理論について

たとえ1回しか現れない文字でも、反復回数を併記します。そのほうがアルゴリズムが簡単だからです。
そもそもの圧縮の理論について

具体例
そもそもの圧縮の理論について

「3」はアスキーコードで 0x03 です。これを7回続ける 3333333 なら、
そもそもの圧縮の理論について

0x03 0x07 です。
そもそもの圧縮の理論について

一方、「37」はアスキーコードで「0x03 0x07」です。1バイト単位で扱うので、「3」を1回、「7」を1回なので、
そもそもの圧縮の理論について

0x03 0x01 0x07 0x01
そもそもの圧縮の理論について

となります。
そもそもの圧縮の理論について

こうすることで、アスキー「3333333」とアスキー「37」を区別できます。
そもそもの圧縮の理論について

追記
そもそもの圧縮の理論について

なお、上記のような方法だと、反復回数は1バイト単位の最大数である FF までなので、つまり 255 回までしか反復を1フレーズでは処理できません。
そもそもの圧縮の理論について

だからもし「3」が262回続いたら、たとえば
そもそもの圧縮の理論について

0x03 0xFF 0x03 0x07
そもそもの圧縮の理論について

とでもなるでしょうか。圧縮後の記録が2バイトからバイトに増えましたが、しかしそれでも「FFFFF・・・」とFを262回記録する262バイトと比べたら大幅に圧縮できているので、意義のある圧縮です。
そもそもの圧縮の理論について

座学ばかりを聴いても頭に入らないでしょう。頭に入った気になるだけです。
そもそもの圧縮の理論について

実際に、ランレングス圧縮するコードを簡易的に作ってみましょう。
そもそもの圧縮の理論について

たとえば下記のテキストファイル "test.txt"
そもそもの圧縮の理論について

をランレングス圧縮するコードを簡易的に書いてみましょう。簡易なので、色々と想定漏れがありますが、しかしこういうのは簡単な例からコードを作っていくのが勉強のコツです。いきなり色々なパターンを想定したものを書くと、挫折してしまいます。
そもそもの圧縮の理論について

例えば、下記のようになります。簡易的なものなので、2種類の文字までしか圧縮に対応していませんが、とりあえずこれでも実装時の方針はつかめます。 想定漏れはいろいろありますが、文句をつけるのではなく、読者がご自身で改良したコードを手元で自分で作っていってください。そうやってプログラミングは勉強していくものです。
そもそもの圧縮の理論について

1バイトの整数を宣言するには char 型を使います。(一方、int 型だと4バイトになるので、エラーの原因になりかねず危険です。)
そもそもの圧縮の理論について

また、ランレングスでは繰り返し回数は必ず0以上の正(ただし0も正とした)の数なので unsigned char 型で宣言するのが安全です。(unsigned をつけないと、繰り返し回数 128以上の結果が異なってしまうので。)
そもそもの圧縮の理論について

実行結果
そもそもの圧縮の理論について

一方、もし
そもそもの圧縮の理論について

と表示されたら、単に読み取り先のテキストファイルを作成し忘れているか、あるいはファイルのアクセス許可などのセキュリティ設定が不適切だろうと考えられますので、テキストファイル側を修正してください。
そもそもの圧縮の理論について

上記コードだけの簡易的なものでも、実際にコードを作るとなると、何時間も掛かってしまいます。
そもそもの圧縮の理論について

上記コードでは、本当はもっと短くできますが(たとえば文字aを読み取るブロックと文字bを読み取るブロックを分けているので、コードが長くなっている)、しかし読者がコードの構造を理解しやすいように、意図的に文字aと文字bを読み取るブロックを分けています。
そもそもの圧縮の理論について

なお、実際にコードを作る場合は、けっしていきなり for とかwhile のコードを書くのではなく、愚直にまずは if 文のコードで「文字が2回続いた場合」「文字が3回続いた場合」などと場合ワケをしていき、あとからそれら場合分けをしたいくつもあるif文ブロックをfor などの構造でまとめていくことで、上記のように雛形(ひながた)を作っていきます。
そもそもの圧縮の理論について

ランレングスは、符号の種類が少ない場合ほど、符号が連続しやすいので、圧縮効率が高くなります。なので、ファクシミリなどの白黒印刷で、よくランレングス圧縮が使われます。
そもそもの圧縮の理論について

説明の都合上、文字の圧縮で解説していますが、どちらかというとランレングスはある種の画像データの圧縮向きであり、文書データの圧縮には向かない手法です。
そもそもの圧縮の理論について

ランレングス圧縮の欠点として、
そもそもの圧縮の理論について

ABCDE
そもそもの圧縮の理論について

のような文字を圧縮すると、
そもそもの圧縮の理論について

A1B1C1D1E1
そもそもの圧縮の理論について

となり、逆に長くなってしまいます。
そもそもの圧縮の理論について

そこで、長さが2以上または3以上のときにだけ圧縮する手法が考えられますが、しかしこの問題は、どこが圧縮された文字で、圧縮されてない文字かの区別をつけるのが難しいことです。
そもそもの圧縮の理論について

そこで、圧縮する部分としない部分との区別を、負数で区別する方法が考えられました。
そもそもの圧縮の理論について

たとえば
そもそもの圧縮の理論について

AAAAABCDEEEE
そもそもの圧縮の理論について

を
そもそもの圧縮の理論について

5A-3BCD4E
そもそもの圧縮の理論について

のように処理する方法です。
そもそもの圧縮の理論について

負数の場合、その絶対値の大きさのぶんだけ、不連続の文字が続きます。
そもそもの圧縮の理論について

また、このような処理のため、文字より先に数値を付記する必要が生じます。
そもそもの圧縮の理論について

ただし、この方法だと、負数を保管するために負号付き整数を利用するため、 つまり 255文字ではなく127文字までしか長さを保管できません。
そもそもの圧縮の理論について

C言語では、unsigned をつけないchar型で宣言すれば、-128~127を保管できる1バイト整数型になります。
そもそもの圧縮の理論について

コード例としては、まずいきなりPackBitsのコードを書くのは難しいので、まずランレングスをきちんと作ろう。
そもそもの圧縮の理論について

読み取り対象のテキストファイル "test2.txt" を下記のようにすれば(aが5回連続、bとcとdは不連続、eは6回連続)、
そもそもの圧縮の理論について

ランレングスのコードは、
そもそもの圧縮の理論について

実験結果
そもそもの圧縮の理論について

のような実行結果になる。
そもそもの圧縮の理論について

あとはこのコードを、PackBitsになるように改良していけばいいだけである。
そもそもの圧縮の理論について

上記コードでバイナリー・データーを格納する配列を2種類(str1 と str2)用意している理由は、なぜか最後に書き込みしたバイナリー・データーを格納している配列が書き換わってしまうバグ的な現象があるため、それを波及させないように2つ配列を用意しているという理由にもとづく。(「バイナリー・データー1」と「バイナリー・データー2」の結果がなぜか違っている。)
そもそもの圧縮の理論について

さて、我々が知りたいのはランレングスではなく、Pack bitsのコードである。それでは、そのコードを作ろう。
そもそもの圧縮の理論について

とりあえず、読者の簡便のため圧縮したい文字列を再掲する。
そもそもの圧縮の理論について

このコードを、下記のコードで圧縮しよう。 下記のように、長いコードになったが、とりあえず下記のコードで、実際に英数字だけの文字列なら、実験の結果、いくつか圧縮できる。
そもそもの圧縮の理論について

なお、コードをなるべく短くするために、前コードにあったデバッグ用メッセージの printf のいくつかは削除した。
そもそもの圧縮の理論について

コード例
そもそもの圧縮の理論について

実験結果
そもそもの圧縮の理論について

さて、実行結果中にある文字列 fffffffd が一見すると負数に見えないが、しかしこれが負数の -3 のことである。
そもそもの圧縮の理論について

なぜなら、16進数で8桁 である数
そもそもの圧縮の理論について

に1を足すと
そもそもの圧縮の理論について

になるので、ffff ffffはつまり-1のことである
そもそもの圧縮の理論について

同様に
そもそもの圧縮の理論について

ffff fffe とは -2 のこと。
そもそもの圧縮の理論について

よって
そもそもの圧縮の理論について

ffff fffd とは -3 のことである。
そもそもの圧縮の理論について

もっとも char型の1バイト(16進数で2桁のはず)の整数を宣言したのに、なぜ負数のときに 16進数で8桁の数が表示されるか疑問であるが、実行環境がそういう実装になっているので、妥協してもらいたい(Windows7 上のgcc で実行した結果である)。
そもそもの圧縮の理論について

ある種の画像はランレングスで効果的に圧縮できそうです。
そもそもの圧縮の理論について

しかしテキストファイルでは、普通、同じ文字が長々と反復しているということはありません。
そもそもの圧縮の理論について

なので、普通のテキストファイルを圧縮してしまうと、むしろ長くなってしまいます。
そもそもの圧縮の理論について

たとえば
そもそもの圧縮の理論について

hello を圧縮すると、
そもそもの圧縮の理論について

なので、無圧縮なら
そもそもの圧縮の理論について

である一方、「圧縮」すると回数が付記されるので
そもそもの圧縮の理論について

とむしろ増えてしまいます。
そもそもの圧縮の理論について

そこで改善案として、 続いていない文字は圧縮しないよう改良したモデルが既に考えられているのですが、問題点は、あるバイト情報が反復回数であるのか元文字のASCIIなのかをどう区別するか、という問題です。
そもそもの圧縮の理論について

さて、ASCII コードには十進で 127 (0x7f)までしかありません。
そもそもの圧縮の理論について

そこで、テキストファイルの圧縮を扱う際、128以上255以下の数字を、圧縮の開始記号として区別する方法もあります。
そもそもの圧縮の理論について

とりあえず、説明の簡単のため 0x80 を開始記号としたとしましょう(実際に採用されているほう恣意とは違う)。
そもそもの圧縮の理論について

たとえば helllllllo という小文字エル l が7回続いているヘロロロロロロローは、
そもそもの圧縮の理論について

となり、たった 6 バイトで表せます。なお、4バイト目の「0x80」が開始記号であり、その次の「0x07」が反復回数です。
そもそもの圧縮の理論について

さて、たった1個しかない開始記号に、1バイトも使うのは、人間にはわかりやすいですが、しかし機械的には冗長すぎて無駄です。現代では 1 バイトは 8ビット(十進で255、十六進でFF)です。
そもそもの圧縮の理論について

開始記号には1ビットか2ビットを使えば十分そうです。
そもそもの圧縮の理論について

アスキーは0x7f までですが、
そもそもの圧縮の理論について

7は二進数で 0111 ですので、つまりアスキーコードでは文字のビットの最初1ケタ目は必ず最初は0になります。
そもそもの圧縮の理論について

つまりアスキーでは最初1ビットに「1」は不使用です。
そもそもの圧縮の理論について

これに注目すれば、「1」をビット単位での開始記号とするアイデアが思いつきます。
そもそもの圧縮の理論について

そこで、先頭1ビットが「1」なら開始記号とする、という方式が採用されています。この方式では、残り7ビットを反復回数に利用できます。すると、おおよそ127回(256÷2 - 1 = 127)までの反復を処理できます。 256÷2 - 1の -1のぶんをしない方式でも、128回までの反復を処理できます。
そもそもの圧縮の理論について

英文の半角英数の直接入力によるテキストファイルは、これで効率的に圧縮できるでしょう。
そもそもの圧縮の理論について

ただし、英語以外の文字は別問題です。
そもそもの圧縮の理論について

また、テキスト以外の画像や音声なども、別問題です。
そもそもの圧縮の理論について

あくまで上記の方法は、ASCIIコードだけで表せる文字だけを使ったテキストファイルの場合のみの事例です。
そもそもの圧縮の理論について

連続していない1文字を圧縮しても、かえってバイトが増えるだけです。
そもそもの圧縮の理論について

AAのように二文字だけ連続している単語なら、(圧縮方法にも寄りますが)圧縮してもしなくても変わらないか、むしろ少し増えるリスクもあります。
そもそもの圧縮の理論について

そこで、反復回数がたとえば3回以上など、一定回数の反復があった場合にだけ圧縮するという方式も提案されています。
そもそもの圧縮の理論について

最低でも、長さに1文字、距離に1文字を使うわけですから、なので2文字よりも多い、3文字以上を圧縮しないと、効果がありません。
そもそもの圧縮の理論について

単純に反復を見るだけだと、ABCABCFのように、ABCが2回以上続いているようなものは圧縮できません。
そもそもの圧縮の理論について

タイトルでは説明の簡単のため2回だけの反復にしていますが、もしこれが ABCABCABCABCABCF のようにABCが5回続いているものだとすると、
そもそもの圧縮の理論について

これを圧縮できないのは困ります。
そもそもの圧縮の理論について

そこで、こういうのを圧縮できるようにした方式として LZSS という方式が考えられています。
そもそもの圧縮の理論について

ABCDEABCDK を ABCDE[5,4]K
そもそもの圧縮の理論について

のように圧縮する方式です。
そもそもの圧縮の理論について

最初の5は、何文字戻った位置が繰り返しの起点かを示す数値です。
そもそもの圧縮の理論について

そして「4」は、繰り返しの単語の長さです。
そもそもの圧縮の理論について

ABCDEABCDABCDK の場合、
そもそもの圧縮の理論について

まず、前から圧縮していき、
そもそもの圧縮の理論について

ABCDE[5,4]ABCDK
そもそもの圧縮の理論について

のようになります。
そもそもの圧縮の理論について

そして、さらに圧縮されてない別の部分について
そもそもの圧縮の理論について

ABCDEABCD[9,4]K
そもそもの圧縮の理論について

のようになります。なお、ABCDEABCD[4,4]Kのようにはしません。なぜそうかと言われても、そういう慣習なので、それに従ってください。
そもそもの圧縮の理論について

そしてともかく、圧縮されてない部分どうしをあわせた
そもそもの圧縮の理論について

ABCDE[5,4][9,4]K
そもそもの圧縮の理論について

が最終的に保存されます。
そもそもの圧縮の理論について

これなら、複合するときに、まず最初のカッコから複合していき、
そもそもの圧縮の理論について

ABCDEABCD[9,4]K
そもそもの圧縮の理論について

が中間的に複合されます。さらに続けて
そもそもの圧縮の理論について

ABCDEABCDABCDK
そもそもの圧縮の理論について

が複合され、元の文字列が得られます。
そもそもの圧縮の理論について

ABABABABKのように繰り返しがある場合(ABが4回続いたあとにK)、カッコ内の数値の大小関係を [小、大] にして、
そもそもの圧縮の理論について

AB[2,4]K
そもそもの圧縮の理論について

のように表します。です。
そもそもの圧縮の理論について

こうすることで、単純な繰り返しにも対応できます。
そもそもの圧縮の理論について

また、それぞれの文字の冒頭に、1bitのフラグをつけて、圧縮しているものかどうかを判断します。 アスキー文字を1バイト(8ビット)で表した場合、先頭1ビットは必ず0なので、つまり先頭1ビットが「1」の場合なら次の数ビットを制御用のビットとして認識することができます。
そもそもの圧縮の理論について

これをもちいて上記 [2,4] などの情報を検出します。
そもそもの圧縮の理論について

制御文字には3ビットを使い、0x100 なら終端ビットである。
そもそもの圧縮の理論について

一致長さは0x101 ~ 0x11D (十進で257~285に相当)で制御されるが、しかしこのままではdeflate圧縮での繰り返しの仕様である3から258までの数をあわらせない。そこで拡張ビット(extra bit)という手法により、
そもそもの圧縮の理論について

拡張ビットのあとに補完で追加されるビットの長さは一定ではなく、基準値によって異なり、下記のように定められている。基本的に参照元の基準値が大きいほど、表す一致長さも大きくなるので拡張ビット数も大きくなるが(最大で5)、しかし例外的に最後の0x11Dは表す一致長さが1通りしかないので拡張ビット数が0である。
そもそもの圧縮の理論について

このように下段の大きい数値ほど拡張ビット数も増えている理由は、使用頻度の高いと考えられている短い一致長さほど、保存に要する容量も小さくすることで、より効率的に圧縮できる、というアイデアである。なお、このようなアイデア(使用頻度の高いものにほど短い符号を割りえてることで容量節約するアイデア)を「ハフマン符号」という。
そもそもの圧縮の理論について

数学書などでの「ハフマン符号」の説明では、確率論などを使って厳密に 使用頻度 が定式化されたりするが、しかし本ページで語るような実際の応用では、そんなに厳密に確率計算する必要はない。実際、上記の対応表も、別に確率方程式などから算出されたわけではないだろうし、表のどこにも確率計算は無い。
そもそもの圧縮の理論について

拡張ビットでは、表現できる値からの不足分の数値を表記する。
そもそもの圧縮の理論について

たとえば基準値 0x10A では 13~14の表現範囲だが、これで14を表現したい場合、
そもそもの圧縮の理論について

13に1をプラスすれば1になるので、
そもそもの圧縮の理論について

のように表現される。0x10Aの拡張ビット数は「1」と決まっているので、10A の次の「1」と書かれた1ビットを読み取った時点で、自動的に拡張ビットの終わりだと判断される。
そもそもの圧縮の理論について

さて一方、(一致長さの符号ではなく)距離符号の拡張ビットの対応表は、別に定められている。下記のように 0x00 ~ 0x1D の距離で 1 ~ 32768 の距離に対応させています。
そもそもの圧縮の理論について

Deflate 圧縮における LZSS 方式では、
そもそもの圧縮の理論について

の順番でそれを表す。
そもそもの圧縮の理論について

たとえば一致長が4、距離が5なら、
そもそもの圧縮の理論について

まず表現「4」は0x102で拡張ビット数0なので、
そもそもの圧縮の理論について

まず、0x102 が先にくることが決まり、拡張ビットはその直後には無い事が決まり、直後はすぐに距離が来ることが決まる。
そもそもの圧縮の理論について

つまり、
そもそもの圧縮の理論について

となる。
そもそもの圧縮の理論について

そして、距離の表現「5」に対応するのは、 0x04 が5~6で拡張ビット数1だから、5+0により0が拡張ビットの値になるので、
そもそもの圧縮の理論について

が来ることが決まる。
そもそもの圧縮の理論について

そして、
そもそもの圧縮の理論について

一致長さ、距離
そもそもの圧縮の理論について

の順序なのだから、
そもそもの圧縮の理論について

が来る。
そもそもの圧縮の理論について

ABCDEABCDK の場合、まずアスキーコードに変換して、
そもそもの圧縮の理論について

である。
そもそもの圧縮の理論について

距離や一致長さの単位はバイト単位(十六進で2ケタ)で測定するので、
そもそもの圧縮の理論について

となる。
そもそもの圧縮の理論について

そして、いくつか前の段落で説明したように、
そもそもの圧縮の理論について

となる。
そもそもの圧縮の理論について

最後に終端記号 0x100 がつくので、
そもそもの圧縮の理論について

となる。これで圧縮がひとまず完了。これがLZSS圧縮の原理である。