「ゲームプログラミング/画像ファイルの作成プログラム」の版間の差分

削除された内容 追加された内容
→‎PackBits: コードを謎のバグ対応ずみのものに変更。
→‎PackBits: とりあえずコード例。長いが、動く。短いくて分かりやすいコードは別の人に任せる。
1,530 行
 
上記コードで機械語を格納する配列を2種類(str1 と str2)用意している理由は、なぜか最後に書き込みした機械語を格納している配列が書き換わってしまうバグ的な現象があるため、それを波及させないように2つ配列を用意しているという理由にもとづく。(「機械語1」と「機械語2」の結果がなぜか違っている。)
 
 
 
 
 
 
 
さて、我々が知りたいのはランレングスではなく、Pack bitsのコードである。それでは、そのコードを作ろう。
 
とりあえず、読者の簡便のため圧縮したい文字列を再掲する。
<pre>
aaaaabcdeeeeee
</pre>
 
このコードを、下記のコードで圧縮しよう。
下記のように、長いコードになったが、とりあえず下記のコードで、実際に英数字だけの文字列なら、実験の結果、いくつか圧縮できる。
 
なお、コードをなるべく短くするために、前コードにあったデバッグ用メッセージの printf のいくつかは削除した。
 
 
コード例
<syntaxhighlight lang="C">
#include <stdio.h>
#pragma warning(disable : 4996)
 
int main() {
FILE *fp1 = fopen("test2.txt", "rb");
 
if (fp1 == NULL) {
perror("ファイルを開けませんでした。\n");
return 1;
} else {
printf("ファイルをオープンしました。\n");
}
 
unsigned char str1[70]; // 表示結果を短くするために数値を微妙に小さくした
printf("機械語を読み取っています。\n"); // 「文字列」ではなく機械語
 
fread(str1, sizeof(unsigned char), 150, fp1);
 
char str2[70]; // 表示結果を短くするために数値を微妙に小さくした
 
int endCount;
for (int i = 0; i < sizeof(str1) / sizeof(str1[0]); i = i + 1) {
 
if (str1[i] > 30 && str1[i] < 128) {
str2[i] = str1[i];
} else {
endCount = i;
 
str2[i] = 0x00;
 
break;
}
}
 
printf("\nファイルに書いてある機械語\n");
 
for (int i = 0; i < sizeof(str1) / sizeof(str1[0]); i = i + 1) {
printf("%02x ", str1[i]); // 最低でも2桁を表示、の意味
}
 
printf("\nファイルに書いてある機械語2\n");
 
for (int i = 0; i < sizeof(str2) / sizeof(str2[0]); i = i + 1) {
printf("%02x ", str2[i]); // 最低でも2桁を表示、の意味
}
 
printf("\n文字の繰り返しを検出しようとしています...\n");
 
int bufRep
[3]; // 繰り返し文字の回数を記録するカウンターをとりあえず3つ用意。
int bufWord[3]; // 繰り返し文字の字を記録するバッファをとりあえず3つ用意
 
bufWord[0] = str1[0];
bufRep[0] = 1;
 
int repFlag = 1;
 
char press1[70]; // 圧縮結果の保存用
int preCount = 0; // 何文字目まで書き込んだかのカウンタ
 
int temp = 0;
int breakFor = 0; // for から抜けるためのフラグ
 
for (temp = 0; temp <= 12; temp = temp + 1) {
 
if (repFlag == 1) {
if (str1[temp + 1] == str1[temp] && str1[temp] != 0) {
bufRep[0] = bufRep[0] + 1;
repFlag = 1;
} else if (str1[temp] != 0) {
repFlag = 0;
press1[0] = bufWord[0];
press1[1] = bufRep[0];
preCount = +2;
breakFor = 1;
break;
} else if (str1[temp] == 0) {
breakFor = 1;
// ヌルが来たら文末などと判断し、他に何もせずに抜け出す
break;
} // else if
 
if (breakFor == 1) {
break;
}
 
} // if
} // for
 
int hokan = temp; // 次のforの開始番号用
 
int loop = 1;
 
int count;
breakFor = 0; // 下記 while のbreak フラグ用
while (breakFor == 0) {
if (loop == endCount) {
 
repFlag = 0;
breakFor = 1;
break;
}
 
hokan = temp;
 
if (str1[hokan + 1] > 30 && str1[hokan + 1] < 128) {
bufWord[loop] = str1[hokan + 1];
bufRep[loop] = 1;
} else {
bufWord[loop] = 0;
bufRep[loop] = 0;
}
 
press1[preCount + 0] = bufWord[loop];
press1[preCount + 1] = bufRep[loop];
 
repFlag = 1; // 次のループ用に再セット
for (temp = hokan + 1; temp <= 255; temp = temp + 1) {
 
if (repFlag == 1 && str1[temp] > 31 && str1[temp] < 256) {
 
if (str1[temp + 1] == str1[temp] && str1[temp] != 0) {
bufRep[loop] = bufRep[loop] + 1;
repFlag = 1;
 
} else if (str1[temp] != 0) {
 
repFlag = 0;
press1[preCount + 0] = bufWord[loop];
press1[preCount + 1] = bufRep[loop];
preCount = preCount + 2;
 
break;
} else if (str1[temp] == 0) {
breakFor =
1; // ヌルが来たら文末などと判断し ループから break
 
break;
}
 
else {
breakFor = 1; // 無限ループ防止のため
// 想定外の自体はループから break
 
break;
} // else if
} else {
breakFor =
1; // 無限ループ防止のため 想定外の自体はループから break
 
break;
}
 
// if
} // for
 
loop = loop + 1;
} // while
 
printf("\nランレングス的に圧縮のシミュレーション...\n");
 
for (int i = 0; i < preCount + 0; i = i + 1) {
printf("%02x ", press1[i]); // 最低でも2桁を表示、の意味
}
 
printf("\nファイルに書いてある機械語1\n");
 
for (int i = 0; i < endCount; i = i + 1) {
printf("%02x ", str1[i]); // 最低でも2桁を表示、の意味
}
 
printf("\nファイルに書いてある機械語2\n");
 
for (int i = 0; i < endCount; i = i + 1) {
printf("%02x ", str2[i]); // 最低でも2桁を表示、の意味
}
 
int press2[100];
 
for (int i = 0; i < preCount + 0; i = i + 1) {
press2[i] = press1[i];
}
 
printf("\nランレングス的に圧縮のシミュレーション redo...\n");
 
for (int i = 0; i < preCount + 0; i = i + 1) {
printf("%02x ", press2[i]); // 最低でも2桁を表示、の意味
}
 
int pack0[50];
 
for (int i = 0; i < preCount + 0; i = i + 1) {
 
if (i % 2 == 0) {
 
pack0[i + 1] = press1[i];
// pCount = pCount + 1;
}
 
if (i % 2 == 1) {
 
pack0[i - 1] = press1[i];
}
}
 
printf("\n pack0 半製品(回数と読み取り文字の入れ替え)...\n");
for (int i = 0; i < preCount; i = i + 1) {
printf("%02x ", pack0[i]); // 最低でも2桁を表示、の意味
}
 
char pack1[50];
int pCount = 0;
int skipCount = 0;
 
char minusCount = 0;
 
int minStart = 0;
 
for (int i = 0; i < preCount + 0; i = i + 1) {
 
if (i % 2 == 1) {
 
pack1[pCount] = pack0[i];
pCount = pCount + 1;
}
 
// 偶数版は回数のはず
if (i % 2 == 0) {
 
// 回数1のときのみ発動
if (pack0[i] == 1 && minStart == 0) {
// pCount = pCount + 1;
minStart = 1;
minusCount = minusCount - 1;
 
int j = i;
int k = 0;
for (k = 0; k <= 15; k = k + 1) {
if (pack0[j + 2 * k] == pack0[j + 2 + 2 * k]) {
minusCount = minusCount - 1;
skipCount = skipCount + 1;
} else {
break;
}
}
 
pack1[pCount] = minusCount;
 
minusCount = 0; // 代入し終わったので 0 に戻す
 
pCount = pCount + 1;
}
 
if (pack0[i] >= 2 && pack0[i] < 256) {
minStart = 0;
minusCount = 0;
 
pack1[pCount] = pack0[i];
pCount = pCount + 1;
}
}
}
 
printf("\n pack1 ほぼ目標品(回数1を負数に)...\n");
for (int i = 0; i <= pCount; i = i + 1) {
printf("%02x ", pack1[i]); // 最低でも2桁を表示、の意味
}
 
fclose(fp1);
printf("\nファイルをクローズしました。\n");
}
</syntaxhighlight>
 
