4K修行僧

Anniversary Updateで4Kに優しくなったWindows 10

~デスクトップアプリのダイナミックスケーリングにメス

 8月に公開されたWindows 10の大型更新「Anniversary Update」(RS1)では、包括的な修正や機能追加がもたらされた。目立つものとしては、Windows Ink、Windows Hello、Cortana、Edge、MS-IMEなどの強化が挙げられる。しかし、RS1にはこれ以外にも大小さまざまな改善が盛り込まれている。その1つが、ディスプレイのスケーリング周りだ。

 過去、この連載では4Kという高解像度がもたらすメリットとデメリットを紹介してきた。メリットはもちろんその圧倒的な情報量だ。デメリットとしては、最新のWindows 10であっても、旧来のデスクトップアプリは高解像度に対応しておらず、文字表示がギザギザになったりぼやけたり、アプリのアイコンのサイズやレイアウトが崩れるという事がある。しかし、RS1では、ひっそりとこの問題点にメスが入れられている。

 もう少し問題点を具体的に説明しよう。4Kなどの高解像度液晶搭載のノートでは、画面サイズが13型前後ということで、スケーリング100%(等倍)の状態では、画面は広々とするが、文字が小さすぎて読めなくなるため、200%などに変更することが多い。筆者も、外付けの27型4K液晶は100%(画面A)だが、15.6型のノートの方は200%(画面B)にしており、その状態で拡張モードのマルチディスプレイにしている。

 この環境で、アプリを画面Aで起動し、そのウインドウ画面Bに移したとする。Per Monitor DPI AwareなユニバーサルWindowsプラットフォーム(UWP)アプリであれば、画面Aでは等倍表示だったのが、画面Bでは縦横2倍にきれいにスムージングされた状態で表示される。一方、従来のWin32デスクトップアプリは、Per Monitor DPI Awareでないため、画面B上では、ピクセルがきっちり縦横2倍に引き伸ばされ、文字はジャギーだらけになる。つまりダイナミックなスケーリングに対応できていないのだ。

デスクトップアプリのメモ帳で画面Aで表示させたもの
それをそのまま画面Bに移したもの。このサムネールでは同じように見えるかもしれないが、画像をクリックして、左上の拡大ボタンを押せば、きっちりピクセルが縦横に2倍に引き伸ばされているのが分かる。これを実機上で見ると、ジャギーが目立つ
こちらはUWPアプリのEdgeを画面Aで表示させたもの
それを画面Bに移したもの。フォントは単純拡大ではなく、スムージングが効いている

 逆にデスクトップアプリを画面Bで起動し、画面Aに移動させると、画面B上ではきれいに2倍にスムージングされていたものが、ぼやけた表示になる(もちろんこの場合もUWPアプリは問題ない)。

