👏

任意のファイルサイズの画像を生成する

2022/10/30に公開約1,800字

やりたいこと

画像のファイルサイズを特定の値にしたい。

入力ファイルサイズに制限のあるシステムである場合には、ファイルサイズの境界値テストをしたいことがあると思います。
ファイルの形式が問われないのであればfsutilでバイナリ生成ができるのですが、特に画像ファイルに限定される場合、ましてや縦幅/横幅が指定される場合には生成が難しいです。
実際にその辺りで痛い目を見たので、ファイルサイズを指定して、画像ファイルを作れるようになっておきたいなと思いました。

PNGファイルの概要

出力する画像ファイルを.pngに限定します。
というのも、PNGの仕様が都合がいいのですが、その辺りの説明から。
こちらこちらが詳しいので、参考にさせてもらっています。あとはWikipedia

PNGは複数のチャンクから構成されます。
このチャンクには、画像の画素情報はもちろん、縦横サイズといったメタ情報や、アニメーション情報など含まれることがあります。仕様にて既定のチャンクが用意されていますが、これに含まれないチャンクがあってもエラーとはなりません。
この仕様により、例えば、見た目はただの画像だけど実はゲームのセーブデータ、といった使い方もできます。

1ファイルのデータ構成は以下の通り。

ヘッダは今回重要ではないので省いて
ヘッダに続いて必須チャンクのIHDRチャンク、その後に必須/補助チャンクが並び、最後にファイル終端を示すIENDチャンクがあります。
それぞれのチャンクは、チャンクのデータサイズ、データタイプ、データ本体、CRCで構成されます。

  • データサイズ
    データ本体のサイズを記述します。

  • データタイプ
    データの種類をアルファベット4文字で定義します。各文字の大文字/小文字に意味があります。

    • 1文字目:
      大文字の場合、このチャンクは必須チャンクに分類されます。これが大文字に関わらず、デコーダーが解析不能に陥る場合にはエラーとなります。
    • 2文字目:
      大文字の場合はパブリックチャンク、小文字の場合はプライベートチャンクを意味します。
      仕様が公開、定義されているかどうかを意味する?
    • 3文字目:
      予約。常に大文字にする必要があります。
    • 4文字目:
      大文字の場合、必須チャンクの変更により影響を受けることを表します。
  • データ本体
    データ本体のバイナリ。

  • CRC
    データの正当性をチェックするために用います。

このチャンクのデータ構造に従えば、好きなデータの追加が可能です。

生成データの制限

前述より、できることの制限が自明になります。

まず、ファイルサイズを小さくすることはできません。ファイルサイズを小さくするということは、チャンクのデータを削減する、あるいはチャンク自体を削除するということになりますが、これはデータ破損になります。
また、データを追加するにおいてもチャンク単位での追加が必要となるので、チャンクの最小サイズ(=12byte)未満を増やすことはできません。

実装

処理の流れとしては以下の通り。

  1. 生成したい形式の画像を一時出力する
    pngファイル全体を自力で生成するのは面倒なので、正しいフォーマットのファイルを適当に生成します。
  2. 1で生成した画像をバイナリで読む
  3. IENDチャンクを見つける
  4. IENDチャンクの前に無駄なチャンクを挿入する

実装の結果はGitHubにあります。
なお、CRCの計算はこちらを参考にさせていただきました。

Discussion

ログインするとコメントできます