Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails以外の開発一般

保存版: Web画像フォーマットを「正しく」扱う(1)ピクセルとDPRを完全理解する(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。画像はすべて元記事からの引用です。記事が長いので4分割し、章立てを浅くしました。記事の表示が重くならないよう、原文と同様に画像にloading="lazy"を指定しています。

保存版: Web画像フォーマットを「正しく」扱う(1)ピクセルとDPRを完全理解する(翻訳)

Web上の画像をもっと本格的に利用開始してみましょう。新旧画像フォーマットの真髄を学んで、アプリケーションのパフォーマンスを高めましょう。SVGについて深く学んで、ベクタ画像とラスタ画像のどちらの画像コンテンツでも最適化できる素晴らしい最新ツールを取り入れましょう。デジタル画像を支える理論と人間が画像を知覚するしくみを研究して、ユーザーエクスペリエンスを向上させましょう。

皆さんは、2019年の平均的なWebページがデスクトップ環境で2MBものトラフィックを消費し、Webサーバーがユーザーに送信するデータの半分を画像が占めていることをご存知でしたか?JPEGやPNGやSVGやGIFやその他もろもろの略語は、デジタル成果物を手がけたことのあるすべての人々に知られています。ところで、Webページに表示されるものはことごとくフロントエンド開発に関連していなければならないと考える人もいますが、私たちは、画像を理解することは、バックエンドからデザイン、管理、カスタマーサポートにいたるまでプロダクトチーム全員にとって必要不可欠であると請け合います。

🔗 忙しい人のためのまとめと目次

私たちは、この長大な記事の内容を言葉で説明し尽くそうとする代わりに、本記事の内容を一覧できる図を制作しました。図のトップにあるカップケーキからスタートし、自分の知りたい方向を見定めて、皆さんの画像が内外で脚光を浴びる準備を整えましょう!

まとめの図

以下の目次からジャンプすることもできます。

  1. ピクセルとDPRを完全理解する(本記事
  2. SVGのマジック
  3. Evil Martians流巨大画像圧縮クックブック
  4. Webのアニメーション

お時間のある方は(かつ忍耐力がありましたら)、ぜひ上から順に読み進めてください。グッドラック!

🔗 ベクタ画像とラスタ画像

技術的には、ダグラス・エンゲルバードが現代におけるコミュニケーションの柱のひとつとして構想したハイパーテキストは、情報を伝えるために必ずしも画像を必要としていませんが、目立つ画像を用いたグラフィカルコンテンツによってユーザーの注意を導く必要があります。画像、動画クリップ、CSSアニメーション、canvasドロー、WebGL、はてはFlashといった遠い古代の技術にいたるまで、どの手段もユーザーをつなぎとめるという果てしなき戦いにおいて正当化されています。

コンピューター目線で見れば、どんな画像も特定の描画命令のかたまりでしかありません。これらが画面上の「ハードウェアピクセル上」に変換されるまでの旅路は、それだけで面白さに満ちています。何よりBMP (Microsoft Paint以外に使われているところはあるのでしょうか?)は例外として有名ですが、ほとんどの画像フォーマットはピクセルごとの値を明示的に保存していないのです。ファイルに含まれるデータを、色を伝達する最も直截的な手法であるRGBカラーコードの値の配列にデコードするときは、多少数学が絡んできます。

RGBは、今存在しているさまざまなカラーモデルのひとつにすぎません。なお私たちが個人的に好んでいるのは、「人間の目と脳」の動作に配慮したYCbCr方式です。私たち人間の目は、実際には色の変化よりも輝度の変化をより敏感に感じ取るようにできているのです。

私たちがベクタ形式を扱う場合、ラスタ形式のようないくつかのカラー値の組み合わせではなく、線や円や四角といった素朴な幾何学図形として画像を扱いますが、それらの図形はいずれも基本的には単なる曲線です。

PNG、GIF、JPEG、WebP、HEIC、AVIFは「ラスタ形式」ですが、SVGはベクタ形式です。私たちはこれらすべてを取り扱っています。

🔗 巨大画像を圧縮して表示する

以下の画像は、プロ用ビデオカメラで撮影したソースファイルを表示したもので、そのときのファイルの大きさは37.8MBでした。私たちが利用しているインターネットプロバイダやいわゆる環境問題に配慮して、画質を大きく落とさずに画像ファイルの大きさを3.5MBまで圧縮するために最善を尽くしました。

37.8MBを3.5MBまで圧縮した画像

しかし、画像の最適化そのものはWebのビジュアルコンテンツを扱ううえで最初に考えるべき要素では「ありません」。最初に行うべきは、この甘い甘いひとかじりをネットワークの向こう側にいるエンドユーザーの元に配信するまでのスタック全体を評価することです。以下の点をセルフチェックしてみましょう。

  • すべての画像は一括で読み込みますか?それとも特定の画像だけを最初にレンダリングしますか?(遅延読み込みをご覧ください)
  • HTTP/2(通信をプロトコルレベルで多重化する技術)を検討したことはありますか?
  • アセット画像の圧縮は効率よく行えていますか?
  • CDN(コンテンツデリバリーネットワーク)は使いますか?

これらのチェックリストについては皆さんの練習課題とし、ここからは画像そのものに絞って説明いたします。

🔗 完璧な画像など存在しない

コンテンツを見るユーザーに対する責務とは、「最小限のリソースで」「最高画質の画像を配信する」ことです。ここで言うリソースとは、処理能力(サーバーとクライアント両方)、メモリー、帯域幅のことです。

画質は「測定可能」です。画質評価指標(SSIM)をご覧ください。

本記事で考察するのは、ほとんどの場合「テクニカルな」意味での画像画質です(つまり画像の構図や遠近法や色の調和といった観点については考慮外とします。ここでは既に「理想的な画像」をいくつか入手済みであるという前提です)。そしてWebにはできるだけ最適な画像を掲載する必要があります。

画像の画質とは「理想的な画像」と「生成された画像」との差がどの程度かけはなれているかです。

本記事ではほとんどの場合、「画質」という言葉は以下を意味します。

🔗 シャープネス

精度や線のシャープネスの画像例

左側のベクタ画像はシャープですが、右側のラスタ画像はぼやけています。Codepenで違いを比較してみましょう。

🔗 色の正確さ

色の正確さの画像例

左側は本当の色で、右側は20色に減色されています。これもCodepenで比較してみましょう。

🔗 アーティファクトの欠落

画像左は滑らかですが、右の画像はピクセルの形が見えています。

🔗 「目」「脳」「コンテキスト」

HTMLやCSSでモックアップを実装するフロントエンド開発者も、そのモックアップを作成しているデザイナーも、「画像を用いてユーザーに何らかのコンセプトを伝える必要がある」という同じ問題をそれぞれ効果的に解決しています。そのため、ツールに飛びつく前に「コンテキスト」というものに注意を向けることが重要です。

以下の画像例をご覧ください。

コンテキストが異なる画像例

コンテキストが異なる画像例

画像1のカードでは画像そのものがコンテンツの主役です。つまりユーザーにこの画像をしばらく見つめてもらうことが狙いなので、できるだけ高画質の画像が必要です。

画像2のバナーではテキストの方が重要です。画像は単なる添え物なので、ユーザーの関心を逸らす心配なしにめいいっぱい最適化を進められます。

画像3のインタラクティブ要素はユーザー操作を促すためのものなので、画像とテキストが同じぐらい重要です。こうした要素は拡大縮小や再利用性が必要ですし、画像が占めるスペースもできるだけ小さくする必要があります。こういうときにラスタ画像ではなくベクタ画像を採用するのはそのためです。

もうひとつの重要なファクターは「人間の感覚」です。前述したように、色よりも「輝度」「コントラスト」の方が重要度が高いので、ユーザーに気づかれないように色数を節約することが可能になります。

もうひとつ忘れてはならないこととして、画像はブラウザによって物理デバイスすなわち液晶画面上にレンダリングされるという事実です。つまり「ブラウザが変わると画像のレンダリングもわずかに変わる」ということです。この違いに気づくにはよほど目を凝らして見ないといけないかもしれません(Retina液晶画面をお持ちでない方、ご愁傷さま)。

同じ画像がブラウザごとに異なる例

こうした違いについても配慮し、画面の種類やviewportサイズやブラウザの特性に応じて異なる画像を提供すべきです。

🔗 作成したピクセルがすべて同じとは限らない

そもそもピクセルとは何でしょうか?親のスマホで『ペッパピッグ』を視聴していた幼児でも何となく思い当たることぐらいはあるのではないでしょうか。しかし、最初の直感が必ずしも正しいとは限りません。

「ピクセル」という言葉は、実際には「ハードウェア目線から見た点」「CSS目線から見た点」「画像データ目線から見た点」という少なくとも3つの目線でがらりと変わります。

中でも特にややこしいのがCSS pixelと呼ばれるものです。CSS pixelとは「長さの単位の一種であり、人間が目に負担をかけずに快適に見ることのできる1個の点の幅や高さと大雑把に対応する」ものです。

スマホ上でテキストを読むときは、眼球から一定の距離を保ちながら画面を手に持つことになりますが、コンピュータの画面を見る場合はもっと離れた距離になる傾向があります。人間の目と画面の距離が広がれば広がるほど、CSS pixelのサイズも拡大されることになります。

CSS pixelの測定方法

CSS pixelの測定方法(CSS specificationより)

これについて徹底的に調べなくても、「CSS pixelの『物理サイズ』は、画面の種類ごとにミリ単位で異なり、しかも値は不変である」ことだけを頭に入れておけば十分です。AppleのRetina液晶画面(iPhoneやiPadやMacなどで使われている)では、この値が0.11mmから0.23mmまでの範囲で異なっています。1個のピクセルがどんな形をしているかなどというハードな質問はなしでお願いします。使うのはあくまで1個の値であり、複数の値の組み合わせではないということだけ覚えておいてください。

MicrosoftのAlvy Ray Smithが1995に発表した論文『A Pixel Is Not A Little Square(ピクセルは小さい四角なんかじゃない)』ではピクセルの「形」について延々と語り倒しています。お好きな方はどうぞ!

CSS pixelは、ハードウェア上のピクセルとも異なります。後者は、特定の物理画面の仕様に基づくドットの最小サイズにすぎません。

いよいよdevicePixelRatio(DPR)のお話にさしかかりました。画像をどんなデバイスでも同じように良好に表示したいのであれば、このDPRを理解しておくことがきわめて重要です。

ここで、お使いのブラウザ(どのブラウザでも構いません)で開発ツール(DevTools)を開いてdevicePixelRatioと入力し、現在の「自分の」画面のDPRをチェックしてみましょう1。ここでご注意いただきたいのが、ブラウザウィンドウを外部ディスプレイなどの別の画面に移動すると、この値も更新されることです。DPRは以下のように算出します。

devicePixelRatio = CSS pixelのサイズ / ハードウェアピクセルのサイズ

それでは幅1000ピクセル高さ1000ピクセルの画像で考えてみましょう。
この画像は画面上でどれだけの領域を占めるでしょうか?

1000 × 1000 pxの画像

1000 × 1000 pxの画像

ではこの画像をWebページに置いてみましょう。

<img src="cake.jpg" style="width: 1000px;">

width1000pxに設定すると、1000 * 1000すなわち「1 000 000 CSS pixel」でレンダリングするようブラウザに指示します。自分のDPRが1(非Retina画面)の場合は1000 * 1 * 1000 * 1すなわち「1 000 000ハードウェアピクセル」となります。

なおDPRの式は幅と高さそれぞれに適用されるので、1000 × 1000の画像では2回適用することになります。

そうなるはずだという直感どおり、1個のハードウェアピクセルは1個の「画像ピクセル」を担当します。

しかしDPRが2(Retina画面)の場合はどうなるでしょうか?画像は1000 * 2 * 1000 * 2すなわち「4 000 000ハードウェアピクセル」でレンダリングされます。

つまり画像上の1個のピクセルが、ハードウェアピクセル4個としてレンダリングされているということです。この画像は拡大されたのです。「画像データ」上のあらゆるピクセルが画面上で引き伸ばされたということになります。

🔗 広い世界の小さなピクセル

ご覧いただいたように、画像の寸法はもはやハードウェア上の寸法と一対一で対応していません。ブラウザはラスタ画像がどんな内容なのかについては何も知らないので、最適なリサイズ戦略を推測するために一般的なアルゴリズム(数式だらけ!)が用いられています。

そして皆さんのご想像どおり、その推測が当たりとは限らないのです。

この処理はCSSのimage-renderingプロパティで制御できます。このプロパティを用いて戦略を指定できますが、ブラウザが選択する特定のアルゴリズムについては制御できないものもあります。

一般的な画像スケーリングアルゴリズムの比較についてはWikipediaをご覧ください。

ChromeにおけるCSS image-renderingプロパティ

Chromeで表示されるCSS image-rendering値の違い

以下の画像は画像のスケール拡大方法を示します。

CSS pixelの測定方法

4pxの領域を16pxにスケールアップする

4pxの領域を16pxにスケールする場合、ブラウザは画像ピクセルを4個しか持っていないので、残りの12個については推測が必要です。つまり、そうしたぼやけた周辺部分やその他の不自然な部分は、リサイズアルゴリズムが「補間」を行った結果です。

補間(interpolation)では、特定の個数の周辺ピクセルを用います。

目的は、必要な手段を用いて画像のスケールアップを回避することです。以下に方法を示します。

  • コンテキストを理解して、許容できる画質を決定する。
  • 元の画像をそのコンテナの最大サイズ * DPRにリサイズする。
  • 複数の画像をよく使ういくつかのDPRに揃える(1倍または2倍、3倍もあり)。
  • 以下のようにマークアップの中で記述する。
<img srcset="cupcake.png 1x, cupcake@2x.png 2x, cupcake@3x.png 3x" src="cupcake.png">

(続く)


  1. ピクセルとDPRを完全理解する(本記事
  2. SVGのマジック
  3. Evil Martians流巨大画像圧縮クックブック
  4. Webのアニメーション

関連記事

保存版: Web画像フォーマットを「正しく」扱う(2)SVGのマジック(翻訳)

2024年のファビコンを極める: 本当に必要なファイルはほぼ6つ(翻訳)

保存版: Web画像フォーマットを「正しく」扱う(4)Webのアニメーション(翻訳)

HTML5のLocal Storageを使ってはいけない(翻訳)


  1. 訳注: Chromeブラウザであればレスポンシブな端末固有ビューポートのテストなどで方法を確認できます 

CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。