MacBook Proを買ったのはいいもののそろそろLinuxに戻りたくなってきたbokkoです。
今回は先月末にGoogleから発表・公開されたばかりのzopfliの紹介と、 そのPHP拡張であるphp-ext-zopfliにPNG画像を再圧縮する関数を追加した時の話をします。
zopfli
zopfliはzlibと比べて3〜8%ほど圧縮率が高く、 それでいてgzipやzlib等で広く使われているdeflateアルゴリズムと互換性のある圧縮アルゴリズムです。Google CodeでCによる実装が公開されています。
繰り返しになりますが、単にdeflateアルゴリズムよりも圧縮率が高いだけでなく、 deflateアルゴリズムと互換性がある、つまりzopfliで圧縮したものは従来のgzipやzlibで展開できるというのがミソです。
zopfliによるPNG画像の再圧縮
PNG画像はデータ部分(IDATチャンク)がdeflateアルゴリズムで圧縮されているので、 一旦IDATチャンクを展開してzopfliで再圧縮をかけることでさらに容量を小さくすることができます。
ただ、zopfli自体はデータ圧縮の機能を提供しているだけなので、
といった一連の処理は自分で書く必要があります。そのため、zopfliを使ってPNG画像の再圧縮を行うにはPNGの内部構造についてある程度知識が必要となります。(そのあたりの面倒を見てくれるライブラリがあったらぜひ教えてほしいです!・・・libpng?)
非常にざっくりですが、PNG画像はまず先頭に8バイトのシグネチャ(16進数で 89 50 4e 47 0d 0a 1a 0a)があり、それ以降は複数のチャンクで構成されています。 チャンクの種類は非常にたくさんありますが、上記の操作に必要なチャンクは以下の3つです。
チャンクの種類 | バイト数 | 備考 |
---|---|---|
IHDRチャンク | 25バイト | ヘッダチャンク |
IDATチャンク | 可変長 | データチャンク(複数存在してもよい) |
IENDチャンク | 12バイト | 終端チャンク |
各チャンクの内容は以下のようになります。
IHDR
内容 | バイト数 | 備考 |
---|---|---|
チャンクサイズ | 4バイト | 13 |
チャンクの種類 | 4バイト | 16進数で49 48 44 52(ASCIIコードで”IHDR”) |
幅 | 4バイト | |
高さ | 4バイト | |
ビット深度 | 1バイト | |
カラータイプ | 1バイト | |
圧縮メソッド | 1バイト | |
フィルターメソッド | 1バイト | |
インタレースメソッド | 1バイト | |
CRC | 4バイト | チャンクのサイズと種類を元に計算 |
IDAT
内容 | バイト数 | 備考 |
---|---|---|
チャンクサイズ | 4バイト | データのサイズ |
チャンクの種類 | 4バイト | 16進数で49 44 41 54(ASCIIコードで”IDAT”) |
データ | 可変長 | |
CRC | 4バイト | チャンクの種類とデータを元に計算 |
IEND
内容 | バイト数 | 備考 |
---|---|---|
チャンクサイズ | 4バイト | 0 |
チャンクの種類 | 4バイト | 16進数で49 45 4e 44(ASCIIコードで”IEND”) |
CRC | 4バイト | チャンクの種類とデータを元に計算 |
zopfliによるPNG画像の再圧縮をPHPで
元々zopfliでPNG画像を小さくすることに関心があったのですが、単にCでzopfliを使ってPNG画像を再圧縮するプログラムを書くよりは 既にあるLLのバインディングに組み込む方が実用的かなぁと思っていたところ、ちょうどphp-ext-zopfliというPHPからzopfliを使うための拡張モジュールがあったので、これにzopfli_png_recompressという関数を追加する形で実装しました。
上述の仕様を元に実装して最初にコミットしたのがこちらになります。(せっかくなので本家にpull requestを送ったところ、無事マージされました)
使い方はこんな感じです。
1 2 3 4 5 |
|
zopfliの使いどころ
zopfliはdeflateアルゴリズムよりも圧縮率が高いかわりに圧縮にかかる時間が非常に長くなるという欠点があります。 あまり詳細にベンチマークを取ったわけではありませんが、実際に作成したzopfli_png_recompressを使ってみたところ、 ピクセル数の小さい画像(400Kpx)の再圧縮でも数秒、大きい画像(約30Mpx)の再圧縮だと数分以上かかってしまったのでGoogleの発表にもある通り、静的コンテンツ配信のような事前に圧縮した形で配布するような用途に向いていると言えるでしょう。
まとめ
zlibやgzipで使われている圧縮アルゴリズムであるdeflateアルゴリズムと互換性があり、より圧縮率が高いzopfliについて紹介しました。
また、zopfliでPNG画像を再圧縮するプログラムをPHPの拡張モジュールであるphp-ext-zopfliにzopfli_png_recompressという関数を追加する形で作成しました。