前回の記事では Apache mod_rewrite を使って事前圧縮した HTML / CSS / JS を返す方法を紹介しました。本稿では、 JPEG / PNG / GIF 画像を事前圧縮しておいた WebP (ウェッピー) に差し替えて返す手順を説明します。
WebPとは
WebP (ウェッピー) は、 JPEG/PNG/GIF それぞれの優れた特徴を持つ画像フォーマットです。いままでは画像の特徴や用途に合わせて JPEG/PNG/GIF を使い分ける必要がありましたが、 WebP ならすべての用途に対応できます。
- JPEG
- 非可逆圧縮。写真のようなフルカラー画像に適しています。
- PNG
- 可逆圧縮。イラストなどの画像に適しています。フルカラー/パレットカラーの両方に対応。アルファチャネル (半透過) にも対応。多くの点で GIF より優れていますが、 アニメーションに対応していないという弱点があります。
- GIF
- 可逆圧縮。フルカラーに対応できない。アルファチャネル (半透過) に対応できない。(単色の完全透過には対応しています。) 今となってはアニメーションくらいしか使い道がありません。
WebP はこれらの特徴をすべてサポートしています。
- フルカラー写真を非可逆圧縮するのも得意 (JPEG より圧縮率が高い)
- イラストを可逆圧縮するのも得意 (PNG より圧縮率が高い)
- アニメーションもできる (GIF より圧縮率が高い)
完璧な画像フォーマットである WebP にも弱点があります。それは WebP に対応していないブラウザーが存在していることです。Internet Explorer や Safari など一部のブラウザーは WebP 画像を表示することができません。
これらの WebP 未対応のブラウザーをサポートするために、 まだ JPEG/PNG/GIF を完全に捨て去ることはできないのです。
そこで、 WebP 対応ブラウザーには WebP を返し、 WebP 未対応ブラウザーには JPEG/PNG/GIF を返す、 というブラウザーに合わせたレスポンスを返すことが重要になります。
圧縮ツール
WebP の圧縮ツールは下記のサイトからダウンロードできます。
https://developers.google.com/speed/webp/download
Windows の場合は下部の Download for Windows というリンクをクリックします。ダウンロードしたファイルを展開すると bin
フォルダーの中にいくつかのツールが入っています。cwebp.exe
が圧縮に使うツールです。
cwebp.exe -h
でヘルプが表示されます。
コマンドプロンプトC:¥>cwebp.exe -h Usage: cwebp [options] -q quality input.png -o output.webp where quality is between 0 (poor) to 100 (very good). Typical value is around 80. Try -longhelp for an exhaustive list of advanced options.
cwebp.exe -longhelp
で詳細なヘルプが表示されます。
コマンドプロンプトC:¥cwebp.exe -longhelp Usage: cwebp [-preset <...>] [options] in_file [-o out_file] If input size (-s) for an image is not specified, it is assumed to be a PNG, JPEG, TIFF or WebP file. Windows builds can take as input any of the files handled by WIC. Options: -h / -help ............. short help -H / -longhelp ......... long help -q <float> ............. quality factor (0:small..100:big), default=75 -alpha_q <int> ......... transparency-compression quality (0..100), default=100 -preset <string> ....... preset setting, one of: default, photo, picture, drawing, icon, text -preset must come first, as it overwrites other parameters -z <int> ............... activates lossless preset with given level in [0:fast, ..., 9:slowest] -m <int> ............... compression method (0=fast, 6=slowest), default=4 -segments <int> ........ number of segments to use (1..4), default=4 -size <int> ............ target size (in bytes) -psnr <float> .......... target PSNR (in dB. typically: 42) -s <int> <int> ......... input size (width x height) for YUV -sns <int> ............. spatial noise shaping (0:off, 100:max), default=50 -f <int> ............... filter strength (0=off..100), default=60 -sharpness <int> ....... filter sharpness (0:most .. 7:least sharp), default=0 -strong ................ use strong filter instead of simple (default) -nostrong .............. use simple filter instead of strong -sharp_yuv ............. use sharper (and slower) RGB->YUV conversion -partition_limit <int> . limit quality to fit the 512k limit on the first partition (0=no degradation ... 100=full) -pass <int> ............ analysis pass number (1..10) -crop <x> <y> <w> <h> .. crop picture with the given rectangle -resize <w> <h> ........ resize picture (after any cropping) -mt .................... use multi-threading if available -low_memory ............ reduce memory usage (slower encoding) -map <int> ............. print map of extra info -print_psnr ............ prints averaged PSNR distortion -print_ssim ............ prints averaged SSIM distortion -print_lsim ............ prints local-similarity distortion -d <file.pgm> .......... dump the compressed output (PGM file) -alpha_method <int> .... transparency-compression method (0..1), default=1 -alpha_filter <string> . predictive filtering for alpha plane, one of: none, fast (default) or best -exact ................. preserve RGB values in transparent area, default=off -blend_alpha <hex> ..... blend colors against background color expressed as RGB values written in hexadecimal, e.g. 0xc0e0d0 for red=0xc0 green=0xe0 and blue=0xd0 -noalpha ............... discard any transparency information -lossless .............. encode image losslessly, default=off -near_lossless <int> ... use near-lossless image preprocessing (0..100=off), default=100 -hint <string> ......... specify image characteristics hint, one of: photo, picture or graph -metadata <string> ..... comma separated list of metadata to copy from the input to the output if present. Valid values: all, none (default), exif, icc, xmp -short ................. condense printed message -quiet ................. don't print anything -version ............... print version number and exit -noasm ................. disable all assembly optimizations -v ..................... verbose, e.g. print encoding/decoding times -progress .............. report encoding progress Experimental Options: -jpeg_like ............. roughly match expected JPEG size -af .................... auto-adjust filter strength -pre <int> ............. pre-processing filter
PNGを圧縮するとき
PNG 画像を可逆圧縮する場合はオプション -z 9
を指定するのが良いと思います。-z
が指定されると -lossless
が指定されたのと同様に可逆圧縮になります。
コマンドプロンプトC:¥>cwebp.exe -z 9 sample.png -o sample.png.webp
JPEGを圧縮するとき
JPEG 画像を不可逆圧縮する場合はオプション -m 6
を指定するのが良いと思います。このパラメータはエンコード速度と圧縮ファイルのサイズおよび品質との間のトレードオフを制御します。0
を指定するとエンコード時間が短くなり、 6
を指定するとエンコード時間が長くなります。事前圧縮ではエンコード時間を気にする必要はないので最大の 6
を指定します。
-q
で品質を指定できます。既定値は 75
です。90
以上を指定するとファイルサイズが極端に大きくなる傾向があるので、 品質を優先する場合でも 85
までに抑えるのが良いとされています。
コマンドプロンプトC:¥>cwebp.exe -q 75 -m 6 sample.jpg -o sample.jpg.webp
アニメーションGIFを圧縮するとき
アニメーション GIF を圧縮する場合は cwebp.exe
ではなく gif2webp.exe
を使用します。(ダウンロードした WebP ツール一式の bin
フォルダーに入っています。)
-min_size
を指定すると可能な限り出力サイズが小さくなります。
コマンドプロンプトC:¥>gif2webp.exe -min_size sample.gif -o sample.gif.webp
.htaccess
JPEG/PNG/GIF へのリクエストに対して、 事前圧縮した WebP を返すように構成した .htaccess
は以下の通りです。
.htaccess<IfModule rewrite_module>
<IfModule headers_module>
RewriteEngine on
#
# WebP
#
RewriteCond %{HTTP:Accept} image/webp
RewriteCond %{REQUEST_URI} ¥.(jpg|png|gif)$
RewriteCond %{REQUEST_FILENAME}¥.webp -s
RewriteRule .* %{REQUEST_URI}.webp [L]
<Files *.jpg.webp>
ForceType image/webp
</Files>
<Files *.png.webp>
ForceType image/webp
</Files>
<Files *.gif.webp>
ForceType image/webp
</Files>
#
# Vary
#
<FilesMatch "¥.(jpg|png|gif)(¥.webp)?$">
Header append Vary Accept
</FilesMatch>
</IfModule>
</IfModule>
- 必要なモジュールのロードや
.htaccess
を有効化する手順については前回の記事を参照してください。
前回の記事で説明した GZIP/Brotli と異なる部分について説明します。
ブラウザーが GZIP/Brotli に対応しているかどうかは Accept-Encoding
ヘッダーで判別することができました。ブラウザーの WebP 対応については Accept
ヘッダーに image/webp
が含まれているかどうかで判定することができます。
URL 書き換え条件として Accept
ヘッダーを確認するようにします。
#
# WebP
#
RewriteCond %{HTTP:Accept} image/webp
Vary
ヘッダーに Accept
を追加するようにします。
#
# Vary
#
<FilesMatch "¥.(jpg|png|gif)(¥.webp)?$">
Header append Vary Accept
</FilesMatch>
以上の変更で *.jpg
がリクエストされたときに *.jpg.webp
が返されるようになります。(*.png
、 *.gif
についても同様です。)
名前を付けて画像を保存するとどうなる?
ブラウザーには表示されている画像に名前を付けて保存する機能があります。
以下のように URL (ファイル名) の末尾が .png
となっている画像を保存したときに、 結果がどうなるのか気になりませんか?
<img src="img/sample.png">
URL (ファイル名) は .png
となっていますが、 実際にサーバーが返す画像は WebP 形式 (Content-Type: image/webp
) になるはずです。拡張子は .png
なのに中身は WebP というおかしなファイルができてしまうかもしれません。確認してみましょう。
Chromeの場合
Chrome (バージョン 75) で確認したところ問題ありませんでした。
画像を右クリックして 名前を付けて画像を保存 を選択すると保存ダイアログが表示されます。
ファイル名の既定値として sample.png
が表示されています。このまま保存すると WebP 形式のバイナリーが sample.png
という名前で保存されてしまいそうですが…。
実際に保存してみるとファイルの中身は PNG 形式になっていました。
なぜこのようなことになるのでしょうか?
Apache サーバーのログを確認して謎が解けました。Chrome は名前を付けて保存するときに、 もう一度、 サーバーにリクエストを出しています。そして、 このときは Accept
ヘッダーを含めていません。その結果、 サーバーは WebP 形式ではなく PNG 形式の画像を返していたのでした。
- レンダリング時は
Accpet: image/webp
が付加されている → WebP が返される - ファイル保存時は
Accept
ヘッダーが付加されていない → PNG が返される
この仕組みならオリジナルの PNG 形式で保存されるのでファイル拡張子 .png
との矛盾は発生しません。ですが、 画像を WebP 形式で保存できないのは少し残念ですね。
Firefoxの場合
Firefox (バージョン 67) で確認したところ少し問題がありました。
Firefox も Chrome と同様にレンダリング時とファイル保存時で Accept
ヘッダーが変化しました。Firefox の場合は、 レンダリング時は Accept: image/webp,*/*
が付加され、 ファイル保存時は Accept: */*
が付加されていました。
Chrome と異なるのは保存ダイアログに表示される既定のファイル名です。Firefox では既定のファイル名として sample.png.webp
が表示されます。
どうやら、 IMG
タグの src
属性だけではなく Content-Type: image/webp
も考慮してファイル名に .webp
を追加してくれたようです。しかし、 これは大きなお世話です。実際に保存操作をおこなうと Accept: */*
でリクエストが再送信されるため、 保存時のレスポンスは Content-Type: image/png
に変化するからです。
その結果、 Firefox では PNG 形式のファイルが拡張子 .webp
で保存されてしまいます。
Windows 10はWebPに対応している?
Windows 10 RS5 (バージョン 1809) から WebP に対応したようです。
バージョン 1709 のペイントで ファイル → 開く を選択したときに、 開くダイアログに表示される拡張子は以下のようになっていました。
バージョン 1809 のペイントでは表示される拡張子に HEIC
と WebP
が追加されています。
エクスプローラーでも WebP 形式の画像ファイルがサムネイル表示されるようになっています。Windows が WebP を標準サポートしたことで WebP の普及にも弾みがつきそうですね。