Chienomi

DPIと画面サイズとスケーリングとピクセル 理解への道

Live With Linux::basic

Cinnamonは4.6でFractional scalingが入ったはずだけどなぁ、と思っていたのだけど、すごくわかりにくいところにあった。

Cinnamon 4.6からUIスケーリングは一般の設定からディスプレイの設定に移ったのだが、Fractional Scalingはこれとは別項目で、日本語では「解像度の伸縮」と訳されている。

CinnamonによるFractional scalingの設定

ちなみに、「UIの拡大率」はレンダリング密度であり、「2倍 (高DPI)」にすると倍密度で描画される。100%を越える表示を行う場合、「2倍」を選択しないとUIがぼんやりする。 一方、「2倍」にすると描画負担が増加するとともに、一部UIのサイズが小さくなってしまう。 「解像度の伸縮」とともに指定した場合、これがスケーリングに影響することはなく、あくまで描画に影響する。

Chienomiでは以前もScalingについて解説しているが、今回はウェブエンジニアなども含めてより理解しやすいように詳しく解説していこうと思う。

基本はLinuxを前提にするが、Linuxに限らない話も登場する。

前提としてのDPI

まず、DPI(dot per inch)は1インチにいくつの点が打てるか、という点密度の話である。 PPI(pixel per inch)という場合もある。

これは、1inch3ではないことに注意してほしい。 そもそもディスプレイのピクセルは正方形ではなく、「縦方向DPI」と「横方向DPI」が存在する。

Windowsは96dpi, Macは72dpiである。 現実のディスプレイでは96dpiが近かった(15インチXGAとかの話である)のだが、Macが72dpiであるのは、1px=1ptとして取り扱えるようにするためである。

このDPIは

  • 物理的なドット密度を表すため
  • 物理寸法をドットに変換するため

の2通りの使われ方をする。

前者については、DPIが高ければ高精細表示であり美しい、ということを意味している。

後者は、例えば16ptの文字を表示するとする。Macの場合、1pt=1pxだから、16ピクセルの文字となる。一方Windowsの場合、96dpiで、72/96=0.75なので、16/0.75≒21.33となり、21ピクセルの文字になる。Windowsでは「16ピクセル」よりも「16ポイント」のほうが画面上は大きく表示される。

ちなみに、印刷物に対して寸法をピクセルで示した場合も、このような変換が行われる。

この、「Windowsは96dpi, Macは72dpi」というのはシステムがそのように認識しているという話で、実際にどうか、ということとは関係ない。だから、同じFHDでも、かたや21.5インチのモニターで、かたや13.3インチのラップトップだったとすると、当然13.3インチのほうが物理的にピクセルは小さく、同じ16ピクセルだったとしても物理的な大きさはだいぶ違う(だいたい1.5倍くらい違う)。 そして、同じように16ポイント、と指定したとしても、これは21ピクセルになるわけで、16ピクセルが1.5倍大きさが違うなら、当然21ピクセルも1.5倍大きさが違う。

フォントサイズ、フォントスケーリング

以前は画面に対して問題になるのは「文字」だと考えられてきた。

ピクセルが小さくなることでそれに伴って物理的な表示サイズが小さくなるわけだが、文字が小さくなることは明確に支障があった。

そこで考えられたのが、「ディスプレイの実DPIを反映する」という考え方である。 つまり、文字表示においてのみ、Windowsの「96dpi」の制約を除外する。「116dpi」と設定されれば約0.62で割った値となり、16ポイントは26ピクセルで表示されるようになる。

そもそもUI部品でベクターだったのはフォントくらいのもので、ほかはラスターであり、拡大縮小する余地に乏しい。 フォントを合わせるためにディスプレイのDPIを反映してシステムに設定できるようになったわけだ。

だが、16:10や16:9のディスプレイが普及してくるに従って、「大きなディスプレイ」「高精細なディスプレイ」が増えてきて、ディスプレイの解像度の振れ幅が大きくなったことなど、複合的な要因からこの方針は取りやめになっていった。

ただし、これは「フォントDPI」「フォントスケーリング」という形で残っている。 これはどちらも「フォントに限って」DPIを設定するものだ。 「フォントDPI」はシステムDPI(Windows, Linuxともに96dpi)との比で(通常10%単位で)フォントを拡大縮小し、フォントスケーリングは直接にこの拡大縮小率を指定する。

UIスケーリング

高解像度化が進み、特に4kが入ったことにより、「高解像ディスプレイ上でUI部品があまりにも小さい」という問題が発生するようになった。

そこで登場したのがUIスケーリングである。

UIスケーリングは「プログラムから見たピクセルと、実際のピクセルを切り離す」というものだ。

あるプログラムが幅1px、長さ5pxの線を引く、とする。 このとき、普通ならば実際に幅1px、長さ5pxの領域に描画されることになる。

ここでUIスケーリングが2であれば、プログラムとしては幅1px、長さ5pxの線だが、実際には幅2px、長さ10pxで描かれる。

この「実際の描画」はプログラムからはわからない、というのがポイントだ。 プログラムが描画したものが、実際には拡大されて表示される。言い方を変えると、プログラムから見ればキャンバスは小さく見えている。

例えば、2560x1600のMacのディスプレイで、UIスケーリングが2だとすると、プログラムから見るとそれは1280x800のディスプレイである。 実際、プログラムが1300pxの横線を引くと表示は画面からはみ出してしまう。

Fractional scaling

スケーリングは原理的に、整数倍が望ましい。

スケーリングが2であれば、1pxは実際には縦2px、横2pxの4pxで描画されることになる。 これは単純な拡大で済み、ややこしい要素はない。