実験結果
<pre>
ファイルをオープンしました。
機械語を読み取っています。
 
ファイルに書いてある機械語
61 61 61 61 61 62 63 64 65 65 65 65 65 65 00 00 00 00 00 00 00 00 00 00 ca 12 64
77 00 00 00 00 40 15 40 00 00 00 00 00 a3 c5 80 44 fe 07 00 00 02 00 00 00 00 0
0 00 00 1f 31 40 00 00 00 00 00 00 00 00 00 00 00
ファイルに書いてある機械語2
61 61 61 61 61 62 63 64 65 65 65 65 65 65 00 00 01 00 00 00 00 00 00 00 11 00 01
10 00 00 00 00 00 00 00 00 00 00 00 00 ffffffca 12 64 77 00 00 00 00 10 3a 60 0
0 00 00 00 00 65 ffffffc9 ffffff94 fffffffe fffffffe 07 00 00 10 3a 60 00 00 00
 
文字の繰り返しを検出しようとしています...
 
ランレングス的に圧縮のシミュレーション...
61 05 62 01 63 01 64 01 65 06
ファイルに書いてある機械語1
61 61 61 61 61 62 63 64 65 65 65 65 65 65
ファイルに書いてある機械語2
01 00 00 00 06 00 00 00 00 00 00 00 65 65
ランレングス的に圧縮のシミュレーション redo...
61 05 62 01 63 01 64 01 65 06
pack0 半製品(回数と読み取り文字の入れ替え)...
05 61 01 62 01 63 01 64 06 65
pack1 ほぼ目標品(回数1を負数に)...
05 61 fffffffd 62 63 64 06 65 00
ファイルをクローズしました。
</pre>
 
:(※ Windows7 上のgcc で実行した結果.)
 
さて、実行結果中にある文字列 <code>fffffffd</code> が一見すると負数に見えないが、しかしこれが負数の -3 のことである。
 
なぜなら、16進数で8桁 である数
ffff ffff
に1を足すと
0000 0000
になるので、ffff ffffはつまり-1のことである
 
同様に
 
ffff fffe とは -2 のこと。
 
よって
 
ffff fffd とは -3 のことである。
 
 
もっとも char型の1バイト(16進数で2桁のはず)の整数を宣言したのに、なぜ負数のときに 16進数で8桁の数が表示されるか疑問であるが、実行環境がそういう実装になっているので、妥協してもらいたい(Windows7 上のgcc で実行した結果である)。
 
=== ASCII文字の英文の圧縮の原理 ===