文字がぼけて表示される例

 全てのデスクトップアプリが非Per Monitor DPI Awareなわけではなく、筆者が利用しているアプリだと、Chomeはダメだが、FirefoxはPer Monitor DPI Awareで動作している。しかし、これはおそらくかなり少数派だ。

 この問題は、デスクトップアプリのウインドウをスケーリング設定が異なるディスプレイ間で移動させなければ顕在化しない。だが、ノートPCは、外付けディスプレイから外して持ち出したりして使うことが頻繁にある。この時、画面Aで表示していたアプリも、全て強制的に画面Bで表示されることになり、上記のような問題が発生する。画面Bできれいに表示させたければ、いったんサインアウトするしかない。そして、外出先から戻り、外付けディスプレイに繋ぐと、またスケーリング問題が発生するので、再度サインアウトする必要があるのだ。

 この原因は、多くのデスクトップアプリの設計思想が古く、動作中にDPIが変わる想定をしていないことにある。デスクトップアプリは起動時にのみディスプレイのサイズやスケーリング設定などの情報をシステムから取得し、その設定は変わらない想定で動作する。

 そこでWindows 10 Anniversary Updateでは、まず「非クライアントエリア」(Non-Client-Area、NCA)に手を入れた。

 多くのデスクトップアプリは、そのアプリ特有のUIや画面などのみを自身で描画し、メニューバーやスクロールバーなどはOSが用意するパーツを利用している。これがNCAだ。このNCAは、スケーリングの変更を認識しない。そのため、例えばデスクトップアプリがスケーリングが100%から200%になったことを感知して、スケーリングを行なうと、NCAはスケーリングしないので、大きさが本来の半分になってしまう。NCAを利用せず、アプリが全てのパーツを描画すれば回避できる問題だが、それには膨大な開発コスト・リソースがかかってしまう(ちなみに、Firefoxは全てのパーツをアプリ自身が描画している)。

 だが、Anniversary UpdateはWindows Presentation Framework(WPF)でのNCAの自動スケーリングをサポートした。「EnableNonClientDpiScaling API」を利用すれば、アプリはこの問題を回避できる。

 実際、Windows標準のメモ帳は、Anniversary Updateにより、メインウインドウはPer Monitor DPI Awareとなり、NCAにも対応した。これにより、サインアウトせずとも、UWPアプリのように先の画面Aでは等倍で、画面Bでは2倍にスムージングされ、かつメニューの表示も小さくなったりしない。

Anniversary Updateを適用したPCの画面Aでメモ帳を起動。この見た目は等倍なので従来と変わらない
これを画面Bに移動させると、メニューもメインウインドウのテキストも全てスケーリングが働いている。挙動はほぼUWPアプリと同じになった

 ただし、1つのアプリでも多数のウインドウを扱うことが多い。メモ帳も各種操作で新しいウインドウによるダイアログやメニューを表示する。理想としては、これらのウインドウについてもアプリがきちんとスケーリングの制御を行なえば見栄えは美しくなるが、それは非常に複雑なプロセスであり、またそもそもサードパーティ製のコンテンツを呼び出した場合、元のアプリからはそのスケーリングを全く手を出せない。比較的新しいデスクトップアプリでもPer Monitor DPI Awareに対応しないのは、そういった事情もある。

 その対策として、Anniversary Updateには「SetThreadDpiAwarenessContext API」も追加されている。これにより、アプリは全てのトップレベルウインドウに対して異なるスケーリングモードを指定できる。メモ帳の場合、印刷ダイアログは非Per Monitor DPI Awareなコンポーネントなので、アプリからはスケーリングを制御せず、OSに任せている。つまり、1つのアプリで異なるDPIが混在している形となる。これによって印刷ダイアログはジャギー表示となるが、開発者は複雑な開発を回避し、とりあえずはメインのウインドウのスケーリングにだけ専念できるようになった。

メモ帳から印刷ダイアログを呼び出したところ。印刷ダイアログのメインウインドウのテキストはOSがスケーリングしているので単純拡大となっている。1つのアプリで異なるDPIが混在している形となる

 ここまで読んで分かる通り、Anniversary UpdateにはデスクトップアプリをPer Monitor DPI Aware化する準備が整えられているものの、対応するにはアプリ側でのアップデートが必要となる。Windows標準の印刷ダイアログですらスケーリングには完全対応できていない状況なので、時間はかかりそうだが、Microsoft Officeについては現在対応が進められており、今後状況は改善していくだろう。

Anniversary Update適用PCで秀丸を実行したところ。上が画面A、下が画面B。秀丸はEnableNonClientDpiScaling APIに未対応なので、画面Bではそのまま拡大されている

 また、Microsoftによれば、サインアウトなしにアプリがスケーリングできるようOS側でもさらなる開発が進められているという。

 なお、Anniversary Updateでもまだ問題は残っている。1つはデスクトップアイコンで、拡張モードのマルチディスプレイ環境でスケーリング設定を変更すると、デスクトップアイコンのサイズがおかしくなる。また、FinFormsなどWin32コモンコントロールのPer Monitor DPI Aware化ができないことも課題となっている。