だが、スケーリングが1.5だとすると、赤ピクセルと青ピクセルが並んでいる場合、両方2にすれば長すぎ、両方1にすれば短すぎる。 かといって、ピクセルは最小単位であるからこれを分けることもできない。

そのため、仮想的に1.5ピクセルが存在するかのように見せるため、「赤・紫・青」と並べることになる。

実際、大きな画像の場合ドットバイドットでなければ縮小表示することになるが、縮小に伴って情報は欠損し、またどのように表示すべきか決められない状態が生じるが、中間部分は色を混ぜたりしている。 また、フォントレンダリングにおいても、点が飛び飛びになるとガタガタに見えるため、濃淡によってスムーズな表現、つまり実際にはピクセルが存在しない部分において密度を表現することでピクセルがあるかのようなスムーズな表現を可能にしている。

だが、実際に違和感なく表示するためのアルゴリズムは非常に難しい。 整数倍のUIスケーリングは簡単に実装できるが、小数倍のスケーリング(=Fractional scaling)は正解のない高度なアルゴリズムが要求される。

Webブラウザの挙動

Google ChromeはUIスケーリングに従い、さらにフォントスケーリングに従ってズームを行う。

これがどういうことかというと、例えばスケーリングは等倍だという前提のもと、フォントDPIを106dpiと設定したとする。これは、標準の96dpiから約10%大きく、つまり1.1倍の拡大である。

この場合に、Chromeはフォントサイズを1.1倍にするのではなく、110%ズームと同等の扱いを行う。 ChromeはChromeの機能としてページズームがあるが、これを自動で行うということである。ただし、この場合ページの100%が110%に拡大された状態となり、ページを110%に拡大した場合、1.1x1.1倍の拡大となる。(恐らく、0.1単位に丸められる。) さらに、フォントスケーリングに基づく拡大はUIに対しても行われる。Chromeにはzoom factorという概念があり、これはElectronについてもサポートされている。

この処理はChromeで行っており、明確にパフォーマンスに対して影響がある。 特にブラウザゲームをするとかなり遅くなったことを感じる。

一方、FirefoxはフォントDPI(フォントスケーリング)の設定はあくまでフォントのみに作用させる。これは正しい挙動であるが、UIスケーリングが十分なHiDPI環境ではFirefoxのUIはやたら小さいと感じられてしまうという問題がある。

また、フォントスケーリングしている場合にスケーリング度合いがChromeと異なる。 基本的にフォントスケーリングが設定されている場合、Firefoxのほうが文字が小さくなる。

ちなみに、標準16ピクセル, UIスケーリング1.5に設定した状態でスケーリング1.2としてみたところ、Chrome(Vivaldi)は36px, Firefoxは29pxだった。16 * 1.5 * 1.2 = 28.8なので、Firefoxのほうが正しい。

web製作者的な話をするならば、これを一行に表示される文字数の参考にしようというのは誤っている。 なぜならば、(すでにここまでの説明でわかる通り)それを事前に想定することは不可能だからだ。Macですらスケーリングは自由に変更可能なのである。

可変システムDPI

Xserverはディスプレイを認識し、ディスプレイのピクセル数と物理サイズからピクセルサイズを推定する。 ディスプレイが正確に識別できているのならば、XServerはそれ自身で「正しいDPI」を知っていることになる。

(これは、うまく動作しないことが多い)

これはxdpyinfoコマンドにょってXServerがどのようにディスプレイを認識しているかを知ることができる。 修正が必要な場合は、/etc/X11/xorg.conf.d/以下にディスプレイを入力する。

この機能はマルチディスプレイである場合、環境によってはうまく動作しない。

いずれにせよ、こうした方法でDPIを認識するため、96dpi基準でどのように拡大縮小すれば物理サイズと一致させられるか、ということを把握できる。 そして、プログラムによってはそのようにして拡大縮小を行う。

スケーリングされるもの、されないもの

基本

仮にUIスケーリングが2だとすると、1pxは実際には縦2px、横2pxの4pxで描画されることになる。

こうしたプログラムは

スケーリングしないもの

しかし、UIスケーリングがすべてに対して単純に拡大縮小したピクセルを表示する、というのは全く意味がない。 4kディスプレイをUIスケーリング2で運用するのは、4kディスプレイをFHDモードで運用するのと同じ意味になってしまうかきらだ。

まず、ベクターグラフィックは4倍密度になっていることを活かしてスムーズな線を引くべきだし、ラスタグラフィックにしても、画像のような画面よりも多くのピクセルを持つものについては、物理ピクセルの多さを活かして美しく表示すべきだ。

このため、グラフィックライブラリの多くは(そのライブラリ自身は)UIスケーリングに関知しない。ベクターグラフィックのライブラリ、あるいはベクターグラフィック機能を提供するものについては、それを利用する側にとってはスケーリングされた単位を反映するようになっているのが一般的だ(例えばフォントのように)。

スケーリングできないもの

UIスケーリングがどのように面倒を見ているかにもよるが、例えばCinnamonのUIスケーリングはGTKのレベルで行っている。 だから、当然GTK3アプリケーションはこのスケーリングが反映される。また、Qtアプリケーションに対してもスケーリングを行う。

しかし、これは「スケーリングされる側が対応している」という前提になる。 だから例えば、GTK+2アプリケーションはUIの拡大に対応しておらず、サイズの異なるラスタ画像をテーマに含んでいるに過ぎない。このため、GTK+2アプリケーションはUIスケーリングの指定を反映しない。 ただし、フォントスケーリング機能はあるため、フォントスケーリングは反映する。

また、Wineなども対応していない。

「対応しているものだけ反映される」というと狭そうに見えるが、GTKとQtが反映されれば事実上あまり困らない。