WebP と AVIF を設定する
まずサーバーに何ができるかを確認する
「画像」の領域には「このサーバーの機能」というカードがあります。これは書類の上に書かれていることではなく、動いている PHP で実際に測った結果を示します。GD 画像ライブラリとそのバージョン、JPEG の読み込み、PNG の読み込み、WebP の読み込み、WebP の書き出し、AVIF の書き出しです。確認は二重に行われます。関数が存在すること、そしてその形式が imagetypes() に含まれていることです。これが大事なのは、ホスティング事業者によっては GD を WebP や AVIF なしでビルドしていることがあるからです。その場合 imagewebp() は存在していて、呼び出したときに初めて失敗します。
形式が使えない場合、対応するスイッチは表示されたままですが無効になり、その理由が添えられます。WebP の場合、その理由は「GD が libwebp 付きでビルドされている必要がある」です。AVIF の場合は「libavif 付きの PHP 8.1 以降」です。どちらもホスティング事業者の領分です。AJAX 呼び出しからの抜け道も通用しません。サーバーは、利用できない形式を有効にすることをはっきり拒否します。
「Imagick」の行は単なる参考情報です。このモジュールは Imagick を使いません。そこに「なし」と表示されていても、不足しているものはありません。
有効にして 3 つの数値を決める
スイッチが 3 つ、数値の欄が 3 つあります。「WebP を生成」と「AVIF を生成」は初期状態でオフ、「<picture> で配信」はオンです。分けてあるのは意図的です。まずファイルだけを作らせておき、配信はあとから追加で有効にできます。逆に、配信をオフにしてもファイルは引き続き作られますが、訪問者には手を加えていないマークアップが届きます。
WebP の品質は 82、AVIF の品質は 50 に設定されています。どちらの欄も 30 から 100 までの値を受け付け、それを超える値も下回る値もこの範囲に丸められます。初期値が違うのは手違いではありません。AVIF は JPEG とは尺度が異なり、50 でもすでにとてもきれいに見えます。WebP では 90 を超えると、見た目の向上より速くファイルが大きくなります。
メガピクセルの上限は 40 に設定されており、4 から 400 までの値を取ります。これはメモリ上限から守るためのものです。6000 × 4000 の写真はデコード時に約 96 MB を占めます。ファイルの大きさとは関係ありません。さらに Kakapo はデコードのたびに、実際にまだ空いているメモリと照らして計算し、アップロードを上限で死なせる代わりに処理を飛ばします。
3 つのスイッチのいずれかを変更すると、保存されているページキャッシュは破棄されます。品質とメガピクセルの上限はキャッシュをそのまま残します — これらが効くのは次のアップロードからであり、すでに保存されているページを誤ったものにすることはないからです。
アップロード時に何が起こるか
変換はアップロードの最後、WordPress がすべての画像サイズを生成した時点で走ります。処理されるのはメタデータに記載されたファイルと、生成された各サイズです — コアが保管している元画像は対象外で、これはそもそも配信されることがありません。
派生ファイルはどれも元ファイルの隣に付随ファイルとして置かれ、元の名前をそのまま含んだうえで拡張子が付きます。bild.jpg からは bild.jpg.webp が生まれ、bild.webp にはなりません。理由は、同じフォルダーに bild.jpg と bild.png が並んで置けるからです。拡張子を置き換えるだけだと、一方の派生ファイルがもう一方を消してしまいます。保存先にすでに何かがある場合、それには手を触れません。
書き込みはまず、名前にプロセス番号と乱数を含む一時ファイルに対して行われます。実際に測って元ファイルより小さいと分かったときに初めて、rename で所定の位置に移されます。同じか大きい場合は削除され、元ファイルがそのまま残ります。そのため実行が途中で止まっても、大きすぎる派生ファイルが残ることは決してありません。
アップロードには 12 秒の時間予算があります。AVIF は生成時に WebP より何倍も遅く、6 つのサイズ × 2 つの形式では、そのままではウェブサーバーの実行時間に達してしまいます。運営者にはアップロードが中断したように見えるでしょう。収まりきらなかった分は、1 回だけの追走が引き受けます。これは 30 秒後に予約され、そのときは 90 秒の時間が与えられます。終わったものも飛ばしたものも添付ファイルに記録され、同じ試みが再生成のたびに繰り返されないようにします。
JPEG では EXIF の回転情報を反映させます。GD から出力される WebP と AVIF は、この情報を引き継がないためです。PNG が元の場合、Kakapo はまず可逆で WebP を試します。ロゴ、図版、スクリーンショットはこれで小さくなることが多く、しかも輪郭がにじみません。透過はどちらの出力形式でも保たれます。
どのように配信されるか
Kakapo は <img> を <picture> で包み、その前に AVIF と WebP のソースを、AVIF を先にして置きます — ブラウザーは自分が理解できる最初のソースを選びます。<picture> には display:contents を付け、テーマの Flex や Grid のレイアウトの中で余計な箱を配置に差し込まないようにしています。
これは .htaccess を使う代替手段に対して、意図的に選んだ方式です。コンテントネゴシエーションは Apache のルールを必要とし、nginx ではまったく効かず、Accept ヘッダーで内容を分けない CDN のキャッシュとぶつかります。<picture> ならブラウザーが判断し、サーバー設定は 1 行も要りません。
対象になるのは投稿の本文と投稿のアイキャッチ画像で、優先度は 22 です。Lazy-Load(20)のあと、CDN の書き換え(25)の前に入り、新しく生まれたソースも CDN を通るようにしています。すでにある <picture> のブロックには手を触れません。他のホストの画像と、アップロードフォルダーの外にあるものもすべて同じです。
覚えておくとよい規則が一つあります。ある形式が提供されるのは、srcset の中で最も大きい版が派生ファイルとして存在する場合だけです。中くらいや小さいものが欠けていれば、ブラウザーは次に大きいものを選びます。数キロバイト増えるだけで、ぼやけた画像になることは決してありません。最も大きいものが欠けていると、幅の広い画面が最大解像度に届かなくなってしまいます。まさにこの場合が起きないようにしています。
何も生成されないとき: 理由を読む
「生成されたファイルと実際の削減量」のカードには、形式ごとにファイル数、元ファイルの合計、派生ファイルの合計、そしてその差が表示されます。どの数値も、生成の時点における実際の 2 つのファイルサイズの比較から得られたもので、推定や外挿は一つもありません。ここでいう「元ファイル」は、派生ファイルが生成された対象のファイルの合計にすぎず、メディア全体ではありません。
その下には、変換されなかったものが理由とともに表示されます。主なものは:
「結果が元ファイルより大きかった」— これはエラーではありません。とても小さいサムネイルでは AVIF でよく起こります。元ファイルはそのまま残り、他のサイズはそれでも派生ファイルを得ます。
「書き込めない」— アップロードフォルダーの権限を確認してください。本当に人の手を必要とする理由は、これだけです。
「保存先にすでにファイルがあった」— 上書きはしていません。多くは既存のファイルに対する一括実行で作られたもので、ここで生成されるはずだったものとすでに同じです。
「メモリに収まらなかった」— メガピクセルの上限か PHP のメモリ上限を引き上げてください。
「読み取れない」— 壊れたファイル、CMYK の JPEG、または特殊な種類のファイルです。「JPEG/PNG/WebP ではない」— GIF アニメーションと PDF には意図的に手を触れません。
カードが空のままなら、WebP を有効にして画像を 1 枚アップロードしてください。すでにメディアライブラリにある画像には、この方法では届きません。同じページの下のほうにある一括実行を使ってください。