Retinaではどうしてラスタ画像がぼやけるのか

TL;DR

Retinaや高DPIをサポートするディスプレイでラスタ画像がぼやける理由は、画像が引き伸ばされる際に、スムージング(アンチエイリアス)が働くから。

ピクセルの2つ意味

普段使っている「ピクセル」という言葉には、2つの意味があります。

バイスピクセル(物理ピクセル

PCやスマートフォンといったデバイスが待つ実際のピクセル数を指します。例えばiPhone8には、750px × 1334px のRetinaディスプレイが搭載されています。

CSSピクセル(論理ピクセル

バイスピクセルが実際のピクセル数を表すのに対し、こちらは見かけ上のピクセル数を表します。CSSピクセルは文字通り、CSSの単位として使用されています。従来のデバイスでは「デバイスピクセルCSSピクセル」であったため、実際のピクセル数と見かけ上のピクセルは同じでした。

Retinaのしくみ

iPhone8のCSSピクセルは375px × 667pxで、デバイスピクセルと比べて縦横ともに半分ずつのサイズとなっています。言い換えると、CSSピクセルの1pxを表現するために、縦横それぞれ2倍のデバイスピクセルを使用している、ということになります。このような、デバイスピクセルCSSピクセルの比を「デバイスピクセル比」と呼び、iPhone8の場合では「デバイスピクセル比が2である」と表現します。

f:id:sgyatto:20210201213554p:plain:w500
当然、デバイスが持つ本来の解像度よりも表示領域は狭くなりますが、より高精細に描画することができます。ある意味では、Retinaは「CSSピクセルをより多くのデバイスピクセルで描画するための技術」が詰まったディスプレイ、と捉えることもできるでしょう。

ラスタ画像がぼやける理由

もともと1pxだったものを縦横2倍に引き伸ばして描画するのです。フォントなどのベクタ形式のデータは拡大しても問題なさそうですが、ラスタ形式の画像データなんかは、なにか問題がありそうですよね。

以下のような9px × 9pxのラスタ画像を縦横9pxの領域に描画した様子を、ディスプレイで確認してみます。

f:id:sgyatto:20210205204548j:plain:w250
9px × 9px

バイスピクセル比が1のディスプレイと、RetinaMacBook Pro)ディスプレイでスクリーンショットを撮影し、拡大したものが以下の画像になります。Reninaでは色の境界部がぼやけているのがわかります。

私の使用しているMacBook Proはデバイスピクセル比が2なので、一度、縦横18pxにまで拡大された後、縦横9pxの領域に収まっています。この拡大(スケーリング)される際にアンチエイリアスピクセルのギザギザを抑えてなめらかにする処理)が働き、画像がぼやけてしまっているのです。もしかしたら、以下のような拡大後のイメージを持たれた方もいるかもしれませんが、実際には上の画像のようにぼやけます。

f:id:sgyatto:20210205212103j:plain:w250
18px × 18px

綺麗に表示するには

このままだと「ラスタ画像をReninaディスプレイで表示するとぼやける」という結論になってしまうのですが、もちろん綺麗に表示する方法が用意されています。その方法とは、拡大後のサイズの画像を用意するというものです。先程の例で言えば、縦横18pxの画像を用意しておけば、拡大せずにそのまま使ってくれるので、ぼやけることなく綺麗に表示することができます(デバイスピクセルの多さが活きてくる!)。実際に、縦横18pxの画像を描画したものをRetinaディスプレイで確認してみます。

f:id:sgyatto:20210206151023p:plain:w250
RetinaMacBook Pro)で縦横18pxの画像を描画
f:id:sgyatto:20210206151303p:plain:w500
縦横18pxの画像が縦横9pxの領域に収まっている

Retinaなのにぼやけることなく表示されているのがわかると思います。このような対応は「Retina対応」と呼ばれ、実際のコードではsrcsetを用いてデバイスピクセル比に応じてロードする画像を切替えたりして対応します。