👏

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

2022/10/30に公開1

やりたいこと

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

入力ファイルサイズに制限のあるシステムである場合には、ファイルサイズの境界値テストをしたいことがあると思います。
ファイルの形式が問われないのであれば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

kecykecy

初めまして。お礼を伝えたくてコメントしました。

境界値テストのために手軽に任意のファイルサイズの画像を作れるツールを探していてこの記事を見つけて、GitHub経由でWeb版を使わせていただきました!

便利なツールを公開してくださって助かりました🙏